From 6b0b0c1d9cbb081a7de310fd4d756b96b8c48b56 Mon Sep 17 00:00:00 2001 From: Fatbag Date: Thu, 26 Jul 2012 13:35:45 -0500 Subject: [PATCH] Added hitdump. It's not yet complete in the sense that it can tolerate any input file you give it, but it can correctly disassemble every HIT file in both games now. --- CMakeLists.txt | 4 +- Libraries/FileHandler/utk/utkdecode.c | 2 +- Libraries/FileHandler/xa/xadecode.c | 2 +- Tools/hitutils/CMakeLists.txt | 6 +- Tools/hitutils/Design.txt | 59 ++ Tools/hitutils/{hitasm.cpp => hitasm.c} | 2 +- Tools/hitutils/hitdump.c | 1049 +++++++++++++++++++++++ Tools/hitutils/{hitdump.cpp => hitld.c} | 12 +- Tools/hitutils/hitld.cpp | 73 -- 9 files changed, 1121 insertions(+), 88 deletions(-) create mode 100644 Tools/hitutils/Design.txt rename Tools/hitutils/{hitasm.cpp => hitasm.c} (94%) create mode 100644 Tools/hitutils/hitdump.c rename Tools/hitutils/{hitdump.cpp => hitld.c} (77%) delete mode 100644 Tools/hitutils/hitld.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d0d6ea3..e3f7bd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,11 +62,11 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) endif() # Size - set(CFLAGS_SIZE "${CFLAGS} -Os -g0 -fomit-frame-pointer -mfpmath=both -msahf -malign-double -mpc32 -ffast-math -fmerge-all-constants -funsafe-loop-optimizations -fmerge-all-constants -fsched-pressure -mstringop-strategy=rep_byte") + set(CFLAGS_SIZE "${CFLAGS} -Os -g0 -fomit-frame-pointer -mfpmath=both -msahf -malign-double -mpc32 -ffast-math -fmerge-all-constants -funsafe-loop-optimizations -fsched-pressure -mstringop-strategy=rep_byte") set(LDFLAGS_SIZE "${LDFLAGS} -s -fwhole-program") # Speed - set(CFLAGS_SPEED "${CFLAGS} -O3 -g0 -fomit-frame-pointer -mfpmath=both -msahf -malign-double -mpc32 -ffast-math -fmerge-all-constants -funsafe-loop-optimizations -fmerge-all-constants -fsched-pressure -fmodulo-sched -fmodulo-sched-allow-regmoves -fgcse-sm -fgcse-las -fsched-spec-load -fsched-spec-load-dangerous -fsched-stalled-insns=0 -fsched-stalled-insns-dep -fsched2-use-superblocks -fipa-pta -fipa-matrix-reorg -ftree-loop-linear -floop-interchange -floop-strip-mine -floop-block -fgraphite-identity -floop-parallelize-all -ftree-loop-distribution -ftree-loop-im -ftree-loop-ivcanon -fivopts -fvect-cost-model -fvariable-expansion-in-unroller -fbranch-target-load-optimize -maccumulate-outgoing-args -flto") + set(CFLAGS_SPEED "${CFLAGS} -O3 -g0 -fomit-frame-pointer -mfpmath=both -msahf -malign-double -mpc32 -ffast-math -fmerge-all-constants -funsafe-loop-optimizations -fsched-pressure -fmodulo-sched -fmodulo-sched-allow-regmoves -fgcse-sm -fgcse-las -fsched-spec-load -fsched-spec-load-dangerous -fsched-stalled-insns=0 -fsched-stalled-insns-dep -fsched2-use-superblocks -fipa-pta -fipa-matrix-reorg -ftree-loop-linear -floop-interchange -floop-strip-mine -floop-block -fgraphite-identity -floop-parallelize-all -ftree-loop-distribution -ftree-loop-im -ftree-loop-ivcanon -fivopts -fvect-cost-model -fvariable-expansion-in-unroller -fbranch-target-load-optimize -maccumulate-outgoing-args -flto") set(LDFLAGS_SPEED "${LDFLAGS} -s -fwhole-program -flto") else() # Debug diff --git a/Libraries/FileHandler/utk/utkdecode.c b/Libraries/FileHandler/utk/utkdecode.c index b807519..0dd7e9e 100644 --- a/Libraries/FileHandler/utk/utkdecode.c +++ b/Libraries/FileHandler/utk/utkdecode.c @@ -150,7 +150,7 @@ int main(int argc, char *argv[]){ printf("File \"%s\" exists.\nContinue anyway? (y/n) ", OutFile); c = getchar(); if(c != 'y' && c != 'Y'){ - printf("\nAborted."); + printf("\nAborted.\n"); return -1; } } diff --git a/Libraries/FileHandler/xa/xadecode.c b/Libraries/FileHandler/xa/xadecode.c index a26f854..5c57885 100644 --- a/Libraries/FileHandler/xa/xadecode.c +++ b/Libraries/FileHandler/xa/xadecode.c @@ -146,7 +146,7 @@ int main(int argc, char *argv[]){ printf("File \"%s\" exists.\nContinue anyway? (y/n) ", OutFile); c = getchar(); if(c != 'y' && c != 'Y'){ - printf("\nAborted."); + printf("\nAborted.\n"); return -1; } } diff --git a/Tools/hitutils/CMakeLists.txt b/Tools/hitutils/CMakeLists.txt index 97776ea..97c1029 100644 --- a/Tools/hitutils/CMakeLists.txt +++ b/Tools/hitutils/CMakeLists.txt @@ -2,15 +2,15 @@ cmake_minimum_required(VERSION 2.6) project(hitutils) set(HITDUMP_SOURCES - hitdump.cpp + hitdump.c ) set(HITASM_SOURCES - hitasm.cpp + hitasm.c ) set(HITLD_SOURCES - hitld.cpp + hitld.c ) add_executable(hitdump ${HITDUMP_SOURCES}) diff --git a/Tools/hitutils/Design.txt b/Tools/hitutils/Design.txt new file mode 100644 index 0000000..c628e39 --- /dev/null +++ b/Tools/hitutils/Design.txt @@ -0,0 +1,59 @@ +1. Dumping + +A HIT file is dumped by running this command: + +$ hitdump -o example.txt example.hit + +This will disassemble example.hit into example.txt using MakeTrax syntax. +* Instructions, arguments, registers, and globals are rewritten using the mnemonics in + example.hsm. +* The disassembly is split into BINARY[] sections and logical addresses are defined names + using the data provided by example.hsm, example.hot, example.evt, and the HIT symbol table. + All user-created constants in example.hsm that begin with "tkd_" are interpreted by + hitutils as track data. + + +2. Reassembling + +A source file is assembled into an intermediate object by running this command: + +$ hitasm -o example.o example.txt + +This will assemble example.txt into example.o according to MakeTrax syntax. +* Instructions, arguments, and registers are interpreted using the mnemonics in example.hsm. +* The logical addresses defined in example.hsm are ignored. + + +3. Relinking into the game + +Object files are relinked into the game by running this command: + +$ hitld -o example.hit example1.o example2.o ... + +This will link example1.o, example2.o, and so on into example.hit and reconstruct the +relocation tables in example.hot and example.hsm. + + +To clarify, the user cannot change the events signaled by objects without changing the +SimAntics of each of those objects. The HIT subroutines invoked by those events, however, can +be changed or swapped out quite easily. + + +Appendix A. Address labeling strategies + +In TSO, the HIT symbol table lists the logical address of each exported function, along with +the Track File ID associated with it. + +In TSO, the EVT file lists the name of each exported function, along with the Track File ID +associated with it. + +* The HIT file can be used to locate each exported function for placement in BINARY[] sections. +* In the case of tsov2.hit, the HIT and EVT files can be used together to name these exported + functions, seeing that the HSM file is not provided. + +The HOT file lists all addresses that were labelled, along with the Track File ID associated +with it, in the TrackData section. Because the HIT symbol table is not included in The Sims 1, +the TrackData section can be used instead. + +The HSM file lists the name of each labelled address, along with its logical address in the +HIT file. (This is done with a Ctrl+F for "tkd_"). \ No newline at end of file diff --git a/Tools/hitutils/hitasm.cpp b/Tools/hitutils/hitasm.c similarity index 94% rename from Tools/hitutils/hitasm.cpp rename to Tools/hitutils/hitasm.c index 0aab3ae..50dcb8b 100644 --- a/Tools/hitutils/hitasm.cpp +++ b/Tools/hitutils/hitasm.c @@ -1,6 +1,6 @@ /* hitutils - The Sims HIT (dis)assembler and linker - hitasm.cpp - Copyright (c) 2012 Niotso Project + hitasm.c - Copyright (c) 2012 Niotso Project Author(s): Fatbag Permission to use, copy, modify, and/or distribute this software for any diff --git a/Tools/hitutils/hitdump.c b/Tools/hitutils/hitdump.c new file mode 100644 index 0000000..a2ec6b4 --- /dev/null +++ b/Tools/hitutils/hitdump.c @@ -0,0 +1,1049 @@ +/* + hitutils - The Sims HIT (dis)assembler and linker + hitdump.c - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include + +#ifndef min + #define min(x,y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef max + #define max(x,y) ((x) > (y) ? (x) : (y)) +#endif + +#ifndef read_int32 + #define read_uint32(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1)) | ((x)[2]<<(8*2)) | ((x)[3]<<(8*3))) + #define read_uint16(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1))) +#endif + +enum { + hsm, hot, evt, hit, out, filecount +}; + +enum { + VERSION_TS1 = 1, VERSION_TSO +}; + +#define OPERAND_BYTES(x) (x) +#define OPERAND(x, y) ((y)<<((x)*4+4)) +#define UNIMPLEMENTED ((uint32_t)~0) + +enum operand_t { + o_byte = 1, + o_dword, + o_address, + o_variable, + o_jump +}; + +typedef struct { + const char * Name; + uint32_t Operands; +} instruction_t; + +typedef struct { + const char * Name; + uint32_t Value; +} global_t; + +static const uint8_t HITHeader[] = {'H','I','T','!',0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,'T','R','A','X'}; + +static const instruction_t Instructions[] = { + {"note", UNIMPLEMENTED}, + {"note_on", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"note_off", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"loadb", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_byte)}, + {"loadl", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"set", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"call", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"return", OPERAND_BYTES(0)}, + {"wait", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"callentrypoint", UNIMPLEMENTED}, + {"wait_samp", OPERAND_BYTES(0)}, + {"end", OPERAND_BYTES(0)}, + {"jump", OPERAND_BYTES(1) | OPERAND(0, o_jump)}, + {"test", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"nop", OPERAND_BYTES(0)}, + {"add", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"sub", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"div", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"mul", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"cmp", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"less", UNIMPLEMENTED}, + {"greater", UNIMPLEMENTED}, + {"not", UNIMPLEMENTED}, + {"rand", OPERAND_BYTES(3) | OPERAND(0, o_variable) | OPERAND(1, o_variable) | OPERAND(2, o_variable)}, + {"abs", UNIMPLEMENTED}, + {"limit", UNIMPLEMENTED}, + {"error", UNIMPLEMENTED}, + {"assert", UNIMPLEMENTED}, + {"add_to_group", UNIMPLEMENTED}, + {"remove_from_group", UNIMPLEMENTED}, + {"get_var", UNIMPLEMENTED}, + {"loop", OPERAND_BYTES(0)}, + {"set_loop", OPERAND_BYTES(0)}, + {"callback", UNIMPLEMENTED}, + {"smart_add", UNIMPLEMENTED}, + {"smart_remove", UNIMPLEMENTED}, + {"smart_removeall", UNIMPLEMENTED}, + {"smart_setcrit", UNIMPLEMENTED}, + {"smart_choose", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"and", UNIMPLEMENTED}, + {"nand", UNIMPLEMENTED}, + {"or", UNIMPLEMENTED}, + {"nor", UNIMPLEMENTED}, + {"xor", UNIMPLEMENTED}, + {"max", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"min", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"inc", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"dec", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"printreg", UNIMPLEMENTED}, + {"play_trk", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"kill_trk", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"push", UNIMPLEMENTED}, + {"push_mask", UNIMPLEMENTED}, + {"push_vars", UNIMPLEMENTED}, + {"call_mask", UNIMPLEMENTED}, + {"call_push", UNIMPLEMENTED}, + {"pop", UNIMPLEMENTED}, + {"test1", OPERAND_BYTES(0)}, + {"test2", OPERAND_BYTES(0)}, + {"test3", OPERAND_BYTES(0)}, + {"test4", OPERAND_BYTES(0)}, + {"ifeq", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"ifne", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"ifgt", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"iflt", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"ifge", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"ifle", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"smart_setlist", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"seqgroup_kill", OPERAND_BYTES(1) | OPERAND(0, o_byte)}, + {"seqgroup_wait", UNIMPLEMENTED}, + {"seqgroup_return", OPERAND_BYTES(1) | OPERAND(0, o_byte)}, + {"getsrcdatafield", OPERAND_BYTES(3) | OPERAND(0, o_variable) | OPERAND(1, o_variable) | OPERAND(2, o_variable)}, + {"seqgroup_trkid", OPERAND_BYTES(2) | OPERAND(0, o_byte) | OPERAND(1, o_byte)}, + {"setll", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"setlt", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"settl", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waiteq", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitne", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitgt", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitlt", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitge", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitle", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"duck", OPERAND_BYTES(0)}, + {"unduck", OPERAND_BYTES(0)}, + {"testx", UNIMPLEMENTED}, + {"setlg", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"setgl", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"throw", UNIMPLEMENTED}, + {"setsrcdatafield", OPERAND_BYTES(3) | OPERAND(0, o_variable) | OPERAND(1, o_variable) | OPERAND(2, o_variable)}, + {"stop_trk", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"setchanreg", UNIMPLEMENTED}, + {"play_note", UNIMPLEMENTED}, + {"stop_note", UNIMPLEMENTED}, + {"kill_note", UNIMPLEMENTED}, + {"smart_index", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"note_on_loop", OPERAND_BYTES(1) | OPERAND(0, o_variable)} +}; + +static const char *Registers[] = { + "arg0", + "arg1", + "arg2", + "arg3", + "arg4", + "v1", + "v2", + "v3", + "v4", + "v5", + "v6", + "v7", + "v8", + "h1", + "h2", + "h3", + "h4" +}; + +#define TSOGlobalCount 66 +static const global_t TSOGlobals[] = { + {"argstype", 0x10}, + {"trackdatasource", 0x11}, + {"patch", 0x12}, + {"priority", 0x13}, + {"vol", 0x14}, + {"extvol", 0x15}, + {"pan", 0x16}, + {"pitch", 0x17}, + {"paused", 0x18}, + {"fxtype", 0x19}, + {"fxlevel", 0x1a}, + {"duckpri", 0x1b}, + {"Is3d", 0x1c}, + {"IsHeadRelative", 0x1d}, + {"MinDistance", 0x1e}, + {"MaxDistance", 0x1f}, + {"X", 0x20}, + {"Y", 0x21}, + {"Z", 0x22}, + {"attack", 0x23}, + {"decay", 0x24}, + {"IsStreamed", 0x25}, + {"bufsizemult", 0x26}, + {"fade_dest", 0x27}, + {"fade_var", 0x28}, + {"fade_speed", 0x29}, + {"fade_on", 0x2a}, + {"Preload", 0x2b}, + {"isplaying", 0x2c}, + {"whattodowithupdate", 0x2d}, + {"tempo", 0x2e}, + {"target", 0x2f}, + {"ctrlgroup", 0x30}, + {"interrupt", 0x31}, + {"ispositioned", 0x32}, + {"AppObjectId", 0x34}, + {"callbackarg", 0x35}, + {"pitchrandmin", 0x36}, + {"pitchrandmax", 0x37}, + {"spl", 0x38}, + {"sem", 0x39}, + {"starttrackid", 0x3a}, + {"endtrackid", 0x3b}, + {"startdelay", 0x3c}, + {"fadeinspeed", 0x3d}, + {"fadeoutspeed", 0x3e}, + {"hitlist", 0x3f}, + {"SimSpeed", 0x64}, + {"test_g1", 0x65}, + {"test_g2", 0x66}, + {"test_g3", 0x67}, + {"test_g4", 0x68}, + {"test_g5", 0x69}, + {"test_g6", 0x6a}, + {"test_g7", 0x6b}, + {"test_g8", 0x6c}, + {"test_g9", 0x6d}, + {"main_songnum", 0x6e}, + {"main_musichitlistid", 0x6f}, + {"campfire_nexttrack", 0x70}, + {"campfire_busy", 0x71}, + {"main_duckpri", 0x7b}, + {"main_vol", 0x7c}, + {"main_fxtype", 0x7d}, + {"main_fxlevel", 0x7e}, + {"main_pause", 0x7f} +}; + +#define TS1GlobalCount 71 +static const global_t TS1Globals[] = { + {"priority", 0x11}, + {"vol", 0x12}, + {"extvol", 0x13}, + {"pan", 0x14}, + {"pitch", 0x15}, + {"paused", 0x16}, + {"fxtype", 0x17}, + {"fxlevel", 0x18}, + {"duckpri", 0x19}, + {"Is3d", 0x1a}, + {"IsHeadRelative", 0x1b}, + {"MinDistance", 0x1c}, + {"MaxDistance", 0x1d}, + {"X", 0x1e}, + {"Y", 0x1f}, + {"Z", 0x20}, + {"filter_type", 0x21}, + {"filter_cutoff", 0x22}, + {"filter_level", 0x23}, + {"attack", 0x24}, + {"decay", 0x25}, + {"IsStreamed", 0x26}, + {"BufSizeMult", 0x27}, + {"fade_dest", 0x28}, + {"fade_var", 0x29}, + {"fade_speed", 0x2a}, + {"Preload", 0x2b}, + {"IsLooped", 0x2c}, + {"fade_on", 0x2d}, + {"isplaying", 0x2e}, + {"source", 0x2f}, + {"patch", 0x32}, + {"WhatToDoWithUpdate", 0x33}, + {"tempo", 0x34}, + {"target", 0x35}, + {"mutegroup", 0x36}, + {"interrupt", 0x37}, + {"IsPositioned", 0x38}, + {"Spl", 0x39}, + {"MultipleInstances", 0x3a}, + {"AssociatedTrack1", 0x3b}, + {"AssociatedTrack2", 0x3c}, + {"AssociatedTrack3", 0x3d}, + {"AssociatedTrack4", 0x3e}, + {"AssociatedTrack5", 0x3f}, + {"AssociatedTrack6", 0x40}, + {"AssociatedTrack7", 0x41}, + {"AssociatedTrack8", 0x42}, + {"SimSpeed", 0x64}, + {"test_g1", 0x65}, + {"test_g2", 0x66}, + {"test_g3", 0x67}, + {"test_g4", 0x68}, + {"test_g5", 0x69}, + {"test_g6", 0x6a}, + {"test_g7", 0x6b}, + {"test_g8", 0x6c}, + {"test_g9", 0x6d}, + {"main_songnum", 0x6e}, + {"main_musichitlistid", 0x6f}, + {"campfire_nexttrack", 0x70}, + {"campfire_busy", 0x71}, + {"main_duckpri", 0x7b}, + {"main_vol", 0x7c}, + {"main_fxtype", 0x7d}, + {"main_fxlevel", 0x7e}, + {"main_pause", 0x7f}, +}; + +static __inline const char * find_global(uint32_t x, const global_t * Globals, size_t GlobalCount){ + size_t i; + for(i=0; iCount == List->Size) + List->Entries = realloc(List->Entries, (List->Size <<= 1) * sizeof(address_t)); + return memset(List->Entries + List->Count++, 0, sizeof(address_t)); +} + +static __inline address_t * find_address_by_track_id(addresslist_t * List, uint32_t TrackID){ + unsigned i; + for(i=0; iCount; i++){ + if(List->Entries[i].TrackID == TrackID) + return List->Entries + i; + } + return NULL; +} + +static __inline address_t * find_address_by_sound_id(addresslist_t * List, uint32_t SoundID){ + unsigned i; + for(i=0; iCount; i++){ + if(List->Entries[i].SoundID == SoundID) + return List->Entries + i; + } + return NULL; +} + +static __inline address_t * find_address_by_logical_address(addresslist_t * List, uint32_t LogicalAddress){ + unsigned i; + for(i=0; iCount; i++){ + if(List->Entries[i].LogicalAddress == LogicalAddress) + return List->Entries + i; + } + return NULL; +} + +static __inline address_t * find_address_by_name(addresslist_t * List, const char * Name){ + unsigned i; + for(i=0; iCount; i++){ + if(List->Entries[i].Name && !strcmp(List->Entries[i].Name, Name)) + return List->Entries + i; + } + return NULL; +} + +static __inline void read_hit_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList, uint32_t * SymbolTable){ + uint8_t * Start = Data, * TableData; + unsigned i, count = 0; + + if(Size < 32) return; + Data += 16; + Size -= 16; + + /* Find the table start */ + while(memcmp(Data, "ENTP", 4)){ + if(Size < 17) return; + Data++; Size--; + } + + TableData = Data; + Data += 4; + Size -= 4; + + /* Find the table end */ + while(memcmp(Data, "EENT", 4)){ + if(Size < 12) return; + Data+=8; Size-=8; + count++; + } + + *SymbolTable = TableData - Start; + + if(count == 0) return; + TableData += 4; + + for(i=0; iExported = 1; + Address->TrackID = read_uint32(TableData); TableData+=4; + Address->LogicalAddress = read_uint32(TableData); TableData+=4; + } +} + +static __inline void read_evt_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + if(Size < 13) return; + + while(1){ + uint8_t * Name = Data, * TrackIDPos; + address_t * Address; + uint32_t TrackID; + + Data++; Size--; + + /* End of first field: address name */ + while(*Data != ','){ + if(Size < 13) return; + Data++; Size--; + } + *Data = '\0'; + Data++; Size--; + + /* End of second field: unneeded */ + while(*Data != ','){ + if(Size < 11) return; + Data++; Size--; + } + Data++; Size--; + TrackIDPos = Data; + + /* End of third field: Track ID */ + while(*Data != ','){ + if(Size < 9) return; + Data++; Size--; + } + *Data = '\0'; + Data++; Size--; + + TrackID = atoi((char*)TrackIDPos); + Address = find_address_by_track_id(AddressList, TrackID); + if(!Address){ + Address = add_address(AddressList); + Address->Exported = 1; + Address->TrackID = TrackID; + } + Address->Name = (char*)Name; + + while(*Data != '\n'){ + if(Size < 15) return; + Data++; Size--; + } + Data++; Size--; + } +} + +static __inline void read_hsm_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + if(Size < 24) return; + + while(1){ + uint8_t * Name, * IDPos; + address_t * Address; + uint32_t SoundID, LogicalAddress; + + /* Find the next constant that begins with "tkd_" */ + while(memcmp(Data, "\ntkd_", 5)){ + if(Size < 25) return; + Data++; Size--; + } + Name = Data += 5; + Size -= 5; + + /* End of tkd constant name */ + while(*Data != ' '){ + if(Size < 19) return; + Data++; Size--; + } + *Data = '\0'; + Data++; Size--; + IDPos = Data; + + /* End of tkd constant value */ + while(*Data != ' '){ + if(Size < 17) return; + Data++; Size--; + } + *Data = '\0'; + Data++; Size--; + SoundID = atoi((char*)IDPos); + + /* End of address constant name */ + while(*Data != ' '){ + if(Size < 9) return; + Data++; Size--; + } + Data++; Size--; + IDPos = Data; + + /* End of address constant value */ + while(*Data != ' '){ + if(Size < 7) return; + Data++; Size--; + } + *Data = '\0'; + Data++; Data--; + LogicalAddress = atoi((char*)IDPos); + + Address = find_address_by_logical_address(AddressList, LogicalAddress); + if(!Address){ + Address = find_address_by_name(AddressList, (char*)Name); + if(!Address){ + Address = add_address(AddressList); + Address->Name = (char*)Name; + } + Address->LogicalAddress = LogicalAddress; + } else Address->Name = (char*)Name; + Address->SoundID = SoundID; + + while(*Data != '\n'){ + if(Size < 25) return; + Data++; Size--; + } + } +} + +static __inline void read_hot_trackdata(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + if(Size < 19) return; + + while(memcmp(Data, "[TrackData]", 11)){ + if(Size < 20) return; + Data++; Size--; + } + + Data += 12; + Size -= 12; + if(*Data == '\n'){ + Data++; Size--; + } + + while(1){ + uint8_t * IDPos = Data; + address_t * Address; + uint32_t SoundID, LogicalAddress; + + /* End of key: Sound ID */ + while(*Data != '='){ + if(Size < 5) return; + Data++; Size--; + } + *Data = '\0'; + SoundID = strtol((char*)IDPos, NULL, 0); + Data++; Size--; + IDPos = Data; + + while(*Data != '\n'){ + if(Size < 2) return; + Data++; Size--; + } + *Data = '\0'; + LogicalAddress = strtol((char*)IDPos, NULL, 0); + Data++; Size--; + + Address = find_address_by_logical_address(AddressList, LogicalAddress); + if(!Address){ + Address = find_address_by_sound_id(AddressList, SoundID); + if(!Address){ + Address = add_address(AddressList); + Address->SoundID = SoundID; + } + Address->LogicalAddress = LogicalAddress; + } else Address->SoundID = SoundID; + + if(Size < 8) return; + while(*Data == '\r' || *Data == '\n' || *Data == ' ' || *Data == '\t'){ + if(Size < 8) return; + Data++; Size--; + } + + if(*Data == '[') return; + } +} + +static __inline void read_hot_track(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + if(Size < 28) return; + + while(memcmp(Data, "[Track]", 7)){ + if(Size < 29) return; + Data++; Size--; + } + + Data += 8; + Size -= 8; + if(*Data == '\n'){ + Data++; Size--; + } + + while(1){ + uint8_t * IDPos = Data, * Name; + address_t * Address; + uint32_t TrackID; + + /* End of key: Track ID */ + while(*Data != '='){ + if(Size < 20) return; + Data++; Size--; + } + *Data = '\0'; + TrackID = strtol((char*)IDPos, NULL, 0); + Data++; Size--; + + /* End of first field: Unknown */ + while(*Data != ','){ + if(Size < 18) return; + Data++; Size--; + } + Data++; Size--; + Name = Data; + + /* End of second field: Name */ + while(*Data != ','){ + if(Size < 16) return; + Data++; Size--; + } + *Data = '\0'; + + Address = find_address_by_name(AddressList, (char*)Name); + if(!Address){ + Address = find_address_by_track_id(AddressList, TrackID); + if(!Address){ + Address = add_address(AddressList); + Address->TrackID = TrackID; + } + Address->Name = (char*)Name; + } else Address->TrackID = TrackID; + Address->Exported = 1; + + if(Size < 36) return; + while(*Data == '\r' || *Data == '\n' || *Data == ' ' || *Data == '\t'){ + if(Size < 22) return; + Data++; Size--; + } + + if(*Data == '[') return; + } +} + +static __inline void read_hot_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + read_hot_trackdata(Data, Size, AddressList); + read_hot_track(Data, Size, AddressList); +} + +int main(int argc, char *argv[]){ + unsigned i, addr; + int SimsVersion = 0; + int overwrite = 0; + int ShowAddresses = 0; + int length; + char *basename; + char *path[filecount] = {NULL}; + uint8_t *data[filecount-1] = {NULL}; + size_t filesize[filecount-1]; + FILE * hFile; + const global_t * Globals; + size_t GlobalCount; + uint32_t SymbolTable = 0; + uint32_t BaseSoundID = 0, BaseSoundIDSet = 0; + + /* collected data */ + addresslist_t AddressList; + + /**** + ** Parse the command-line arguments + */ + + if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ + printf("Usage: hitdump [-ts1|-tso] [-f] [-a] [-o outfile.txt] [-hsm infile.hsm]\n" + " [-hot infile.hot] [-evt infile.evt] infile.hit\n" + "Disassemble a HIT binary.\n" + "\n" + "The HSM, HOT, and EVT files are not strictly necessary but\n" + "each help in their own way to provide labels for addresses.\n" + "Use -f to force overwriting without confirmation.\n" + "Use -a to show addresses (verbose).\n" + "\n" + "Report bugs to .\n" + "hitutils is maintained by the Niotso project.\n" + "Home page: \n"); + return 0; + } + + for(i=1; i<(unsigned)argc-1; i++){ + if(!strcmp(argv[i], "-ts1")) SimsVersion = VERSION_TS1; + else if(!strcmp(argv[i], "-tso")) SimsVersion = VERSION_TSO; + else if(!strcmp(argv[i], "-f")) overwrite = 1; + else if(!strcmp(argv[i], "-a")) ShowAddresses = 1; + else if(i != (unsigned)argc-2){ + if(!strcmp(argv[i], "-out")) path[out] = argv[++i]; + else if(!strcmp(argv[i], "-hsm")) path[hsm] = argv[++i]; + else if(!strcmp(argv[i], "-hot")) path[hot] = argv[++i]; + else if(!strcmp(argv[i], "-evt")) path[evt] = argv[++i]; + else break; + } + else break; + } + + if(!SimsVersion){ + fprintf(stderr, "%sSims version not specified. (Use -ts1 or -tso.)\n", "hitdump: Error: "); + return -1; + } + + if(SimsVersion == VERSION_TS1){ + Globals = TS1Globals; + GlobalCount = TS1GlobalCount; + }else{ + Globals = TSOGlobals; + GlobalCount = TSOGlobalCount; + } + + path[hit] = argv[i]; + length = strlen(path[hit]); + if(path[out] == NULL){ + path[out] = malloc(max(length+1, 5)); + strcpy(path[out], path[hit]); + strcpy(path[out] + max(length-4, 0), ".txt"); + } + length = max(length+1-4, 1); + basename = malloc(length); + memcpy(basename, path[hit], length-1); + basename[length-1] = '\0'; + + /**** + ** Read all of the requested files + */ + + for(i=0; iName) fprintf(hFile, "%s", Address->Name); + else fprintf(hFile, "%u", LogicalAddress); + } + + if(addr-4 != SymbolTable) + fprintf(hFile, "\r\n"); + fprintf(hFile, "\r\n" + "\t69\r\n" + "\t69\r\n" + "\t78\r\n" + "\t84"); + + if(addr+4 == filesize[hit]) + break; + fprintf(hFile, "\r\n]\r\n\r\nBINARY\r\n["); + + addr += 4; + SymbolTable = 0; + HadSymbolTable++; + } + + Address = find_address_by_logical_address(&AddressList, addr); + if(Address){ + if(!HadSymbolTable && addr != 16 && Address->Exported) + fprintf(hFile, "\r\n]\r\n\r\nBINARY\r\n["); + if(Address->Name) + fprintf(hFile, "\r\n%s", Address->Name); + } + + opcode = data[hit][addr]; + if(opcode == 0 || opcode > 96){ + fprintf(stderr, "%sIllegal opcode 0x%02X at address 0x%08X.\n", "hitdump: Error: ", opcode, addr); + return -1; + } + + instruction = Instructions + opcode - 1; + operands = instruction->Operands; + if(operands == UNIMPLEMENTED){ + fprintf(stderr, "%sUnimplemented instruction '%s' at address 0x%08X.\n", "hitdump: Error: ", + instruction->Name, addr); + return -1; + } + + addr++; + + if(filesize[hit] - addr < (operands & 15)){ + fprintf(stderr, "%sInsufficient operand bytes for '%s' instruction at address 0x%08X (%u of %u supplied).\n", + "hitdump: Error: ", instruction->Name, addr, filesize[hit] - addr, instruction->Operands); + return -1; + } + + fprintf(hFile, "\r\n\t\t%s", instruction->Name); + for(i=0; (operands >>= 4) != 0; i++){ + int type = operands & 15; + const char *position[] = {"first","second","third","fourth"}; + if(type == o_byte){ + fprintf(hFile, " #%u", data[hit][addr]); + addr += 1; + }else if(type == o_dword){ + fprintf(hFile, " #%u", read_uint32(data[hit]+addr)); + addr += 4; + }else if(type == o_address){ + int LogicalAddress = read_uint32(data[hit]+addr); + + Address = find_address_by_logical_address(&AddressList, LogicalAddress); + if(Address && Address->Name) fprintf(hFile, " #%s", Address->Name); + else fprintf(hFile, " #%u", LogicalAddress); + addr += 4; + }else if(type == o_variable){ + int x = data[hit][addr]; + if(x > 16){ + const char * Global = find_global(x, Globals, GlobalCount); + if(Global == NULL){ + fprintf(stderr, "%sInvalid %s operand 0x%02X for '%s' instruction at address 0x%08X (expected %s).\n", + "hitdump: Error: ", position[i], x, instruction->Name, addr, "argument, register, or global"); + return -1; + } + fprintf(hFile, " %s", Global); + } else fprintf(hFile, " %s", Registers[x]); + addr += 1; + }else if(type == o_jump){ + unsigned x = 0; + + if(filesize[hit]-addr >= 4) + x = read_uint32(data[hit]+addr); + else if(data[hit][addr] != 0x05 && data[hit][addr] != 0x06){ + fprintf(stderr, + "%sInsufficient operand bytes for '%s' instruction at address 0x%08X (%u of %u supplied).\n", + "hitdump: Error: ", instruction->Name, addr, filesize[hit] - addr, 4); + return -1; + } + + if(x >= 16 && x < filesize[hit]){ + Address = find_address_by_logical_address(&AddressList, x); + if(Address && Address->Name) fprintf(hFile, " #%s", Address->Name); + else fprintf(hFile, " #%u", x); + addr += 4; + }else{ + x = data[hit][addr]; + if(x > 16){ + const char * Global = find_global(x, Globals, GlobalCount); + if(Global == NULL){ + fprintf(stderr, + "%sInvalid %s operand 0x%02X for '%s' instruction at address 0x%08X (expected %s).\n", + "hitdump: Error: ", position[i], x, instruction->Name, addr, "argument, register, or global"); + return -1; + } + fprintf(hFile, " %s", Global); + } else fprintf(hFile, " %s", Registers[x]); + addr += (data[hit][addr] != 0x05 && data[hit][addr] != 0x06) ? 4 : 1; + } + } + } + } + + fprintf(hFile, "\r\n]\r\n\r\n"); + + fclose(hFile); + + return 0; +} \ No newline at end of file diff --git a/Tools/hitutils/hitdump.cpp b/Tools/hitutils/hitld.c similarity index 77% rename from Tools/hitutils/hitdump.cpp rename to Tools/hitutils/hitld.c index 7326475..f899cad 100644 --- a/Tools/hitutils/hitdump.cpp +++ b/Tools/hitutils/hitld.c @@ -1,6 +1,6 @@ /* hitutils - The Sims HIT (dis)assembler and linker - hitdump.cpp - Copyright (c) 2012 Niotso Project + hitld.c - Copyright (c) 2012 Niotso Project Author(s): Fatbag Permission to use, copy, modify, and/or distribute this software for any @@ -19,12 +19,10 @@ #include int main(){ - printf("Usage: hitdump [-f] [-o outfile.txt] [-hsm outfile.hsm]\n" - " [-hot outfile.hot] infile.hit\n" - "Disassemble a HIT binary.\n" - "\n" - "The HSM and HOT files contain necessary information and are\n" - "required as inputs.\n" + printf("Usage: hitld [-f] [-hsm infile.hsm] [-hot infile.hot]\n" + " outfile.hit INFILES\n" + "Link object files produced by hitasm into a HIT binary, and\n" + "relink the game's HSM and HOT files.\n" "Use -f to force overwriting without confirmation.\n" "\n" "Report bugs to .\n" diff --git a/Tools/hitutils/hitld.cpp b/Tools/hitutils/hitld.cpp deleted file mode 100644 index 522571b..0000000 --- a/Tools/hitutils/hitld.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - hitutils - The Sims HIT (dis)assembler and linker - hitld.cpp - Copyright (c) 2012 Niotso Project - Author(s): Fatbag - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include -#include -#include - -int main(int argc, char *argv[]){ - unsigned objectcount; - int arg; - - /**** - ** Parameter extraction - */ - - if(argc < 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ - printf("Usage: hitld [-f] [-hsm infile.hsm] [-hot infile.hot]\n" - " outfile.hit INFILES\n" - "Link object files produced by hitasm into a HIT binary, and\n" - "relink the game's HSM and HOT files.\n" - "Use -f to force overwriting without confirmation.\n" - "\n" - "Report bugs to .\n" - "hitutils is maintained by the Niotso project.\n" - "Home page: \n"); - return 0; - } - - const char * hitfile; - char * hsmfile = NULL, * hotfile = NULL; - bool force = false; - - for(arg=1; arg=1 - - for(int i=0, length = strlen(hitfile); i<2; i++){ - char *& string = (i==0) ? hsmfile : hotfile; - if(!string){ - string = (char*) malloc(length+1); - strcpy(string, hitfile); - for(int j=1; j<=3 && j<=length; j++){ - const char * ext = "hsmhot"; - string[length-j] = ext[3*i + 3-j]; - } - } - } - - printf("Force: %s\nHSM file: %s\nHOT file: %s\nHIT file: %s\nObject count: %u", - force ? "yes" : "no", hsmfile, hotfile, hitfile, objectcount); - return 0; -} \ No newline at end of file