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