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