vm_t *VM_Create( vmSlots_t vmSlot ) { vm_t *vm = NULL; // see if we already have the VM if ( vmTable[vmSlot] ) return vmTable[vmSlot]; // find a free vm vmTable[vmSlot] = (vm_t *)Z_Malloc( sizeof( *vm ), TAG_VM, qtrue ); vm = vmTable[vmSlot]; // initialise it vm->slot = vmSlot; Q_strncpyz( vm->name, vmNames[vmSlot], sizeof( vm->name ) ); // find the module api FS_FindPureDLL( vm->name ); Com_Printf( "VM_Create: %s"ARCH_STRING DLL_EXT, vm->name ); vm->dllHandle = Sys_LoadGameDll( vm->name, &vm->GetModuleAPI ); if ( vm->dllHandle ) { if ( com_developer->integer ) Com_Printf( " succeeded [0x%X+0x%X]\n", vm->dllHandle, (intptr_t)vm->GetModuleAPI - (intptr_t)vm->dllHandle ); else Com_Printf( " succeeded\n" ); return vm; } VM_Free( vm ); Com_Printf( " failed!\n" ); return NULL; }
vm_t *VM_CreateLegacy( vmSlots_t vmSlot, intptr_t( *systemCalls )(intptr_t *) ) { vm_t *vm = NULL; if ( !systemCalls ) { Com_Error( ERR_FATAL, "VM_CreateLegacy: bad parms" ); return NULL; } // see if we already have the VM if ( vmTable[vmSlot] ) return vmTable[vmSlot]; // find a free vm vmTable[vmSlot] = (vm_t *)Z_Malloc( sizeof(*vm), TAG_VM, qtrue ); vm = vmTable[vmSlot]; // initialise it vm->isLegacy = qtrue; vm->slot = vmSlot; Q_strncpyz( vm->name, vmNames[vmSlot], sizeof(vm->name) ); vm->legacy.syscall = systemCalls; // find the legacy syscall api FS_FindPureDLL( vm->name ); vm->dllHandle = Sys_LoadLegacyGameDll( vm->name, &vm->legacy.main, VM_DllSyscall ); Com_Printf( "VM_CreateLegacy: %s" ARCH_STRING DLL_EXT, vm->name ); if ( vm->dllHandle ) { if ( com_developer->integer ) Com_Printf( " succeeded [0x%" PRIxPTR "]\n", (uintptr_t)vm->dllHandle ); else Com_Printf( " succeeded\n" ); return vm; } VM_Free( vm ); Com_Printf( " failed!\n" ); return NULL; }
vm_t *VM_Create( const char *module, int (*systemCalls)(int *), vmInterpret_t interpret ) { vm_t *vm; vmHeader_t *header; int length; int dataLength; int i; char filename[MAX_QPATH]; if ( !module || !module[0] || !systemCalls ) { Com_Error( ERR_FATAL, "VM_Create: bad parms" ); } // see if we already have the VM for ( i = 0 ; i < MAX_VM ; i++ ) { if (!Q_stricmp(vmTable[i].name, module)) { vm = &vmTable[i]; return vm; } } // find a free vm for ( i = 0 ; i < MAX_VM ; i++ ) { if ( !vmTable[i].name[0] ) { break; } } if ( i == MAX_VM ) { Com_Error( ERR_FATAL, "VM_Create: no free vm_t" ); } vm = &vmTable[i]; Q_strncpyz( vm->name, module, sizeof( vm->name ) ); vm->systemCall = systemCalls; if ( interpret == VMI_NATIVE ) { // try to load as a system dll FS_FindPureDLL( module ); Com_Printf( "Loading dll file %s.\n", vm->name ); vm->dllHandle = Sys_LoadGameDll( module, &vm->entryPoint, VM_DllSyscall ); if ( vm->dllHandle ) { return vm; } Com_Printf( "Failed to load dll, looking for qvm.\n" ); interpret = VMI_COMPILED; } // load the image Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name ); Com_Printf( "Loading vm file %s.\n", filename ); length = FS_ReadFile( filename, (void **)&header ); if ( !header ) { Com_Printf( "Failed.\n" ); VM_Free( vm ); return NULL; } // byte swap the header for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) { ((int *)header)[i] = LittleLong( ((int *)header)[i] ); } // validate if ( header->vmMagic != VM_MAGIC || header->bssLength < 0 || header->dataLength < 0 || header->litLength < 0 || header->codeLength <= 0 ) { VM_Free( vm ); Com_Error( ERR_FATAL, "%s has bad header", filename ); } // round up to next power of 2 so all data operations can // be mask protected dataLength = header->dataLength + header->litLength + header->bssLength; for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) { } dataLength = 1 << i; // allocate zero filled space for initialized and uninitialized data vm->dataBase = (unsigned char *)VM_Alloc( dataLength ); vm->dataMask = dataLength - 1; // copy the intialized data Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength ); // byte swap the longs for ( i = 0 ; i < header->dataLength ; i += 4 ) { *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) ); } // allocate space for the jump targets, which will be filled in by the compile/prep functions vm->instructionPointersLength = header->instructionCount * 4; vm->instructionPointers = (int *)VM_Alloc( vm->instructionPointersLength ); // copy or compile the instructions vm->codeLength = header->codeLength; if ( interpret >= VMI_COMPILED ) { vm->compiled = qtrue; VM_Compile( vm, header ); } else { vm->compiled = qfalse; VM_PrepareInterpreter( vm, header ); } // free the original file FS_FreeFile( header ); // load the map file VM_LoadSymbols( vm ); // the stack is implicitly at the end of the image vm->programStack = vm->dataMask + 1; vm->stackBottom = vm->programStack - STACK_SIZE; return vm; }