/** * Destroy a placeholder. Mark this placeholder as free * and store the index in the pool of free placeholders. * @param index The placeholder handle. * @return Status code. */ int ResourceArray::_maDestroyPlaceholder(unsigned index) { // The handle must be a dynamic placeholder. if (!(index & DYNAMIC_PLACEHOLDER_BIT)) { // TODO: Use MYASSERT_IF_PANICS_ENABLED when // conditional panics are to be used. BIG_PHAT_ERROR(ERR_RES_PLACEHOLDER_NOT_DYNAMIC); return -2; } // Get the index into the dynamic resource array. unsigned i = index & (~DYNAMIC_PLACEHOLDER_BIT); TESTINDEX(i, mDynResSize); // The placeholder must not have been destroyed. if (RT_NIL == mDynResTypes[i]) { // TODO: Use MYASSERT_IF_PANICS_ENABLED when // conditional panics are to be used. BIG_PHAT_ERROR(ERR_RES_PLACEHOLDER_ALREADY_DESTROYED); return -2; } // Set the handle type to RT_NIL. This marks the // placeholder as destroyed. mDynResTypes[i] = RT_NIL; // Put handle into the pool. // Create or expand the pool as needed. if (0 == mDynResPoolCapacity) { // Create the initial pool. mDynResPoolCapacity = 2; mDynResPool = new unsigned[mDynResPoolCapacity]; MYASSERT(mDynResPool != NULL, ERR_OOM); } else if (mDynResPoolSize + 1 > mDynResPoolCapacity) { // Expand the pool. unsigned* oldDynResPool = mDynResPool; mDynResPool = new unsigned[mDynResPoolCapacity * 2]; MYASSERT(mDynResPool != NULL, ERR_OOM); // Copy from old to new and delete old. memcpy( mDynResPool, oldDynResPool, mDynResPoolCapacity * sizeof(unsigned)); delete []oldDynResPool; // Set new capacity. mDynResPoolCapacity = mDynResPoolCapacity * 2; } // Increment pool size. ++mDynResPoolSize; // Add free handle index last in array (push to stack). mDynResPool[mDynResPoolSize - 1] = index; return RES_OK; }
/** * /return The newly created Data Section as a Direct ByteBuffer object */ static void nativeRun(JNIEnv* env, jobject jthis) { SYSLOG("nativeRun"); Base::gSyscall->setJNIEnvironment(env, jthis); while(1) { Core::Run2(gCore); SYSLOG("Runtime yielded!"); // Check if we should load a resource as a program/resource combfile int reloadHandle = Base::gSyscall->getReloadHandle(); if (0 != reloadHandle) { SYSLOG("Program is loading from handle"); Base::Stream* stream = Base::gSyscall->resources.extract_RT_BINARY(reloadHandle); bool res = Core::LoadVMApp(gCore, *stream); delete stream; if (!res) { BIG_PHAT_ERROR(ERR_PROGRAM_LOAD_FAILED); } Base::gSyscall->setReloadHandle(0); } // Check if we should reload the initial program and resource file else if (Base::gSyscall->isReloading()) { SYSLOG("Program is reloading! 1"); Base::gSyscall->setReloading(false); SYSLOG("Program is reloading! 2"); jclass cls = env->GetObjectClass(jthis); jmethodID methodID = env->GetMethodID(cls, "loadProgram", "()Z"); if (methodID == 0) return; jboolean res = env->CallBooleanMethod(jthis, methodID); SYSLOG("Program is reloading! 3"); if (!res) { BIG_PHAT_ERROR(ERR_PROGRAM_LOAD_FAILED); } __android_log_write(ANDROID_LOG_INFO,"JNI","Program is reloading! 4"); } } }
void* ResourceArray::_extract(unsigned index, byte R) { void **res = mRes; byte *types = mResTypes; if(index&DYNAMIC_PLACEHOLDER_BIT) { res = mDynRes; types = mDynResTypes; index = index&(~DYNAMIC_PLACEHOLDER_BIT); TESTINDEX(index, mDynResSize); } else { TESTINDEX(index, mResSize); } if(types[index] != R) { BIG_PHAT_ERROR(ERR_RES_INVALID_TYPE); } #ifdef RESOURCE_MEMORY_LIMIT switch(types[index]) { #define CASE_SUBMEM(R, T, D) case R: mResmem -= size_##R((T*)res[index]); break; TYPES(CASE_SUBMEM); } if(mResmem > mResmemMax) { DEBIG_PHAT_ERROR; } #endif //RESOURCE_MEMORY_LIMIT void* temp = res[index]; res[index] = NULL; types[index] = RT_PLACEHOLDER; return temp; }
/** * Gets a pointer to the data for the given handle. * @param index The resource handle. * @param R The resource type. * @return a pointer to the data for the given handle. */ void* ResourceArray::_get(unsigned index, byte R) { void **res = mRes; byte *types = mResTypes; if(index&DYNAMIC_PLACEHOLDER_BIT) { res = mDynRes; types = mDynResTypes; index = index&(~DYNAMIC_PLACEHOLDER_BIT); TESTINDEX(index, mDynResSize); } else { TESTINDEX(index, mResSize); } if(types[index] != R) { BIG_PHAT_ERROR(ERR_RES_INVALID_TYPE); } return res[index]; }
/** * Add a resource. * @param index Resource index. * @param obj Pointer to object data. * @param type Resource type. */ int ResourceArray::_add(unsigned index, void* obj, byte type) { void **res = mRes; byte *types = mResTypes; if(index&DYNAMIC_PLACEHOLDER_BIT) { res = mDynRes; types = mDynResTypes; index = index&(~DYNAMIC_PLACEHOLDER_BIT); TESTINDEX(index, mDynResSize); } else { TESTINDEX(index, mResSize); } // obj is the resource data. If the resource is NULL // and not a placeholder or in flux (resource is changing) // then create a panic. if (obj == NULL && (type != RT_PLACEHOLDER && type != RT_FLUX)) { DEBIG_PHAT_ERROR; } // Resource at this index must not be in use, but it can be a // placeholder. If the resource is in use, a panic is generated. if(res[index] != NULL || types[index] != RT_PLACEHOLDER) { BIG_PHAT_ERROR(ERR_RES_OVERWRITE); } #ifdef RESOURCE_MEMORY_LIMIT int oldResmem = mResmem; switch(type) { #define CASE_ADDMEM(R, T, D) case R: mResmem += size_##R((T*)obj); break; TYPES(CASE_ADDMEM); } if(mResmem >= mResmemMax) { //BIG_PHAT_ERROR(ERR_RES_OOM); mResmem = oldResmem; __destroy(obj, type, index); //avoids memory leaks return RES_OUT_OF_MEMORY; } #endif //RESOURCE_MEMORY_LIMIT res[index] = obj; types[index] = type; return RES_OK; }
/* * Loads a resource from the stream, from originalHandle index to destHandle placeholder. */ bool Syscall::loadResource(Stream& file, MAHandle originalHandle, MAHandle destHandle) { if(!file.isOpen()) { return false; } if ((resourceType == NULL) || (resourceSize == NULL) || (resourceOffset == NULL)) { return false; } int type = resourceType[originalHandle - 1]; int size = resourceSize[originalHandle - 1]; int offset = resourceOffset[originalHandle - 1]; int rI = destHandle; if ( resources.is_loaded(rI) ) { return true; } TEST(file.seek(Seek::Start, offset)); switch(type) { case RT_BINARY: { #ifndef _android MemStream* ms = new MemStream(size); #else char* b = loadBinary(rI, size); MemStream* ms = new MemStream(b, size); #endif TEST(file.readFully(*ms)); ROOM(resources.dadd_RT_BINARY(rI, ms)); #ifdef _android checkAndStoreAudioResource(rI); #endif } break; case RT_IMAGE: { MemStream b(size); TEST(file.readFully(b)); #ifndef _android // On all platforms except Android, we load and add // the image data. "dadd" means "delete and add", // and is defined in runtimes\cpp\base\ResourceArray.h RT_IMAGE_Type* image = loadImage(b); if(!image) BIG_PHAT_ERROR(ERR_IMAGE_LOAD_FAILED); ROOM(resources.dadd_RT_IMAGE(rI, image)); #else // On Android images are stored on the Java side. // Here we allocate a dummy array (real image is // in a table in Java) so that the resource handling, // like deleting resources, will work also on Android. // The actual image will be garbage collected on // Android when a resource is replaced in the Java table. ROOM(resources.dadd_RT_IMAGE(rI, new int[1])); int pos; file.tell(pos); loadImage( rI, pos - size, size, Base::gSyscall->getReloadHandle()); #endif } break; case RT_SPRITE: { DAR_USHORT(indexSource); DAR_USHORT(left); DAR_USHORT(top); DAR_USHORT(width); DAR_USHORT(height); DAR_SHORT(cx); DAR_SHORT(cy); #ifndef _android ROOM(resources.dadd_RT_IMAGE(rI, loadSprite(resources.get_RT_IMAGE(indexSource), left, top, width, height, cx, cy))); #endif } break; default: LOG("Cannot load resource type %d.", type); } return true; }
/* * Loads all resources from the stream, except images, binaries and sprites. */ bool Syscall::loadResources(Stream& file, const char* aFilename) { bool hasResources = true; if(!file.isOpen()) hasResources = false; else { int len, pos; TEST(file.length(len)); TEST(file.tell(pos)); if(len == pos) hasResources = false; } if(!hasResources/* && aFilename != NULL*/) { resources.init(0); return true; } #define MATCH_BYTE(c) { DAR_UBYTE(b); if(b != c) { FAIL; } } MATCH_BYTE('M'); MATCH_BYTE('A'); MATCH_BYTE('R'); MATCH_BYTE('S'); DAR_UVINT(nResources); DAR_UVINT(rSize); resources.init(nResources); resourcesCount = nResources; resourceOffset = new int[nResources]; resourceSize = new int[nResources]; resourceType = new int[nResources]; resourcesFilename = new char[strlen(aFilename) + 1]; strcpy(resourcesFilename, aFilename); // rI is the resource index. int rI = 1; while(true) { DAR_UBYTE(type); if(type == 0) break; //dispose flag DAR_UVINT(size); LOG_RES("Type %i, size %i\n", type, size); int index = rI - 1; TEST(file.tell(resourceOffset[index])); resourceSize[index] = size; resourceType[index] = type; switch(type) { case RT_UBIN: { int pos; MYASSERT(aFilename, ERR_RES_LOAD_UBIN); TEST(file.tell(pos)); #ifndef _android ROOM(resources.dadd_RT_BINARY(rI, new LimitedFileStream(aFilename, pos, size))); #else // Android loads ubins by using JNI. loadUBinary(rI, pos, size); ROOM(resources.dadd_RT_BINARY(rI, new LimitedFileStream( aFilename, pos, size, getJNIEnvironment(), getJNIThis()))); #endif TEST(file.seek(Seek::Current, size)); } break; case RT_PLACEHOLDER: ROOM(resources.dadd_RT_PLACEHOLDER(rI, NULL)); break; case RT_LABEL: { MemStream b(size); TEST(file.readFully(b)); ROOM(resources.dadd_RT_LABEL(rI, new Label((const char*)b.ptr(), rI))); } break; #ifdef LOGGING_ENABLED case 99: //testtype #define DUMP_UVI { DAR_UVINT(u); LOG_RES("u %i\n", u); } #define DUMP_SVI { DAR_SVINT(s); LOG_RES("s %i\n", s); } DUMP_UVI; DUMP_UVI; DUMP_UVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; break; #endif default: TEST(file.seek(Seek::Current, size)); } rI++; } if(rI != nResources + 1) { LOG("rI %i, nR %i\n", rI, nResources); BIG_PHAT_ERROR(ERR_RES_FILE_INCONSISTENT); } LOG_RES("ResLoad complete\n"); return true; }
/* * Loads all resources from the given buffer. */ bool Syscall::loadResourcesFromBuffer(Stream& file, const char* aFilename) { bool hasResources = true; if(!file.isOpen()) hasResources = false; else { int len, pos; TEST(file.length(len)); TEST(file.tell(pos)); if(len == pos) hasResources = false; } if(!hasResources/* && aFilename != NULL*/) { resources.init(0); return true; } #define MATCH_BYTE(c) { DAR_UBYTE(b); if(b != c) { FAIL; } } MATCH_BYTE('M'); MATCH_BYTE('A'); MATCH_BYTE('R'); MATCH_BYTE('S'); DAR_UVINT(nResources); DAR_UVINT(rSize); resources.init(nResources); // rI is the resource index. int rI = 1; while(true) { DAR_UBYTE(type); if(type == 0) break; //dispose flag DAR_UVINT(size); LOG_RES("Type %i, size %i\n", type, size); switch(type) { case RT_BINARY: { #ifndef _android MemStream* ms = new MemStream(size); #else char* b = loadBinary(rI, size); MemStream* ms = new MemStream(b, size); #endif TEST(file.readFully(*ms)); ROOM(resources.dadd_RT_BINARY(rI, ms)); #ifdef _android checkAndStoreAudioResource(rI); #endif } break; case RT_UBIN: { int pos; MYASSERT(aFilename, ERR_RES_LOAD_UBIN); TEST(file.tell(pos)); #ifndef _android ROOM(resources.dadd_RT_BINARY(rI, new LimitedFileStream(aFilename, pos, size))); #else // Android loads ubins by using JNI. loadUBinary(rI, pos, size); ROOM(resources.dadd_RT_BINARY(rI, new LimitedFileStream( aFilename, pos, size, getJNIEnvironment(), getJNIThis()))); #endif TEST(file.seek(Seek::Current, size)); } break; case RT_PLACEHOLDER: ROOM(resources.dadd_RT_PLACEHOLDER(rI, NULL)); break; case RT_IMAGE: { MemStream b(size); TEST(file.readFully(b)); #ifndef _android // On all platforms except Android, we load and add // the image data. "dadd" means "delete and add", // and is defined in runtimes\cpp\base\ResourceArray.h RT_IMAGE_Type* image = loadImage(b); if(!image) BIG_PHAT_ERROR(ERR_IMAGE_LOAD_FAILED); ROOM(resources.dadd_RT_IMAGE(rI, image)); #else // On Android images are stored on the Java side. // Here we allocate a dummy array (real image is // in a table in Java) so that the resource handling, // like deleting resources, will work also on Android. // The actual image will be garbage collected on // Android when a resource is replaced in the Java table. ROOM(resources.dadd_RT_IMAGE(rI, new int[1])); int pos; file.tell(pos); loadImage( rI, pos - size, size, Base::gSyscall->getReloadHandle()); #endif } break; case RT_SPRITE: { DAR_USHORT(indexSource); DAR_USHORT(left); DAR_USHORT(top); DAR_USHORT(width); DAR_USHORT(height); DAR_SHORT(cx); DAR_SHORT(cy); #ifndef _android ROOM(resources.dadd_RT_IMAGE(rI, loadSprite(resources.get_RT_IMAGE(indexSource), left, top, width, height, cx, cy))); #endif } break; case RT_LABEL: { MemStream b(size); TEST(file.readFully(b)); ROOM(resources.dadd_RT_LABEL(rI, new Label((const char*)b.ptr(), rI))); } break; #ifdef LOGGING_ENABLED case 99: //testtype #define DUMP_UVI { DAR_UVINT(u); LOG_RES("u %i\n", u); } #define DUMP_SVI { DAR_SVINT(s); LOG_RES("s %i\n", s); } DUMP_UVI; DUMP_UVI; DUMP_UVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; DUMP_SVI; break; #endif default: TEST(file.seek(Seek::Current, size)); } rI++; } if(rI != nResources + 1) { LOG("rI %i, nR %i\n", rI, nResources); BIG_PHAT_ERROR(ERR_RES_FILE_INCONSISTENT); } LOG_RES("ResLoad complete\n"); return true; }
/** * /return The newly created Data Section as a Direct ByteBuffer object */ static void nativeRun(JNIEnv* env, jobject jthis) { __android_log_write(ANDROID_LOG_INFO,"JNI","native run"); Base::gSyscall->setJNIEnvironment(env, jthis); while(1) { Core::Run2(gCore); __android_log_write(ANDROID_LOG_INFO,"JNI","Runtime yielded!"); // Check if we should load a resource as a program/resource combfile int reloadHandle = Base::gSyscall->getReloadHandle(); if(0 != reloadHandle) { __android_log_write(ANDROID_LOG_INFO,"JNI","Program is loading from handle"); Base::Stream* stream = Base::gSyscall->resources.extract_RT_BINARY(reloadHandle); bool res = Core::LoadVMApp(gCore, *stream); delete stream; if(!res) { BIG_PHAT_ERROR(ERR_PROGRAM_LOAD_FAILED); } Base::gSyscall->setReloadHandle(0); } // TODO: Could this be a good place to check for pending // exceptions from the Java side? // See commented out function handlePendingExceptions // below for how to do this. //handlePendingExceptions(env); // TODO: Why is this code commented out? Document its purpose. // check if we should reload the initial program and resource file else if(true == Base::gSyscall->isReloading()) { __android_log_write(ANDROID_LOG_INFO,"JNI","Program is reloading! 1"); Base::gSyscall->setReloading(false); __android_log_write(ANDROID_LOG_INFO,"JNI","Program is reloading! 2"); jclass cls = env->GetObjectClass(jthis); jmethodID methodID = env->GetMethodID(cls, "loadProgram", "()Z"); if (methodID == 0) return; jboolean res = env->CallBooleanMethod(jthis, methodID); __android_log_write(ANDROID_LOG_INFO,"JNI","Program is reloading! 3"); if(!res) { BIG_PHAT_ERROR(ERR_PROGRAM_LOAD_FAILED); } __android_log_write(ANDROID_LOG_INFO,"JNI","Program is reloading! 4"); } } }
extern "C" void GCCATTRIB(noreturn) maLoadProgram(MAHandle data, int reload) { BIG_PHAT_ERROR(ERR_FUNCTION_UNSUPPORTED); }
int Base::Syscall::GetValidatedStackValue(int offset) { BIG_PHAT_ERROR(ERR_FUNCTION_UNSUPPORTED); }