/******************************************************************** * ConvertTMPQShunt ********************************************************************/ void ConvertTMPQShunt(void *shunt) { TMPQShunt * theShunt = (TMPQShunt *)shunt; theShunt->dwID = SwapULong(theShunt->dwID); theShunt->dwUnknown = SwapULong(theShunt->dwUnknown); theShunt->dwHeaderPos = SwapULong(theShunt->dwHeaderPos); }
/******************************************************************** * ConvertTMPQUserData ********************************************************************/ void ConvertTMPQUserData(void *userData) { TMPQUserData * theData = (TMPQUserData *)userData; theData->dwID = SwapULong(theData->dwID); theData->cbUserDataSize = SwapULong(theData->cbUserDataSize); theData->dwHeaderOffs = SwapULong(theData->dwHeaderOffs); theData->cbUserDataHeader = SwapULong(theData->cbUserDataHeader); }
/******************************************************************** * ConvertTMPQHeader ********************************************************************/ void ConvertTMPQHeader(void *header) { TMPQHeader2 * theHeader = (TMPQHeader2 *)header; theHeader->dwID = SwapULong(theHeader->dwID); theHeader->dwHeaderSize = SwapULong(theHeader->dwHeaderSize); theHeader->dwArchiveSize = SwapULong(theHeader->dwArchiveSize); theHeader->wFormatVersion = SwapUShort(theHeader->wFormatVersion); theHeader->wBlockSize = SwapUShort(theHeader->wBlockSize); theHeader->dwHashTablePos = SwapULong(theHeader->dwHashTablePos); theHeader->dwBlockTablePos = SwapULong(theHeader->dwBlockTablePos); theHeader->dwHashTableSize = SwapULong(theHeader->dwHashTableSize); theHeader->dwBlockTableSize = SwapULong(theHeader->dwBlockTableSize); if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) { DWORD dwTemp = theHeader->ExtBlockTablePos.LowPart; theHeader->ExtBlockTablePos.LowPart = theHeader->ExtBlockTablePos.HighPart; theHeader->ExtBlockTablePos.HighPart = dwTemp; theHeader->ExtBlockTablePos.LowPart = SwapULong(theHeader->ExtBlockTablePos.LowPart); theHeader->ExtBlockTablePos.HighPart = SwapULong(theHeader->ExtBlockTablePos.HighPart); theHeader->wHashTablePosHigh = SwapUShort(theHeader->wHashTablePosHigh); theHeader->wBlockTablePosHigh = SwapUShort(theHeader->wBlockTablePosHigh); } }
// // ACS_countOpSizeACS0 // // Some instructions require special handling to find next index. // static uint32_t ACS_countOpSizeACS0(acs0_tracer_t *tracer, uint32_t index, uint32_t opSize, const acs0_opdata_t *opdata) { switch(opdata->op) { case ACS0_OP_LINESPEC1_IMM_BYTE: case ACS0_OP_LINESPEC2_IMM_BYTE: case ACS0_OP_LINESPEC3_IMM_BYTE: case ACS0_OP_LINESPEC4_IMM_BYTE: case ACS0_OP_LINESPEC5_IMM_BYTE: case ACS0_OP_GET_IMM_BYTE: case ACS0_OP_GET2_IMM_BYTE: case ACS0_OP_GET3_IMM_BYTE: case ACS0_OP_GET4_IMM_BYTE: case ACS0_OP_GET5_IMM_BYTE: case ACS0_OP_DELAY_IMM_BYTE: case ACS0_OP_RANDOM_IMM_BYTE: return opSize + opdata->args; case ACS0_OP_GETARR_IMM_BYTE: // Need room for the byte count. if(index + opSize > tracer->lumpLength - 4) return opSize; return opSize + 1 + tracer->data[index + opSize]; case ACS0_OP_BRANCH_CASETABLE: // Set opSize to the op plus alignment. opSize = ((index + opSize + 3) & ~3) - index; // Need room for the case count. if(index + opSize > tracer->lumpLength - 4) return opSize; // Read the number of cases. return opSize + (SwapULong(*(uint32_t *)(tracer->data + index + opSize)) * 8) + 4; case ACS0_OP_CALLFUNC: return opSize + (tracer->compressed ? 3 : 8); default: return opSize + opdata->args * (tracer->compressed && opdata->compressed ? 1 : 4); } }
// // ACS_LoadScriptACS0 // void ACS_LoadScriptACS0(ACSVM *vm, WadDirectory *dir, int lump, byte *data) { uint32_t lumpAvail; // Used in bounds checking. uint32_t lumpLength = dir->lumpLength(lump); int32_t *rover; uint32_t tableIndex; // Header + table index + script count + string count = 16 bytes. if (lumpLength < 16) return; lumpAvail = lumpLength - 16; rover = (int32_t *)data + 1; // Find script table. tableIndex = SwapLong(*rover); // Aha, but there might really be an ACSE header here! if(tableIndex >= 8 && tableIndex <= lumpLength) { uint32_t fakeHeader = SwapULong(*(uint32_t *)(data + tableIndex - 4)); if(fakeHeader == ACS_CHUNKID('A', 'C', 'S', 'E')) { ACS_LoadScriptACSE(vm, dir, lump, data, tableIndex - 8); return; } else if(fakeHeader == ACS_CHUNKID('A', 'C', 'S', 'e')) { ACS_LoadScriptACSe(vm, dir, lump, data, tableIndex - 8); return; } } // At the index there must be at least the script count and string count. // Subtracting from lumpLength instead of adding to tableIndex in case the // latter would cause an overflow. if (tableIndex > lumpLength - 8) return; rover = (int32_t *)(data + tableIndex); // Read script count. vm->numScripts = SwapLong(*rover++); // Verify that there is enough space for the given number of scripts. if (vm->numScripts * 12 > lumpAvail) return; lumpAvail -= vm->numScripts * 12; // Also verify that the string count will be inside the lump. if (tableIndex + 8 > lumpLength - (vm->numScripts * 12)) return; tableIndex += 8 + (vm->numScripts * 12); // Read scripts. vm->scripts = estructalloctag(ACSScript, vm->numScripts, PU_LEVEL); for(ACSScript *itr = vm->scripts, *end = itr + vm->numScripts; itr != end; ++itr) { itr->number = SwapLong(*rover++); itr->codeIndex = SwapLong(*rover++); itr->numArgs = SwapLong(*rover++); itr->numVars = ACS_NUM_LOCALVARS; itr->vm = vm; if(itr->number >= 1000) { itr->type = ACS_STYPE_OPEN; itr->number -= 1000; } else itr->type = ACS_STYPE_CLOSED; } // Read string count. vm->numStrings = SwapLong(*rover++); // Again, verify that there is enough space for the table first. if (vm->numStrings * 4 > lumpAvail) return; lumpAvail -= vm->numStrings * 4; // This time, just verify the end of the table is in bounds. if (tableIndex > lumpLength - (vm->numStrings * 4)) return; // Read strings. vm->strings = (uint32_t *)Z_Malloc(vm->numStrings * sizeof(uint32_t), PU_LEVEL, NULL); for(uint32_t *itr = vm->strings, *end = itr + vm->numStrings; itr != end; ++itr) { tableIndex = SwapLong(*rover++); if (tableIndex < lumpLength) *itr = ACS_LoadStringACS0(data + tableIndex, data + lumpLength); else *itr = ACS_LoadStringACS0(data + lumpLength, data + lumpLength); } // The first part of the global string table must match VM-0 for compatibility. if(vm->id == 0 && ACSVM::GlobalNumStrings < vm->numStrings) vm->addStrings(); // Read code. ACS_LoadScriptCodeACS0(vm, data, lumpLength, false); vm->loaded = true; }
// // ACS_traceScriptACS0 // static void ACS_traceScriptACS0(acs0_tracer_t *tracer, uint32_t index) { uint32_t indexNext, opSize; int32_t op; acs0_opdata_t const *opdata; for(;;) { // Read next instruction from file. op = ACS_readOpACS0(tracer, &opSize, index); // Invalid opcode terminates tracer. if(op >= ACS0_OPMAX || op < 0) { // But flag it so that a KILL gets generated by the translator. ACS_touchScriptACS0(tracer->codeTouched + index, tracer->codeTouched + index + opSize); ++tracer->vm->numCode; return; } opdata = &ACS0opdata[op]; // Calculate next index. indexNext = index + ACS_countOpSizeACS0(tracer, index, opSize, opdata); // Leaving the bounds of the lump also terminates the tracer. if(indexNext > tracer->lumpLength) return; // If already touched this instruction, stop tracing. if(ACS_touchScriptACS0(tracer->codeTouched + index, tracer->codeTouched + indexNext)) return; // Determine how many internal codes this counts for. switch(op) { case ACS0_OP_LINESPEC1_IMM: case ACS0_OP_LINESPEC2_IMM: case ACS0_OP_LINESPEC3_IMM: case ACS0_OP_LINESPEC4_IMM: case ACS0_OP_LINESPEC5_IMM: case ACS0_OP_LINESPEC1_IMM_BYTE: case ACS0_OP_LINESPEC2_IMM_BYTE: case ACS0_OP_LINESPEC3_IMM_BYTE: case ACS0_OP_LINESPEC4_IMM_BYTE: case ACS0_OP_LINESPEC5_IMM_BYTE: case ACS0_OP_GET2_IMM_BYTE: case ACS0_OP_GET3_IMM_BYTE: case ACS0_OP_GET4_IMM_BYTE: case ACS0_OP_GET5_IMM_BYTE: tracer->vm->numCode += opdata->args + 2; break; case ACS0_OP_GETARR_IMM_BYTE: tracer->vm->numCode += *(tracer->data + index + opSize) + 2; break; case ACS0_OP_ACTIVATORHEALTH: case ACS0_OP_ACTIVATORARMOR: case ACS0_OP_ACTIVATORFRAGS: case ACS0_OP_BRANCH_RETURNVOID: case ACS0_OP_PLAYERNUMBER: case ACS0_OP_ACTIVATORTID: case ACS0_OP_SIGILPIECES: tracer->vm->numCode += opdata->opdata->args + 1 + 2; // GET_IMM 0 break; case ACS0_OP_GAMETYPE_ONEFLAGCTF: tracer->vm->numCode += 2; // GET_IMM break; case ACS0_OP_GAMETYPE_SINGLEPLAYER: tracer->vm->numCode += 4; // GAMETYPE + GET_IMM + CMP_EQ break; case ACS0_OP_BRANCH_CALLDISCARD: tracer->vm->numCode += opdata->opdata->args + 1 + 1; // DROP break; case ACS0_OP_BRANCH_CASETABLE: tracer->vm->numCode += 2; // More alignment stuff. (Wow, that's a mouthfull.) tracer->vm->numCode += SwapULong(*(uint32_t *)(((uintptr_t)tracer->data + index + opSize + 3) & ~3)) * 2; break; case ACS0_OP_CALLFUNC: if(tracer->compressed) { uint8_t argc = tracer->data[index + opSize]; uint16_t func = SwapUShort(*(uint16_t *)(tracer->data + index + opSize + 1)); tracer->vm->numCode += ACS_traceFuncACS0(func, argc); } else { uint32_t argc = SwapULong(*(uint32_t *)(tracer->data + index + opSize + 0)); uint32_t func = SwapULong(*(uint32_t *)(tracer->data + index + opSize + 4)); tracer->vm->numCode += ACS_traceFuncACS0(func, argc); } break; default: // Translation to CALLFUNC. if(opdata->opdata->op == ACS_OP_CALLFUNC_IMM) { // Adds the func and argc arguments. tracer->vm->numCode += opdata->args + 1 + 2; break; } // Direct translation. #ifdef RANGECHECK // This should never happen. if(opdata->opdata->args == -1) I_Error("Unknown translation for opcode. opcode %i", (int)op); #endif tracer->vm->numCode += opdata->opdata->args + 1; break; } // Advance the index past the instruction. switch(op) { case ACS0_OP_SCRIPT_TERMINATE: case ACS0_OP_BRANCH_RETURN: case ACS0_OP_BRANCH_RETURNVOID: return; case ACS0_OP_BRANCH_IMM: ++tracer->jumpCount; index = SwapLong(*(int32_t *)(tracer->data + index + opSize)); continue; case ACS0_OP_BRANCH_NOTZERO: case ACS0_OP_BRANCH_ZERO: ++tracer->jumpCount; ACS_traceScriptACS0(tracer, SwapLong(*(int32_t *)(tracer->data + index + opSize))); break; case ACS0_OP_BRANCH_CASE: ++tracer->jumpCount; ACS_traceScriptACS0(tracer, SwapLong(*(int32_t *)(tracer->data + index + opSize + 4))); break; case ACS0_OP_BRANCH_CASETABLE: { uint32_t jumps, *rover; rover = (uint32_t *)(tracer->data + index + opSize); // And alignment again. rover = (uint32_t *)(((uintptr_t)rover + 3) & ~3); jumps = SwapULong(*rover++); tracer->jumpCount += jumps; // Trace all of the jump targets. // Start by incrementing rover to point to address. for(++rover; jumps--; rover += 2) ACS_traceScriptACS0(tracer, SwapULong(*rover)); } break; } index = indexNext; } }