/* Copyright (c) 2014 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "inputcontroller/InputSession.h" #include "utilities/IOUtilities.h" #include InputSession * InputSession_loadFile(const char * filePath, enum InputSessionError * outError) { InputSession * self; void * fileData; size_t length; fileData = readFileSimple(filePath, &length); if (fileData == NULL) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_FILE_NOT_FOUND; } return NULL; } self = InputSession_loadData(fileData, length, outError); free(fileData); return self; } static bool readUInt16(struct memreadContext * context, uint16_t * outUInt16) { uint16_t uint16; if (!memread(context, 2, &uint16)) { return false; } #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ uint16 = swapEndian16(uint16); #endif *outUInt16 = uint16; return true; } static bool readUInt32(struct memreadContext * context, uint32_t * outUInt32) { uint32_t uint32; if (!memread(context, 4, &uint32)) { return false; } #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ uint32 = swapEndian32(uint32); #endif *outUInt32 = uint32; return true; } InputSession * InputSession_loadData(const void * data, size_t length, enum InputSessionError * outError) { uint8_t uint8; uint16_t uint16; uint32_t uint32; struct memreadContext context = memreadContextInit(data, length); if (!readUInt16(&context, &uint16)) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_PREMATURE_EOF; } return NULL; } unsigned int formatVersion = uint16; if (formatVersion > INPUT_SESSION_FORMAT_VERSION) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_FORMAT_TOO_NEW; } return NULL; } if (formatVersion < INPUT_SESSION_MIN_FORMAT_VERSION) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_FORMAT_TOO_OLD; } return NULL; } if (!readUInt32(&context, &uint32)) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_PREMATURE_EOF; } return NULL; } InputSession * self = malloc(sizeof(*self)); self->replayStartupDataSize = uint32; if (self->replayStartupDataSize == 0) { self->replayStartupData = NULL; } else { self->replayStartupData = malloc(self->replayStartupDataSize); if (!memread(&context, self->replayStartupDataSize, self->replayStartupData)) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_PREMATURE_EOF; } free(self->replayStartupData); free(self); return NULL; } } if (!readUInt16(&context, &uint16)) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_PREMATURE_EOF; } free(self->replayStartupData); free(self); return NULL; } self->actionCount = uint16; if (self->actionCount == 0) { self->actions = NULL; } else { self->actions = malloc(self->actionCount * sizeof(*self->actions)); for (unsigned int actionIndex = 0; actionIndex < self->actionCount; actionIndex++) { size_t actionStartPosition = context.position; while (context.position < context.length && ((char *) context.data)[context.position] != '\x00') { context.position++; } context.position++; if (context.position > context.length) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_PREMATURE_EOF; } free(self->replayStartupData); free(self->actions); free(self); return NULL; } self->actions[actionIndex] = Atom_fromString(context.data + actionStartPosition); } } unsigned int eventAllocatedCount = 64, eventCount = 0; self->events = malloc(eventAllocatedCount * sizeof(*self->events)); while (context.position < context.length) { if (eventCount >= eventAllocatedCount) { eventAllocatedCount *= 2; self->events = realloc(self->events, eventAllocatedCount * sizeof(*self->events)); } if (!readUInt32(&context, &uint32)) { break; } self->events[eventCount].frameDelta = uint32; if (!readUInt16(&context, &uint16)) { break; } self->events[eventCount].actionIndex = uint16; if (self->events[eventCount].actionIndex >= self->actionCount) { if (outError != NULL) { *outError = INPUT_SESSION_ERROR_INVALID_ACTION_INDEX; } free(self->replayStartupData); free(self->actions); free(self->events); free(self); return NULL; } if (!memread(&context, 1, &uint8)) { break; } self->events[eventCount].eventType = uint8; if (uint8 == INPUT_SESSION_EVENT_MOTION) { union {uint32_t uint32; fixed16_16 fixed;} uint32Union; if (!readUInt32(&context, &uint32Union.uint32)) { break; } self->events[eventCount].valueX = uint32Union.fixed; if (!readUInt32(&context, &uint32Union.uint32)) { break; } self->events[eventCount].valueY = uint32Union.fixed; if (!readUInt32(&context, &uint32Union.uint32)) { break; } self->events[eventCount].rawValueX = uint32Union.fixed; if (!readUInt32(&context, &uint32Union.uint32)) { break; } self->events[eventCount].rawValueY = uint32Union.fixed; } eventCount++; } self->eventCount = eventCount; return self; } void InputSession_dispose(InputSession * inputSession) { free(inputSession->replayStartupData); free(inputSession->actions); free(inputSession->events); free(inputSession); }