int loom_net_initialize() { // Platform-specific init. #if LOOM_PLATFORM == LOOM_PLATFORM_WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 0); int err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { lmLogError(netLogGroup, "Failed WinSock initalization with error %d", err); return 0; } if (((LOBYTE(wsaData.wVersion) != 2) || (HIBYTE(wsaData.wVersion) != 0)) && ((LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))) { lmLogError(netLogGroup, "Failed WinSock initalization due to version mismatch."); WSACleanup(); return 0; } // Sanity checks. lmAssert(sizeof(SOCKET) <= sizeof(loom_socketId_t), "Can't pack a SOCKET into loom_socketId_t"); lmLogError(netLogGroup, "WinSock initialized."); return 1; #else // Ignore sigpipe. lmLogInfo(netLogGroup, "Disabling signal SIGPIPE"); signal(SIGPIPE, SIG_IGN); return 1; #endif }
void *loom_asset_lock(const char *name, unsigned int type, int block) { const char *namePtr = stringtable_insert(name); loom_mutex_lock(gAssetLock); // Look it up. loom_asset_t *asset = loom_asset_getAssetByName(namePtr, 1); lmAssert(asset != NULL, "Didn't get asset even though we should have!"); // If not loaded, and we aren't ready to block, return NULL. if ((block == 0) && (asset->state != loom_asset_t::Loaded)) { lmLogDebug(gAssetLogGroup, "Not blocking and not loaded yet; lock of '%s' failed.", namePtr); loom_mutex_unlock(gAssetLock); return NULL; } // Otherwise, let's force it to load now. if (asset->state != loom_asset_t::Loaded) { lmLogDebug(gAssetLogGroup, "Blocking so forcing load of '%s'.", namePtr); loom_asset_preload(namePtr); lmAssert(loom_asset_isOnTrackToLoad(asset), "Preloaded but wasn't on track to load!"); while (loom_asset_checkLoadedPercentage(namePtr) != 1.f && loom_asset_isOnTrackToLoad(asset)) { lmLogDebug(gAssetLogGroup, "Pumping load of '%s'...", namePtr); loom_asset_pump(); } if (asset->state != loom_asset_t::Loaded) { lmLogError(gAssetLogGroup, "Unable to load asset '%s'!", name); loom_mutex_unlock(gAssetLock); return NULL; } } // Check type. if (asset->type != type) { lmLogError(gAssetLogGroup, "Tried to lock asset '%s' with wrong type, assetType=%x, requestedType=%x", name, asset->type, type); loom_mutex_unlock(gAssetLock); return NULL; } // Inc count. asset->blob->incRef(); loom_mutex_unlock(gAssetLock); lmLogInfo(gAssetLogGroup, "Locked asset '%s'...", namePtr); // Return ptr. return asset->blob->bits; }
static void loom_net_setSocketReuseAddress(loom_socketId_t s, int reuse) { #if LOOM_PLATFORM_IS_APPLE == 1 int reuseTmp = reuse; int status = setsockopt((SOCKET)s, SOL_SOCKET, SO_REUSEPORT, &reuseTmp, sizeof(reuseTmp)); lmAssert(status == 0, "Failed trying to set socket reuse address status due to %d", status); #else // If you get issues where the socket stays bound after a listening process (like the assetAgent) // terminates you may need to set SO_REUSEADDR or SO_REUSEPORT or equivalent on your platform. lmLogInfo(netLogGroup, "Note: this platform doesn't support reusing port, but it probably doesn't matter."); #endif }
int loom_net_writeTCPSocket(loom_socketId_t s, void *buffer, int bytesToWrite) { #if LOOM_PLATFORM == LOOM_PLATFORM_WIN32 int winsockErrorCode; #endif int bytesLeft = bytesToWrite; for ( ; ; ) { int result = send((SOCKET)s, buffer, bytesLeft, MSG_NOSIGNAL); if (result >= 0) { bytesLeft -= result; if (bytesLeft != 0) { lmLogInfo(netLogGroup, "Partial write on socket %d, expected %d but wrote %d! Retrying...", s, bytesToWrite, result); // Set up to try again by advancing into the buffer. buffer = (void *)((char *)buffer + result); continue; } return bytesToWrite - bytesLeft; } #if LOOM_PLATFORM == LOOM_PLATFORM_WIN32 winsockErrorCode = WSAGetLastError(); if (winsockErrorCode != WSAEWOULDBLOCK) { break; } #else if (errno != EAGAIN) { break; } #endif if (loom_net_isSocketDead(s)) { break; } loom_thread_sleep(5); } return -1; }
// Helper to recognize an asset's type from its path/name. static int loom_asset_recognizeAssetTypeFromPath(utString& path) { // Easy out - empty strings are no good! if (path.length() == 0) { return 0; } // Walk backwards to first dot. size_t firstDotPos = path.size() - 1; for (size_t pos = path.size() - 1; pos > 0; pos--) { if (path.at(pos) != '.') { continue; } firstDotPos = pos; break; } // Split out the extension. utString pathExt = path.substr(firstDotPos + 1); // See if we can get a type out of any of the recognizers. int type = 0; for (UTsize i = 0; i < gRecognizerList.size(); i++) { type = gRecognizerList[i](pathExt.c_str()); if (type) { break; } } // No match, so let's use text. if (type == 0) { lmLogInfo(gAssetLogGroup, "Couldn't recognize '%s', defaulting to LATText...", path.c_str()); type = LATText; } return type; }
void QuadRenderer::initializeGraphicsResources() { LOOM_PROFILE_SCOPE(quadInit); lmLogInfo(gGFXQuadRendererLogGroup, "Initializing Graphics Resources"); GL_Context* ctx = Graphics::context(); // create the single initial vertex buffer ctx->glGenBuffers(1, &vertexBufferId); ctx->glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId); ctx->glBufferData(GL_ARRAY_BUFFER, MAXBATCHQUADS * 4 * sizeof(VertexPosColorTex), 0, GL_STREAM_DRAW); ctx->glBindBuffer(GL_ARRAY_BUFFER, 0); // create the single, reused index buffer ctx->glGenBuffers(1, &indexBufferId); ctx->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferId); uint16_t *pIndex = (uint16_t*)lmAlloc(gQuadMemoryAllocator, sizeof(unsigned short) * 6 * MAXBATCHQUADS); uint16_t *pStart = pIndex; int j = 0; for (int i = 0; i < 6 * MAXBATCHQUADS; i += 6, j += 4, pIndex += 6) { pIndex[0] = j; pIndex[1] = j + 2; pIndex[2] = j + 1; pIndex[3] = j + 1; pIndex[4] = j + 2; pIndex[5] = j + 3; } ctx->glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAXBATCHQUADS * 6 * sizeof(uint16_t), pStart, GL_STREAM_DRAW); ctx->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); lmFree(gQuadMemoryAllocator, pStart); // Create the system memory buffer for quads. batchedVertices = static_cast<VertexPosColorTex*>(lmAlloc(gQuadMemoryAllocator, MAXBATCHQUADS * 4 * sizeof(VertexPosColorTex))); }
void *loom_asset_soundDeserializer( void *buffer, size_t bufferLen, LoomAssetCleanupCallback *dtor ) { loom_asset_sound_t *sound = (loom_asset_sound_t*)lmAlloc(gAssetAllocator, sizeof(loom_asset_sound_t)); memset(sound, 0, sizeof(loom_asset_sound_t)); unsigned char *charBuff = (unsigned char *)buffer; // Look for magic header in buffer. if(charBuff[0] == 0x4f && charBuff[1] == 0x67 && charBuff[2] == 0x67 && charBuff[3] == 0x53) { // It's an Ogg, assume vorbis and throw it to stb_vorbis. int channels = 0; short *outputBuffer = NULL; int sampleCount = stb_vorbis_decode_memory(charBuff, bufferLen, &channels, &outputBuffer); if(sampleCount < 0) { lmLogError(gSoundAssetGroup, "Failed to decode Ogg Vorbis!"); loom_asset_soundDtor(&sound); return NULL; } sound->channels = channels; sound->bytesPerSample = 2; sound->sampleCount = sampleCount; sound->bufferSize = sampleCount * channels * 2; sound->sampleRate = 44100; // TODO: This should be variable // We can skip this if we get clever about allocations in stbv. sound->buffer = lmAlloc(gAssetAllocator, sound->bufferSize); memcpy(sound->buffer, outputBuffer, sound->bufferSize); free(outputBuffer); } else if((charBuff[0] == 0x49 // ID3 && charBuff[1] == 0x44 && charBuff[2] == 0x33) || (charBuff[0] == 0xff // Missing ID3 Tag && charBuff[1] == 0xfb)) { // It's an MP3, y'all! short *outBuffer = (short*)lmAlloc(gAssetAllocator, MP3_MAX_SAMPLES_PER_FRAME * 2); mp3_info_t mp3Info; // Decode once to get total size. int totalBytes = 0; int bytesRead = 0, bytesLeft = bufferLen; mp3_decoder_t decmp3 = mp3_create(); for(;;) { int bytesDecoded = mp3_decode(decmp3, charBuff + bytesRead, bytesLeft, outBuffer, &mp3Info); bytesRead += bytesDecoded; bytesLeft -= bytesDecoded; totalBytes += mp3Info.audio_bytes; if(bytesDecoded > 0) continue; // Clean up. mp3_done(decmp3); break; } // Great, set up the sound asset. // TODO: Warn about non 44.1khz mp3s. sound->channels = mp3Info.channels; sound->bytesPerSample = 2; sound->sampleCount = totalBytes / sound->bytesPerSample; sound->bufferSize = sound->channels * sound->bytesPerSample * sound->sampleCount; sound->sampleRate = 44100; // TODO: This should be variable sound->buffer = lmAlloc(gAssetAllocator, sound->bufferSize); // Decode again to get real samples. decmp3 = mp3_create(); bytesRead = 0; bytesLeft = bufferLen; int curBufferOffset = 0; for(;;) { int bytesDecoded = mp3_decode(decmp3, charBuff + bytesRead, bytesLeft, outBuffer, &mp3Info); bytesRead += bytesDecoded; bytesLeft -= bytesDecoded; memcpy(((unsigned char*)sound->buffer) + curBufferOffset, outBuffer, mp3Info.audio_bytes); curBufferOffset += mp3Info.audio_bytes; if(bytesDecoded > 0) continue; // Clean up. mp3_done(decmp3); break; } // Awesome, all set! lmFree(gAssetAllocator, outBuffer); } else if(charBuff[0] == 0x52 // 'RIFF' && charBuff[1] == 0x49 && charBuff[2] == 0x46 && charBuff[3] == 0x46) { // We've got a wav file wav_info wav; bool wavLoadSuccess = load_wav(charBuff, bufferLen, NULL, &wav); if (!wavLoadSuccess) { lmLogError(gSoundAssetGroup, "Failed to load wav format info"); loom_asset_soundDtor(sound); return 0; } sound->channels = wav.numChannels; sound->bytesPerSample = wav.sampleSize / 8; // wav sample size is in bits if (sound->bytesPerSample != 1 && sound->bytesPerSample != 2) { lmLogError(gSoundAssetGroup, "Unsupported wav format. Currently only 8-bit or 16-bit PCM are supported"); loom_asset_soundDtor(sound); return 0; } sound->bufferSize = wav.sampleDataSize; sound->sampleCount = sound->bufferSize / sound->bytesPerSample; sound->sampleRate = wav.samplesPerSecond; sound->buffer = lmAlloc(gAssetAllocator, sound->bufferSize); bool dataCopySuccess = load_wav(charBuff, bufferLen, (uint8_t*)sound->buffer, NULL); if (!dataCopySuccess) { lmLogError(gSoundAssetGroup, "Failed to copy wav data"); loom_asset_soundDtor(sound); return 0; } } else { lmLogError(gSoundAssetGroup, "Failed to identify sound buffer by magic number!"); loom_asset_soundDtor(sound); return 0; } *dtor = loom_asset_soundDtor; if(!sound->buffer) { lmLogError(gSoundAssetGroup, "Sound load failed due to this cryptic reason: %s", "(unknown)"); lmFree(gAssetAllocator, sound); return 0; } lmLogInfo(gSoundAssetGroup, "Allocated %d bytes for a sound!", sound->bufferSize); return sound; }
void QuadRenderer::initializeGraphicsResources() { lmLogInfo(gGFXQuadRendererLogGroup, "Initializing Graphics Resources"); //lmLogInfo(gGFXQuadRendererLogGroup, "OpenGL error %d", Graphics::context()->glGetError()); // Create the quad shader. GLuint vertShader = Graphics::context()->glCreateShader(GL_VERTEX_SHADER); //GLuint fragShader = Graphics::context()->glCreateShader(GL_FRAGMENT_SHADER); GLuint fragShaderColor = Graphics::context()->glCreateShader(GL_FRAGMENT_SHADER); GLuint quadProg = Graphics::context()->glCreateProgram(); GLuint quadProgColor = Graphics::context()->glCreateProgram(); char vertShaderSrc[] = "attribute vec4 a_position;\n" "attribute vec4 a_color0;\n" "attribute vec2 a_texcoord0;\n" "varying vec2 v_texcoord0;\n" "varying vec4 v_color0;\n" "uniform vec4 screenSize;\n" "void main()\n" "{\n" " gl_Position = a_position / vec4(screenSize.x / 2.0, -screenSize.y / 2.0, 1.0, 1.0) + vec4(-1, 1, 0.0, 0.0);\n" " v_color0 = a_color0;\n" " v_texcoord0 = a_texcoord0;\n" "}\n"; const int vertShaderLen = sizeof(vertShaderSrc); GLchar *vertShaderPtr = &vertShaderSrc[0]; /* */ char fragShaderColorSrc[] = #if LOOM_RENDERER_OPENGLES2 "precision mediump float;\n" #endif "uniform sampler2D u_texture;\n" "varying vec2 v_texcoord0;\n" "varying vec4 v_color0\n;" "void main()\n" "{\n" " gl_FragColor = v_color0 * texture2D(u_texture, v_texcoord0);\n" "}\n"; const int fragShaderColorLen = sizeof(fragShaderColorSrc); GLchar *fragShaderColorPtr = &fragShaderColorSrc[0]; Graphics::context()->glShaderSource(vertShader, 1, &vertShaderPtr, &vertShaderLen); Graphics::context()->glCompileShader(vertShader); char error[4096]; GLsizei outLen = 0; Graphics::context()->glGetShaderInfoLog(vertShader, 4096, &outLen, error); lmLogInfo(gGFXQuadRendererLogGroup, "Program info log %s", error); Graphics::context()->glShaderSource(fragShaderColor, 1, &fragShaderColorPtr, &fragShaderColorLen); Graphics::context()->glCompileShader(fragShaderColor); Graphics::context()->glGetShaderInfoLog(fragShaderColor, 4096, &outLen, error); lmLogInfo(gGFXQuadRendererLogGroup, "Program info log %s", error); Graphics::context()->glAttachShader(quadProgColor, fragShaderColor); Graphics::context()->glAttachShader(quadProgColor, vertShader); Graphics::context()->glLinkProgram(quadProgColor); Graphics::context()->glGetProgramInfoLog(quadProgColor, 4096, &outLen, error); lmLogInfo(gGFXQuadRendererLogGroup, "Program info log %s", error); /* char fragShaderSrc[] = #if LOOM_RENDERER_OPENGLES2 "precision mediump float;\n" #endif "uniform sampler2D u_texture;\n" "varying vec2 v_texcoord0;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(u_texture, v_texcoord0);\n" "}\n"; const int fragShaderLen = sizeof(fragShaderSrc); GLchar *fragShaderPtr = &fragShaderSrc[0]; Graphics::context()->glShaderSource(fragShader, 1, &fragShaderPtr, &fragShaderLen); Graphics::context()->glCompileShader(fragShader); Graphics::context()->glGetShaderInfoLog(fragShader, 4096, &outLen, error); lmLogInfo(gGFXQuadRendererLogGroup, "Program info log %s", error); Graphics::context()->glAttachShader(quadProg, fragShader); Graphics::context()->glAttachShader(quadProg, vertShader); Graphics::context()->glLinkProgram(quadProg); Graphics::context()->glGetProgramInfoLog(quadProg, 4096, &outLen, error); lmLogInfo(gGFXQuadRendererLogGroup, "Program info log %s", error); */ // Get attributes and uniforms. sProgram_posAttribLoc = Graphics::context()->glGetAttribLocation(quadProgColor, "a_position"); sProgram_posColorLoc = Graphics::context()->glGetAttribLocation(quadProgColor, "a_color0"); sProgram_posTexCoordLoc = Graphics::context()->glGetAttribLocation(quadProgColor, "a_texcoord0"); sProgram_texUniform = Graphics::context()->glGetUniformLocation(quadProgColor, "u_texture"); sProgram_screenSize = Graphics::context()->glGetUniformLocation(quadProgColor, "screenSize"); // Save program for later! sProgramPosColorTex = sProgramPosTex = quadProgColor; // create the single initial vertex buffer numVertexBuffers = 0; _initializeNextVertexBuffer(); // create the single, reused index buffer Graphics::context()->glGenBuffers(1, &sIndexBufferHandle); Graphics::context()->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sIndexBufferHandle); uint16_t *pIndex = (uint16_t*)lmAlloc(gQuadMemoryAllocator, sizeof(unsigned short) * 6 * MAXBATCHQUADS); uint16_t *pStart = pIndex; int j = 0; for (int i = 0; i < 6 * MAXBATCHQUADS; i += 6, j += 4, pIndex += 6) { pIndex[0] = j; pIndex[1] = j + 2; pIndex[2] = j + 1; pIndex[3] = j + 1; pIndex[4] = j + 2; pIndex[5] = j + 3; } Graphics::context()->glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAXBATCHQUADS * 6 * sizeof(uint16_t), pStart, GL_STATIC_DRAW); Graphics::context()->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); lmFree(gQuadMemoryAllocator, pStart); // Create the system memory buffer for quads. vertexDataMemory = lmAlloc(gQuadMemoryAllocator, MAXVERTEXBUFFERS * MAXBATCHQUADS * 4 * sizeof(VertexPosColorTex)); for(int i=0; i<MAXVERTEXBUFFERS; i++) vertexData[i] = ((VertexPosColorTex*)vertexDataMemory) + (MAXBATCHQUADS * 4) * i; }
void QuadRenderer::initializeGraphicsResources() { const bgfx::Memory *mem = NULL; lmLogInfo(gGFXQuadRendererLogGroup, "Initializing Graphics Resources"); // Create texture sampler uniforms. sUniformTexColor = bgfx::createUniform("u_texColor", bgfx::UniformType::Uniform1iv); sUniformNodeMatrixRemoveMe = bgfx::createUniform("u_nodeMatrix", bgfx::UniformType::Uniform4x4fv); int sz; const uint8_t *pshader; // Load vertex shader. bgfx::VertexShaderHandle vsh_pct; pshader = GetVertexShaderPosColorTex(sz); mem = bgfx::makeRef(pshader, sz); vsh_pct = bgfx::createVertexShader(mem); bgfx::VertexShaderHandle vsh_pt; pshader = GetVertexShaderPosTex(sz); mem = bgfx::makeRef(pshader, sz); vsh_pt = bgfx::createVertexShader(mem); // Load fragment shaders. bgfx::FragmentShaderHandle fsh_pct; pshader = GetFragmentShaderPosColorTex(sz); mem = bgfx::makeRef(pshader, sz); fsh_pct = bgfx::createFragmentShader(mem); bgfx::FragmentShaderHandle fsh_pt; pshader = GetFragmentShaderPosTex(sz); mem = bgfx::makeRef(pshader, sz); fsh_pt = bgfx::createFragmentShader(mem); // Create program from shaders. sProgramPosColorTex = bgfx::createProgram(vsh_pct, fsh_pct); sProgramPosTex = bgfx::createProgram(vsh_pt, fsh_pt); // We can destroy vertex and fragment shader here since // their reference is kept inside bgfx after calling createProgram. // Vertex and fragment shader will be destroyed once program is // destroyed. bgfx::destroyVertexShader(vsh_pct); bgfx::destroyVertexShader(vsh_pt); bgfx::destroyFragmentShader(fsh_pct); bgfx::destroyFragmentShader(fsh_pt); // create the vertex stream sVertexPosColorTexDecl.begin(); sVertexPosColorTexDecl.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float); sVertexPosColorTexDecl.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true); sVertexPosColorTexDecl.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float); sVertexPosColorTexDecl.end(); // create the single, reused quad index buffer numVertexBuffers = 0; vertexBuffers[numVertexBuffers++] = bgfx::createDynamicVertexBuffer(MAXBATCHQUADS * 4, sVertexPosColorTexDecl); mem = bgfx::alloc(sizeof(uint16_t) * 6 * MAXBATCHQUADS); uint16_t *pindice = (uint16_t *)mem->data; int j = 0; for (int i = 0; i < 6 * MAXBATCHQUADS; i += 6, j += 4, pindice += 6) { pindice[0] = j; pindice[1] = j + 2; pindice[2] = j + 1; pindice[3] = j + 1; pindice[4] = j + 2; pindice[5] = j + 3; } sIndexBufferHandle = bgfx::createIndexBuffer(mem); size_t bufferSize = MAXVERTEXBUFFERS * sizeof(VertexPosColorTex) * MAXBATCHQUADS * 4; vertexDataMemory = lmAlloc(gQuadMemoryAllocator, bufferSize); lmAssert(vertexDataMemory, "Unable to allocate buffer for quad vertex data"); VertexPosColorTex* p = (VertexPosColorTex*) vertexDataMemory; for (int i = 0; i < MAXVERTEXBUFFERS; i++) { // setup buffer pointer vertexData[i] = p; p += MAXBATCHQUADS * 4; } }
virtual bool handleMessage(int fourcc, AssetProtocolHandler *handler, NetworkBuffer& buffer) { switch (fourcc) { case LOOM_FOURCC('F', 'I', 'L', 'E'): { // How many pending files? gPendingFiles = buffer.readInt(); loom_asset_notifyPendingCountChange(); // Read the filename. char *path; int fileStringLength; buffer.readString(&path, &fileStringLength); // And the file length. int bitsLength = buffer.readInt(); // Checkpoint at end! buffer.readCheckpoint(0xDEADBEE3); // Prepare the buffer! if (pendingFile != NULL) { lmLogError(gAssetLogGroup, "Got a new FILE '%s' while still processing existing file '%s'!", path, pendingFilePath.c_str()); wipePendingData(); } // Update the pending file state. pendingFilePath = path; lmFree(NULL, path); path = NULL; pendingFileLength = bitsLength; pendingFile = (const char *)lmAlloc(gAssetAllocator, pendingFileLength); // Log it. lmLogDebug(gAssetLogGroup, "FILE '%s' %d bytes incoming.", pendingFilePath.c_str(), bitsLength); // Awesome, sit back and wait for chunks to come in. return true; } case LOOM_FOURCC('F', 'C', 'H', 'K'): { // How many pending files? gPendingFiles = buffer.readInt(); loom_asset_notifyPendingCountChange(); // Get the offset. int chunkOffset = buffer.readInt(); // Read bits into the buffer. char *fileBits; int fileBitsLength; buffer.readString(&fileBits, &fileBitsLength); memcpy((void *)(pendingFile + chunkOffset), (void *)fileBits, fileBitsLength); lmFree(NULL, fileBits); // Checkpoint at end! buffer.readCheckpoint(0xDEADBEE2); int lastByteOffset = chunkOffset + fileBitsLength; // Log it. lmLogInfo(gAssetLogGroup, "FILE '%s' %d of %d bytes!", pendingFilePath.c_str(), lastByteOffset, pendingFileLength); // If it's the last one, instate it and wipe our buffer. if (lastByteOffset == pendingFileLength) { // And this resolves a file so decrement the pending count. This way // we will get to zero. gPendingFiles--; loom_asset_notifyPendingCountChange(); // Instate the new asset data. loom_asset_t *asset = loom_asset_getAssetByName(pendingFilePath.c_str(), 1); int assetType = loom_asset_recognizeAssetTypeFromPath(pendingFilePath); if (assetType == 0) { lmLogDebug(gAssetLogGroup, "Couldn't infer file type for '%s', ignoring.", pendingFilePath.c_str()); wipePendingData(); return true; } lmLogWarn(gAssetLogGroup, "Applying new version of '%s', %d bytes.", pendingFilePath.c_str(), pendingFileLength); LoomAssetCleanupCallback dtor = NULL; void *assetBits = loom_asset_deserializeAsset(pendingFilePath.c_str(), assetType, pendingFileLength, (void *)pendingFile, &dtor); asset->instate(assetType, assetBits, dtor); // And wipe the pending date. wipePendingData(); } } return true; } return false; }