mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
544 lines
19 KiB
C
544 lines
19 KiB
C
/*H********************************************************************************/
|
|
/*!
|
|
\File testercomm_file.c
|
|
|
|
\Description
|
|
This module provides a communication layer between the host and the client.
|
|
Typical operations are SendLine() and GetLine(), which send and receive
|
|
lines of text, commands, debug output, etc. Each platform will implement
|
|
its own way of communicating through files, debugger API calls, etc.
|
|
|
|
\Copyright
|
|
Copyright (c) 2005 Electronic Arts Inc.
|
|
|
|
\Version 03/23/2005 (jfrank) First Version
|
|
*/
|
|
/********************************************************************************H*/
|
|
|
|
/*** Include files ****************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "DirtySDK/dirtysock.h"
|
|
#include "DirtySDK/dirtysock/dirtylib.h"
|
|
#include "DirtySDK/util/base64.h"
|
|
#include "DirtySDK/util/jsonformat.h"
|
|
#include "DirtySDK/util/jsonparse.h"
|
|
#include "libsample/zmem.h"
|
|
#include "libsample/zlib.h"
|
|
#include "libsample/zlist.h"
|
|
#include "libsample/zfile.h"
|
|
#include "testerprofile.h"
|
|
#include "testerregistry.h"
|
|
#include "testercomm.h"
|
|
|
|
/*** Defines **********************************************************************/
|
|
|
|
#define TESTERCOMM_FILEPATHSIZE_DEFAULT (256) //!< path/file storage
|
|
#define TESTERCOMM_PATH_DEFAULT (TESTERPROFILE_CONTROLDIR_VALUEDEFAULT) //!< default path for control files
|
|
|
|
#define TESTERCOMM_OUTPUTTIMEOUT_DEFAULT (60*1000) //!< timeout (ms) for a output message
|
|
|
|
#define TESTERCOMM_FILEBUFFERSIZE (4096) //!< size of temp file buffer to use
|
|
|
|
#define TESTERCOMM_FILEPROFILING (0) //! turn this on to see times for file writing
|
|
|
|
/*** Type Definitions *************************************************************/
|
|
|
|
typedef struct TesterCommFileT TesterCommFileT;
|
|
struct TesterCommFileT
|
|
{
|
|
// file specific stuff
|
|
char strControlDir[TESTERPROFILE_CONTROLDIR_SIZEDEFAULT]; //!< location of control directory
|
|
char strTempPathFile[TESTERCOMM_FILEPATHSIZE_DEFAULT]; //!< temporary output filename
|
|
char strInputPathFile [TESTERCOMM_FILEPATHSIZE_DEFAULT]; //!< input file filename
|
|
char strOutputPathFile[TESTERCOMM_FILEPATHSIZE_DEFAULT]; //!< output file filename
|
|
};
|
|
|
|
/*** Variables ********************************************************************/
|
|
|
|
/*** Private Functions ************************************************************/
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _TesterCommCheckInput
|
|
|
|
\Description
|
|
Check for data coming from the other side (host or client) and pull
|
|
any data into our internal buffer, if possible.
|
|
|
|
\Input *pState - pointer to host client comm module
|
|
|
|
\Output int32_t - 0 = no data, >0 = data, error code otherwise
|
|
|
|
\Version 03/24/2005 (jfrank)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _TesterCommCheckInput(TesterCommT *pState)
|
|
{
|
|
TesterCommFileT *pInterfaceData;
|
|
char *pInputData;
|
|
const char *pInputLoop;
|
|
int32_t iInputDataSize;
|
|
ZFileT iInputFile;
|
|
uint16_t aJson[16*1024];
|
|
|
|
if ((pState == NULL) || (pState->pInterface->pData == NULL))
|
|
{
|
|
return(-1);
|
|
}
|
|
pInterfaceData = pState->pInterface->pData;
|
|
|
|
// check to make sure our filename is sane
|
|
if (strlen(pInterfaceData->strInputPathFile) == 0)
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
// open the input file first
|
|
iInputFile = ZFileOpen(pInterfaceData->strInputPathFile, ZFILE_OPENFLAG_RDONLY | ZFILE_OPENFLAG_APPEND);
|
|
|
|
if (iInputFile == ZFILE_INVALID)
|
|
{
|
|
// could not open it - nothing there - not necessarily an error
|
|
return(-1);
|
|
}
|
|
|
|
// get the file size
|
|
iInputDataSize = (int32_t)ZFileSize(iInputFile);
|
|
|
|
// close the file
|
|
ZFileClose(iInputFile);
|
|
|
|
// check for empty file
|
|
if (iInputDataSize == 0)
|
|
{
|
|
// delete the file
|
|
ZFileDelete(pInterfaceData->strInputPathFile);
|
|
// and quit
|
|
return(0);
|
|
}
|
|
|
|
// load the contents of the file into the buffer
|
|
pInputData = ZFileLoad(pInterfaceData->strInputPathFile, &(iInputDataSize), 0);
|
|
JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), pInputData, iInputDataSize);
|
|
|
|
// and delete the file
|
|
ZFileDelete(pInterfaceData->strInputPathFile);
|
|
|
|
if (pInputData == NULL)
|
|
{
|
|
// no data to process
|
|
return(0);
|
|
}
|
|
|
|
// remember that we got input
|
|
pState->bGotInput = TRUE;
|
|
|
|
// push each line onto the list
|
|
iInputDataSize = 0;
|
|
while ((pInputLoop = JsonFind2(aJson, NULL, "msg[", iInputDataSize)) != NULL)
|
|
{
|
|
char strBuffer[sizeof(pState->LineData.strBuffer)];
|
|
int32_t iBufLen;
|
|
|
|
ds_memclr(&pState->LineData, sizeof(pState->LineData));
|
|
pState->LineData.iType = (int32_t)JsonGetInteger(JsonFind2(aJson, pInputLoop, ".TYPE", iInputDataSize), 0);
|
|
iBufLen = JsonGetString(JsonFind2(aJson, pInputLoop, ".TEXT", iInputDataSize), strBuffer, sizeof(strBuffer), "");
|
|
Base64Decode3(strBuffer, iBufLen, pState->LineData.strBuffer, sizeof(pState->LineData.strBuffer));
|
|
// add to the back of the list
|
|
// remember to add one for the terminator
|
|
ZListPushBack(pState->pInputData, &pState->LineData);
|
|
|
|
iInputDataSize += 1;
|
|
}
|
|
|
|
// kill the file memory we used to store the temp input
|
|
ZMemFree(pInputData);
|
|
|
|
// done - return how much we got from the file
|
|
return(iInputDataSize);
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _TesterCommCheckOutput
|
|
|
|
\Description
|
|
Check and send data from the output buffer, if possible.
|
|
|
|
\Input *pState - pointer to host client comm module
|
|
|
|
\Output int32_t - 0=success, error code otherwise
|
|
|
|
\Version 03/24/2005 (jfrank)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _TesterCommCheckOutput(TesterCommT *pState)
|
|
{
|
|
TesterCommFileT *pInterfaceData;
|
|
ZFileStatT FileStat;
|
|
ZFileT iTempFile;
|
|
int32_t iResult;
|
|
int32_t iLineLength=0;
|
|
#if TESTERCOMM_FILEPROFILING
|
|
int32_t iTime1, iTime2;
|
|
#endif
|
|
|
|
// file writing stuff
|
|
const char *pFileBuf;
|
|
|
|
// check for error conditions
|
|
if ((pState == NULL) || (pState->pInterface->pData == NULL))
|
|
{
|
|
return(-1);
|
|
}
|
|
pInterfaceData = pState->pInterface->pData;
|
|
|
|
// see if there's any data to send
|
|
if (ZListPeekFront(pState->pOutputData) == NULL)
|
|
{
|
|
// reset the last send time - we're not timing out because there's no data
|
|
pState->uLastSendTime = ZTick();
|
|
return(0);
|
|
}
|
|
|
|
// see if we've timed out
|
|
if (ZTick() - pState->uLastSendTime > TESTERCOMM_OUTPUTTIMEOUT_DEFAULT)
|
|
{
|
|
// timeout occurred - dump all pending messages
|
|
// and pop a warning
|
|
ZListClear(pState->pOutputData);
|
|
ZPrintf("testercomm: TIMEOUT in sending data. List cleared. Comms Lost?\n");
|
|
}
|
|
|
|
// we've got data to send in the output list
|
|
// see if we can send it in the file
|
|
iResult = ZFileStat(pInterfaceData->strOutputPathFile, &FileStat);
|
|
|
|
// only do something if the file doesn't exist
|
|
// so quit if the NOSUCHFILE isn't set
|
|
if (iResult != ZFILE_ERROR_NOSUCHFILE)
|
|
return(0);
|
|
|
|
// open the file
|
|
iTempFile = ZFileOpen(pInterfaceData->strTempPathFile, ZFILE_OPENFLAG_WRONLY | ZFILE_OPENFLAG_CREATE);
|
|
if (iTempFile == ZFILE_INVALID)
|
|
{
|
|
// could not open it - error condition
|
|
return(-1);
|
|
}
|
|
|
|
#if TESTERCOMM_FILEPROFILING
|
|
printf("--**-- [%12d] Starting get from buffer to file \n", ZTick());
|
|
#endif
|
|
JsonInit(pState->strCommand, sizeof(pState->strCommand), 0);
|
|
JsonArrayStart(pState->strCommand, "msg");
|
|
while(ZListPeekFront(pState->pOutputData))
|
|
{
|
|
char strBuffer[sizeof(pState->LineData.strBuffer)];
|
|
|
|
// snag the data into the local buffer
|
|
ZListPopFront(pState->pOutputData, &pState->LineData);
|
|
|
|
// create the stuff to write
|
|
JsonObjectStart(pState->strCommand, NULL);
|
|
JsonAddInt(pState->strCommand, "TYPE", pState->LineData.iType);
|
|
Base64Encode2(pState->LineData.strBuffer, (int32_t)strlen(pState->LineData.strBuffer), strBuffer, sizeof(strBuffer));
|
|
if ((iResult = JsonAddStr(pState->strCommand, "TEXT", strBuffer)) == JSON_ERR_FULL)
|
|
{
|
|
// Sometimes, the text we get in will be too big. Make sure this is an easy error to diagnose.
|
|
printf("Text too large to send in TESTERCOMM_COMMANDSIZE_DEFAULT. Discarding.\n");
|
|
}
|
|
JsonObjectEnd(pState->strCommand);
|
|
}
|
|
JsonArrayEnd(pState->strCommand);
|
|
pFileBuf = JsonFinish(pState->strCommand);
|
|
iLineLength = (int32_t)strlen(pFileBuf);
|
|
|
|
// write the last little chunk
|
|
if (iLineLength > 0)
|
|
{
|
|
#if TESTERCOMM_FILEPROFILING
|
|
iTime1 = ZTick();
|
|
#endif
|
|
ZFileWrite(iTempFile, (void *)pFileBuf, iLineLength);
|
|
#if TESTERCOMM_FILEPROFILING
|
|
iTime2 = ZTick();
|
|
printf("------ [%12d] Writing [%d] bytes to file took [%d] Ticks.\n",
|
|
ZTick(), iWrittenSize, iTime2 - iTime1);
|
|
#endif
|
|
}
|
|
|
|
#if TESTERCOMM_FILEPROFILING
|
|
printf("--**-- [%12d] Done pushing from buffer to file \n", ZTick());
|
|
#endif
|
|
|
|
// close the temp file
|
|
ZFileClose(iTempFile);
|
|
iTempFile = 0;
|
|
#if TESTERCOMM_FILEPROFILING
|
|
printf("--**-- [%12d] File closed. \n", ZTick());
|
|
#endif
|
|
|
|
// move the temp file into the real file
|
|
iResult = ZFileRename(pInterfaceData->strTempPathFile, pInterfaceData->strOutputPathFile);
|
|
if (iResult != 0)
|
|
{
|
|
NetPrintf(("testercomm: error [%d=0x%X] renaming [%s] to [%s]\n",
|
|
iResult, iResult, pInterfaceData->strTempPathFile, pInterfaceData->strOutputPathFile));
|
|
}
|
|
#if TESTERCOMM_FILEPROFILING
|
|
printf("--**-- [%12d] File renamed. \n", ZTick());
|
|
#endif
|
|
|
|
// mark the last send time
|
|
pState->uLastSendTime = ZTick();
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _TesterCommConnect
|
|
|
|
\Description
|
|
Connect the host client communication module.
|
|
|
|
\Input *pState - pointer to host client comm module
|
|
\Input *pParams - startup parameters
|
|
\Input bIsHost - TRUE=host, FALSE=client
|
|
|
|
\Output int32_t - 0 for success, error code otherwise
|
|
|
|
\Version 03/23/2005 (jfrank)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _TesterCommConnect(TesterCommT *pState, const char *pParams, uint32_t bIsHost)
|
|
{
|
|
TesterCommFileT *pInterfaceData;
|
|
uint32_t uControlDirSize;
|
|
uint32_t uTempPathFileSize;
|
|
uint32_t uInputPathFileSize;
|
|
uint32_t uOutputPathFileSize;
|
|
char strInputFile[256];
|
|
char strOutputFile[256];
|
|
char strControlDir[256];
|
|
uint16_t aJson[512];
|
|
|
|
// check for error conditions
|
|
if ((pState == NULL) || (pParams == NULL) || (pState->pInterface->pData == NULL))
|
|
{
|
|
ZPrintf("testercomm: connect got invalid NULL pointer - pState [0x%X] pParams [0x%X]\n", pState, pParams);
|
|
return(-1);
|
|
}
|
|
pInterfaceData = pState->pInterface->pData;
|
|
|
|
// get the necessary startup parameters
|
|
JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), pParams, -1);
|
|
ds_memclr(strInputFile, sizeof(strInputFile));
|
|
ds_memclr(strOutputFile, sizeof(strOutputFile));
|
|
ds_memclr(strControlDir, sizeof(strControlDir));
|
|
JsonGetString(JsonFind(aJson, "INPUTFILE"), strInputFile, sizeof(strInputFile), "");
|
|
JsonGetString(JsonFind(aJson, "OUTPUTFILE"), strOutputFile, sizeof(strOutputFile), "");
|
|
JsonGetString(JsonFind(aJson, "CONTROLDIR"), strControlDir, sizeof(strControlDir), "");
|
|
|
|
// check for more error conditions
|
|
if ((strlen(strInputFile) == 0) || (strlen(strOutputFile) == 0))
|
|
{
|
|
ZPrintf("testercomm: connect got invalid startup paths - INPUTFILE [%s] OUTPUTFILE [%s] CONTROLDIR [%s}\n",
|
|
strInputFile, strOutputFile, strControlDir);
|
|
return(-1);
|
|
}
|
|
|
|
// wipe out the current stored strings
|
|
ds_memclr((void *)pInterfaceData->strControlDir, sizeof(pInterfaceData->strControlDir));
|
|
ds_memclr((void *)pInterfaceData->strTempPathFile, sizeof(pInterfaceData->strTempPathFile));
|
|
ds_memclr((void *)pInterfaceData->strInputPathFile, sizeof(pInterfaceData->strInputPathFile));
|
|
ds_memclr((void *)pInterfaceData->strOutputPathFile, sizeof(pInterfaceData->strOutputPathFile));
|
|
|
|
// get the available string size
|
|
uControlDirSize = sizeof(pInterfaceData->strControlDir)-1;
|
|
uTempPathFileSize = sizeof(pInterfaceData->strTempPathFile)-1;
|
|
uInputPathFileSize = sizeof(pInterfaceData->strInputPathFile)-1;
|
|
uOutputPathFileSize = sizeof(pInterfaceData->strOutputPathFile)-1;
|
|
|
|
// if we got nothing in for the control directory, use the default
|
|
if (strlen(strControlDir) == 0)
|
|
{
|
|
strncat(pInterfaceData->strControlDir, TESTERCOMM_PATH_DEFAULT, uControlDirSize);
|
|
strncat(pInterfaceData->strTempPathFile, TESTERCOMM_PATH_DEFAULT, uTempPathFileSize);
|
|
strncat(pInterfaceData->strInputPathFile, TESTERCOMM_PATH_DEFAULT, uInputPathFileSize);
|
|
strncat(pInterfaceData->strOutputPathFile, TESTERCOMM_PATH_DEFAULT, uOutputPathFileSize);
|
|
}
|
|
else
|
|
{
|
|
strncat(pInterfaceData->strControlDir, strControlDir, uControlDirSize);
|
|
strncat(pInterfaceData->strTempPathFile, strControlDir, uTempPathFileSize);
|
|
strncat(pInterfaceData->strInputPathFile, strControlDir, uInputPathFileSize);
|
|
strncat(pInterfaceData->strOutputPathFile, strControlDir, uOutputPathFileSize);
|
|
}
|
|
|
|
// subtract off the amount of size we've used
|
|
uTempPathFileSize -= (uint32_t)strlen(pInterfaceData->strTempPathFile);
|
|
uInputPathFileSize -= (uint32_t)strlen(pInterfaceData->strInputPathFile);
|
|
uOutputPathFileSize -= (uint32_t)strlen(pInterfaceData->strOutputPathFile);
|
|
|
|
// attach the paths
|
|
if (strlen(strControlDir) > 0)
|
|
{
|
|
strncat(pInterfaceData->strTempPathFile, "/", uTempPathFileSize--);
|
|
strncat(pInterfaceData->strInputPathFile, "/", uInputPathFileSize--);
|
|
strncat(pInterfaceData->strOutputPathFile, "/", uOutputPathFileSize--);
|
|
}
|
|
|
|
// attach an extra char for the tempfile.
|
|
strncat(pInterfaceData->strTempPathFile, "_", uTempPathFileSize--);
|
|
|
|
// attach the filename
|
|
strncat(pInterfaceData->strTempPathFile, strOutputFile, uTempPathFileSize);
|
|
strncat(pInterfaceData->strInputPathFile, strInputFile, uInputPathFileSize);
|
|
strncat(pInterfaceData->strOutputPathFile, strOutputFile, uOutputPathFileSize);
|
|
|
|
// some debugging
|
|
ZPrintf("testercomm: connect ControlDir [%s]\n", pInterfaceData->strControlDir);
|
|
ZPrintf("testercomm: connect TempPathFile [%s]\n", pInterfaceData->strTempPathFile);
|
|
ZPrintf("testercomm: connect InputPathFile [%s]\n", pInterfaceData->strInputPathFile);
|
|
ZPrintf("testercomm: connect OutputPathFile [%s]\n", pInterfaceData->strOutputPathFile);
|
|
|
|
// wipe out any existing output files at startup (remove any previous junk)
|
|
ZFileDelete(pInterfaceData->strTempPathFile);
|
|
ZFileDelete(pInterfaceData->strOutputPathFile);
|
|
|
|
// dump anything from an incoming connection
|
|
ZFileDelete(pInterfaceData->strInputPathFile);
|
|
|
|
// set the tick time so we don't automatically get a timeout
|
|
pState->uLastSendTime = ZTick();
|
|
|
|
// done for now
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _TesterCommUpdate
|
|
|
|
\Description
|
|
Give the host/client interface module some processor time. Call this
|
|
once in a while to pump the input and output pipes.
|
|
|
|
\Input *pState - module state
|
|
|
|
\Output int32_t - 0 for success, error code otherwise
|
|
|
|
\Version 03/28/2005 (jfrank)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _TesterCommUpdate(TesterCommT *pState)
|
|
{
|
|
int32_t iResult;
|
|
|
|
if (pState == NULL)
|
|
return(-1);
|
|
|
|
// quit if we are suspended (don't do any more commands)
|
|
if (pState->uSuspended)
|
|
return(0);
|
|
|
|
// check for outgoing and incoming data
|
|
_TesterCommCheckOutput(pState);
|
|
_TesterCommCheckInput(pState);
|
|
|
|
// now call the callbacks for incoming messages
|
|
iResult = ZListPopFront(pState->pInputData, &pState->LineData);
|
|
while(iResult > 0)
|
|
{
|
|
// try to access the message map
|
|
if (pState->MessageMap[pState->LineData.iType] != NULL)
|
|
{
|
|
// protect against recursion by suspending commands until this one completes
|
|
TesterCommSuspend(pState);
|
|
(pState->MessageMap[pState->LineData.iType])(pState, pState->LineData.strBuffer, pState->pMessageMapUserData[pState->LineData.iType]);
|
|
TesterCommWake(pState);
|
|
}
|
|
|
|
// try to get the next chunk
|
|
iResult = ZListPopFront(pState->pInputData, &pState->LineData);
|
|
}
|
|
|
|
// done
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _TesterCommDisconnect
|
|
|
|
\Description
|
|
Disconnect the host client communication module.
|
|
|
|
\Input *pState - pointer to host client comm module
|
|
|
|
\Output int32_t - 0=success, error code otherwise
|
|
|
|
\Version 03/23/2005 (jfrank)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _TesterCommDisconnect(TesterCommT *pState)
|
|
{
|
|
TesterCommFileT *pData;
|
|
|
|
// check for error conditions
|
|
if ((pState == NULL) || (pState->pInterface->pData == NULL))
|
|
{
|
|
return(-1);
|
|
}
|
|
pData = pState->pInterface->pData;
|
|
|
|
// wipe out the current strings
|
|
ds_memclr((void *)pData->strControlDir, sizeof(pData->strControlDir));
|
|
ds_memclr((void *)pData->strTempPathFile, sizeof(pData->strTempPathFile));
|
|
ds_memclr((void *)pData->strInputPathFile, sizeof(pData->strInputPathFile));
|
|
ds_memclr((void *)pData->strOutputPathFile, sizeof(pData->strOutputPathFile));
|
|
|
|
// else return no error
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*** Public functions *************************************************************/
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function TesterCommAttachFile
|
|
|
|
\Description
|
|
Attach file module function pointers to a tester comm module.
|
|
|
|
\Input *pState - pointer to host client comm module
|
|
|
|
\Output None
|
|
|
|
\Version 05/02/2005 (jfrank)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void TesterCommAttachFile(TesterCommT *pState)
|
|
{
|
|
if(pState == NULL)
|
|
return;
|
|
|
|
ZPrintf("testercomm: attaching FILE interface methods\n");
|
|
|
|
pState->pInterface->CommConnectFunc = &_TesterCommConnect;
|
|
pState->pInterface->CommUpdateFunc = &_TesterCommUpdate;
|
|
pState->pInterface->CommDisconnectFunc = &_TesterCommDisconnect;
|
|
pState->pInterface->pData = (TesterCommFileT *)ZMemAlloc(sizeof(TesterCommFileT));
|
|
ds_memclr(pState->pInterface->pData, sizeof(TesterCommFileT));
|
|
}
|