iff2html working

This commit is contained in:
Jip 2024-05-06 19:40:19 +02:00
parent deaf3327e0
commit 4cd716e94d
89 changed files with 7711 additions and 3 deletions

View file

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 2.6)
project(hitutils)
set(HITDUMP_SOURCES
hitdump.c
)
set(HITASM_SOURCES
hitasm.c
)
set(HITLD_SOURCES
hitld.c
)
add_executable(hitdump ${HITDUMP_SOURCES})
add_executable(hitasm ${HITASM_SOURCES})
add_executable(hitld ${HITLD_SOURCES})

View file

@ -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_").

Binary file not shown.

View file

@ -0,0 +1,120 @@
ELF Header:
* e_ident:
* Elf32_Word EI_MAG0: (7F 45 4C 46)
* unsigned char EI_CLASS: ELFCLASS32 (01)
* unsigned char EI_DATA: ELFDATA2LSB (01)
* unsigned char EI_VERSION: EV_CURRENT (01)
* unsigned char EI_PAD[9]: (00 00 00 00 00 00 00 00 00)
* Elf32_Half e_type: ET_REL (01 00)
* Elf32_Half e_machine: EM_NONE (00 00)
* Elf32_Word e_version: EV_CURRENT (01 00 00 00)
* Elf32_Addr e_entry: 0 (00 00 00 00)
* Elf32_Off e_phoff: 0 (00 00 00 00)
* Elf32_Off e_shoff: 64 (40 00 00 00)
* Elf32_Word e_flags: 0x00000001 (01 00 00 00)
* Elf32_Half e_ehsize: 52 (34 00)
* Elf32_Half e_phentsize: 0 (00 00)
* Elf32_Half e_phnum: 0 (00 00)
* Elf32_Half e_shentsize: 40 (28 00)
* Elf32_Half e_shnum: 6 (06 00)
* Elf32_Half e_shstrndx: 2 (02 00)
* unsigned char padding[12] = (00 00 00 00 00 00 00 00 00 00 00 00)
Section headers
0 (""): All 0s
1 (".text"):
* Elf32_Word sh_name: 1 (01 00 00 00)
* Elf32_Word sh_type: SHT_PROGBITS (01 00 00 00)
* Elf32_Word sh_flags: SHF_ALLOC | SHF_EXECINSTR (06 00 00 00)
* Elf32_Addr sh_addr: 0 (00 00 00 00)
* Elf32_Off sh_offset: (?? ?? ?? ??)
* Elf32_Word sh_size: (?? ?? ?? ??)
* Elf32_Word sh_link: 0 (00 00 00 00)
* Elf32_Word sh_info: 0 (00 00 00 00)
* Elf32_Word sh_addralign: 1 (01 00 00 00)
* Elf32_Word sh_entsize: 0 (00 00 00 00)
2 (".shstrtab"):
* Elf32_Word sh_name: 7 (07 00 00 00)
* Elf32_Word sh_type: SHT_STRTAB (03 00 00 00)
* Elf32_Word sh_flags: 0 (00 00 00 00)
* Elf32_Addr sh_addr: 0 (00 00 00 00)
* Elf32_Off sh_offset: (?? ?? ?? ??)
* Elf32_Word sh_size: (?? ?? ?? ??)
* Elf32_Word sh_link: 0 (00 00 00 00)
* Elf32_Word sh_info: 0 (00 00 00 00)
* Elf32_Word sh_addralign: 1 (01 00 00 00)
* Elf32_Word sh_entsize: 0 (00 00 00 00)
3 (".symtab"):
* Elf32_Word sh_name: 17 (11 00 00 00)
* Elf32_Word sh_type: SHT_SYMTAB (02 00 00 00)
* Elf32_Word sh_flags: 0 (00 00 00 00)
* Elf32_Addr sh_addr: 0 (00 00 00 00)
* Elf32_Off sh_offset: (?? ?? ?? ??)
* Elf32_Word sh_size: (?? ?? ?? ??)
* Elf32_Word sh_link: 4 (04 00 00 00)
* Elf32_Word sh_info: 5 (05 00 00 00)
* Elf32_Word sh_addralign: 1 (01 00 00 00)
* Elf32_Word sh_entsize: 16 (10 00 00 00)
4 (".strtab"):
* Elf32_Word sh_name: 25 (19 00 00 00)
* Elf32_Word sh_type: SHT_STRTAB (03 00 00 00)
* Elf32_Word sh_flags: 0 (00 00 00 00)
* Elf32_Addr sh_addr: 0 (00 00 00 00)
* Elf32_Off sh_offset: (?? ?? ?? ??)
* Elf32_Word sh_size: (?? ?? ?? ??)
* Elf32_Word sh_link: 0 (00 00 00 00)
* Elf32_Word sh_info: 0 (00 00 00 00)
* Elf32_Word sh_addralign: 1 (01 00 00 00)
* Elf32_Word sh_entsize: 0 (00 00 00 00)
5 (".rel.text"):
* Elf32_Word sh_name: 33 (21 00 00 00)
* Elf32_Word sh_type: SHT_REL (09 00 00 00)
* Elf32_Word sh_flags: 0 (00 00 00 00)
* Elf32_Addr sh_addr: 0 (00 00 00 00)
* Elf32_Off sh_offset: (?? ?? ?? ??)
* Elf32_Word sh_size: (?? ?? ?? ??)
* Elf32_Word sh_link: 3 (03 00 00 00)
* Elf32_Word sh_info: 1 (01 00 00 00)
* Elf32_Word sh_addralign: 1 (01 00 00 00)
* Elf32_Word sh_entsize: 8 (08 00 00 00)
Symbol format: (4+4+4+1+1+2= 16 bytes)
Undefined symbol:
* Elf32_Word st_name: 0 (00 00 00 00)
* Elf32_Addr st_value: 0 (00 00 00 00)
* Elf32_Word st_size: 0 (00 00 00 00)
* unsigned char st_info: 0 (00)
* unsigned char st_other: 0 (00)
* Elf32_Half st_shndx: 0 (00 00)
Source file symbol:
* Elf32_Word st_name: 1 (01 00 00 00)
* Elf32_Addr st_value: 0 (00 00 00 00)
* Elf32_Word st_size: 0 (00 00 00 00)
* unsigned char st_info: ELF32_ST_INFO(STB_LOCAL, STT_FILE) (04)
* unsigned char st_other: 0 (00)
* Elf32_Half st_shndx: SHN_ABS (f1 ff)
Text section symbol:
* Elf32_Word st_name: 0 (00 00 00 00)
* Elf32_Addr st_value: 0 (00 00 00 00)
* Elf32_Word st_size: 0 (00 00 00 00)
* unsigned char st_info: ELF32_ST_INFO(STB_LOCAL, STT_SECTION) (03)
* unsigned char st_other: 0 (00)
* Elf32_Half st_shndx: 1 (01 00)
Undefined symbol:
* Elf32_Word st_name: (?? ?? ?? ??)
* Elf32_Addr st_value: 0 (00 00 00 00)
* Elf32_Word st_size: 0 (00 00 00 00)
* unsigned char st_info: ELF32_ST_INFO(STB_GLOBAL, STT_FUNC) (12)
* unsigned char st_other: 0 (00)
* Elf32_Half st_shndx: 0 (00 00)
Defined symbol:
* Elf32_Word st_name: (?? ?? ?? ??)
* Elf32_Addr st_value: (?? ?? ?? ??)
* Elf32_Word st_size: 0 (00 00 00 00)
* unsigned char st_info: ELF32_ST_INFO(STB_GLOBAL, STT_FUNC) (12)
* unsigned char st_other: 0 (00)
* Elf32_Half st_shndx: 1 (01 00)
Relocation format: (4+4=8)
* Elf32_Addr r_offset: (?? ?? ?? ??)
* Elf32_Word r_info: ELF32_R_INFO(?, R_386_PC32) (?? ?? ?? ??)

View file

@ -0,0 +1,602 @@
/*
hitutils - The Sims HIT (dis)assembler and linker
hitasm.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#define HITASM_HEADERS
#include "hitutils.h"
static uint8_t ObjectHeader[] = {
0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00,
0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00
};
enum {
txt, out, filecount
};
typedef struct {
uint8_t * Data;
size_t Position;
size_t Size;
char * Name;
} ByteWriterContext;
static void bw_expand(ByteWriterContext *bwc){
void * ptr;
if(bwc->Size > SIZE_MAX/2 || !(ptr = realloc(bwc->Data, bwc->Size<<=1)))
Shutdown_M("%sCould not allocate memory for %s section.\n", "hitasm: Error: ", bwc->Name);
bwc->Data = ptr;
}
static void bw_write32(ByteWriterContext *bwc, uint32_t value){
if(bwc->Size-bwc->Position < 4)
bw_expand(bwc);
write_uint32(bwc->Data+bwc->Position, value);
bwc->Position += 4;
}
static void bw_write8(ByteWriterContext *bwc, uint8_t value){
if(bwc->Size-bwc->Position < 1)
bw_expand(bwc);
bwc->Data[bwc->Position] = value;
bwc->Position += 1;
}
static void bw_write_memory(ByteWriterContext *bwc, const uint8_t *ptr, size_t size){
while(bwc->Size-bwc->Position < size)
bw_expand(bwc);
do bwc->Data[bwc->Position++] = *ptr++;
while(--size);
}
static void bw_write_string(ByteWriterContext *bwc, const char *string){
do bw_write8(bwc, *string);
while(*string++);
}
typedef struct {
char * Token;
size_t Line, NextLine;
size_t Col, NextCol;
int GaveLastToken;
ByteReaderContext brc;
} ParserContext;
enum {
TK_CROSSLINES = 1,
CN_LABELONLY = 1
};
enum Sections {
Text, SymbolTable, StringTable, RelocationTable, SectionCount
};
static FILE *hFile = NULL;
static char *path[filecount] = {NULL};
static uint8_t *data[filecount] = {NULL};
static ByteWriterContext Section[] = {
{0,0,1024,"text"},
{0,0,1024,"symtab"},
{0,0,1024,"strtab"},
{0,0,1024,".rel.text"}
};
static uint8_t * add_symbol(const char * Name){
bw_write32(&Section[SymbolTable], Section[StringTable].Position);
bw_write32(&Section[SymbolTable], 0);
bw_write32(&Section[SymbolTable], 0);
bw_write32(&Section[SymbolTable], 18);
bw_write_string(&Section[StringTable], Name);
return Section[SymbolTable].Data + Section[SymbolTable].Position - 16;
}
static uint8_t * find_symbol_by_name(const char * Name, uint32_t * SymbolIndex){
uint32_t p;
for(p=48; p<Section[SymbolTable].Position; p+=16){
if(!strcmp((char*) Section[StringTable].Data + read_uint32(Section[SymbolTable].Data + p), Name)){
if(SymbolIndex) *SymbolIndex = p>>4;
return Section[SymbolTable].Data + p;
}
}
if(SymbolIndex) *SymbolIndex = p>>4;
return NULL;
}
static __inline int whitespace(const uint8_t *Data){
return (Data[0] == ' ' || Data[0] == '\t' || Data[0] == '\r' || Data[0] == '\n' || Data[0] == '\0');
}
static __inline int comment(const uint8_t *Data){
return (Data[0] == ';' || (Data[0] == '#' && whitespace(Data+1)));
}
static int parser_next_token(ParserContext *pc, int CrossLines){
if(!pc->brc.Size) return 0;
if(pc->GaveLastToken){
pc->GaveLastToken = 0;
/* find the start of the next token */
while(1){
if(whitespace(pc->brc.Data)){
if(pc->brc.Data[0] == '\n'){
pc->NextLine++; pc->NextCol = 1;
}else if(pc->brc.Data[0] == '\t'){
pc->NextCol += 4 - ((pc->NextCol-1)%4);
}else{
pc->NextCol++;
}
}else if(comment(pc->brc.Data)){
/* skip to the end of the line */
pc->NextLine++; pc->NextCol = 1;
do {
if(!--pc->brc.Size) return 0;
pc->brc.Data++;
} while(pc->brc.Data[0] != '\n');
}else break;
if(!--pc->brc.Size) return 0;
pc->brc.Data++;
}
}
if(!CrossLines && pc->Line != pc->NextLine) return 0;
pc->Token = (char*)pc->brc.Data;
pc->Line = pc->NextLine;
pc->Col = pc->NextCol++;
pc->GaveLastToken = 1;
/* mark the end of this token with a null character */
while(1){
if(!--pc->brc.Size) return 1;
pc->brc.Data++;
if(whitespace(pc->brc.Data)){
if(pc->brc.Data[0] == '\n'){
pc->NextLine++; pc->NextCol = 1;
}else if(pc->brc.Data[0] == '\t'){
pc->NextCol += 4 - ((pc->NextCol-1)%4);
}else{
pc->NextCol++;
}
break;
}else if(comment(pc->brc.Data)){
/* skip to the end of the line */
pc->NextLine++; pc->NextCol = 1;
do {
if(!--pc->brc.Size) return 1;
pc->brc.Data++;
} while(pc->brc.Data[0] != '\n');
break;
}else pc->NextCol++;
}
pc->brc.Size--;
pc->brc.Data[0] = '\0';
pc->brc.Data++;
return 1;
}
static __inline void verify_eol(ParserContext *pc){
if(parser_next_token(pc, 0))
Shutdown_M("%s:%u:%u: error: expected newline before '%s'\n",
path[txt], pc->Line, pc->Col, pc->Token);
}
static void verify_name(const ParserContext *pc, const char *thistoken, const char *nexttoken){
const char *Name;
for(Name = pc->Token; *Name; Name++)
if((Name == pc->Token || *Name < '0' || *Name > '9')
&& (*Name < 'A' || *Name > 'Z') && (*Name < '_' || *Name > '_') && (*Name < 'a' || *Name > 'z'))
Shutdown_M("%s:%u:%u: error: expected %s before '%c'\n",
path[txt], pc->Line, pc->Col, (Name == pc->Token) ? thistoken : nexttoken, *Name);
}
static __inline void parser_add_symbol(const ParserContext *pc){
uint8_t * Symbol;
verify_name(pc, "label or integer constant", "newline");
/* we're using the ELF st_size field (offset 8) to hold the line number temporarily */
Symbol = find_symbol_by_name(pc->Token, NULL);
if(Symbol){
if(read_uint32(Symbol+4))
Shutdown_M("%s:%u:%u: error: label '%s' redefined (previous definition at %s:%u:1)\n",
path[txt], pc->Line, pc->Col, pc->Token, path[txt], read_uint32(Symbol+8));
}else Symbol = add_symbol(pc->Token);
write_uint32(Symbol+4, Section[Text].Position);
write_uint32(Symbol+8, pc->Line);
}
static __inline void parser_add_reference(const ParserContext *pc){
uint32_t SymbolIndex;
verify_name(pc, "label", "operand or newline");
if(!find_symbol_by_name(pc->Token, &SymbolIndex))
add_symbol(pc->Token);
bw_write32(&Section[RelocationTable], Section[Text].Position);
bw_write32(&Section[RelocationTable], (SymbolIndex<<8)|0x02);
}
static uint32_t read_integer(const ParserContext *pc, uint32_t maxval){
unsigned long value;
char * endptr;
if(pc->Token[0] == '-')
Shutdown_M("%s:%u:%u: error: integer constant '%s' may not be negative\n",
path[txt], pc->Line, pc->Col, pc->Token);
if(pc->Token[0] < '0' || pc->Token[0] > '9')
Shutdown_M("%s:%u:%u: error: expected integer constant before '%s'\n",
path[txt], pc->Line, pc->Col, pc->Token);
if(pc->Token[0] == '0' && (pc->Token[1] == 'x' || pc->Token[1] == '0' || pc->Token[1] == 'o'))
Shutdown_M("%s:%u:%u: error: illegal %s prefix '%s%c' on integer constant '%s'\n",
path[txt], pc->Line, pc->Col, pc->Token[1] == 'x' ? "hexadecimal" : "octal",
pc->Token[1] != '0' ? "0" : "", pc->Token[1], pc->Token);
value = strtoul(pc->Token, &endptr, 10);
if(*endptr == '.')
Shutdown_M("%s:%u:%u: error: illegal floating point constant '%s'\n",
path[txt], pc->Line, pc->Col, endptr, pc->Token);
if(*endptr != '\0')
Shutdown_M("%s:%u:%u: error: illegal suffix '%s' on integer constant '%s'\n",
path[txt], pc->Line, pc->Col, endptr, pc->Token);
if(value > (unsigned long) maxval || errno == ERANGE)
Shutdown_M("%s:%u:%u: error: integer constant '%s' is out of range\n",
path[txt], pc->Line, pc->Col, pc->Token);
return (uint32_t)value;
}
static uint32_t read_constant(ParserContext *pc, int Type, uint32_t maxval){
unsigned i;
if(pc->Token[0] == '#') /* note that the tokens "#" and "" will never be returned by the parser */
pc->Token++;
if((pc->Token[0] >= '0' && pc->Token[0] <= '9') || pc->Token[0] == '-' || pc->Token[0] == '.'){
if(Type == CN_LABELONLY)
Shutdown_M("%s:%u:%u: error: explicit address '%s' is forbidden\n",
path[txt], pc->Line, pc->Col, pc->Token);
return read_integer(pc, maxval);
}
verify_name(pc, "constant or label", "operand or newline");
for(i=0; i<ConstantCount; i++){
if(!strcmp(Constants[i].Name, pc->Token)){
if(Constants[i].Value > maxval)
Shutdown_M("%s:%u:%u: error: integer constant '%s' (%u) is out of range\n",
path[txt], pc->Line, pc->Col, pc->Token, Constants[i].Value);
return Constants[i].Value;
}
}
if(maxval != 0xFFFFFFFF)
Shutdown_M("%s:%u:%u: error: address referenced by label '%s' is out of range\n",
path[txt], pc->Line, pc->Col, pc->Token);
parser_add_reference(pc);
return 0;
}
static __inline uint8_t read_instruction(const ParserContext *pc, uint32_t *operands){
uint8_t i;
verify_name(pc, "instruction", "operand or newline");
for(i=0; i<InstructionCount; i++){
if(!strcmp(pc->Token, Instructions[i].Name)){
*operands = Instructions[i].Operands;
return i+1;
}
}
return 0;
}
static __inline uint8_t read_variable(const ParserContext *pc, const variable_t *Variables, size_t VariableCount){
unsigned i;
verify_name(pc, "variable", "operand or newline");
for(i=0; i<VariableCount; i++)
if(!strcmp(Variables[i].Name, pc->Token))
return Variables[i].Value;
Shutdown_M("%s:%u:%u: error: unrecognized variable '%s'\n",
path[txt], pc->Line, pc->Col, pc->Token);
return 0;
}
static void Shutdown(){
unsigned i;
for(i=0; i<filecount; i++){
free(path[i]);
free(data[i]);
}
for(i=0; i<SectionCount; i++)
free(Section[i].Data);
if(hFile)
fclose(hFile);
}
int main(int argc, char *argv[]){
unsigned i;
int SimsVersion = 0;
int overwrite = 0;
size_t filesize[filecount-1];
unsigned slash;
const variable_t * Variables;
size_t VariableCount;
ParserContext pc;
int InBinary = 0;
uint8_t * SectionHeader;
size_t SectionOffset;
if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){
printf("Usage: hitasm [-ts1|-tso] [-f] [-o outfile.o] infile.txt\n"
"Assemble a HIT source file to an intermediary object file\n"
"which can be linked using hitld.\n"
"\n"
"Use -f to force overwriting without confirmation.\n"
"\n"
"Report bugs to <X-Fi6@phppoll.org>.\n"
"hitutils is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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(i != (unsigned)argc-2){
if(!strcmp(argv[i], "-o")) path[out] = argv[++i];
else break;
}
else break;
}
path[txt] = argv[i];
for(i=0; i<filecount; i++)
if(path[i])
path[i] = strdup(path[i]); /* necessary for free(path[i]) in Shutdown_M */
if(!SimsVersion)
Shutdown_M("%sSims version not specified. (Use -ts1 or -tso.)\n", "hitasm: Error: ");
if(SimsVersion == VERSION_TS1){
Variables = TS1Variables;
VariableCount = TS1VariableCount;
}else{
Variables = TSOVariables;
VariableCount = TSOVariableCount;
}
if(path[out] == NULL){
int length = strlen(path[txt]);
path[out] = malloc(max(length+1, 3));
strcpy(path[out], path[txt]);
strcpy(path[out] + max(length-4, 0), ".o");
}
/****
** Read all of the requested files
*/
for(i=0; i<filecount-1; i++){
size_t bytestransferred;
hFile = fopen(path[i], "rb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitasm: Error: ", path[i]);
fseek(hFile, 0, SEEK_END);
filesize[i] = ftell(hFile);
if(filesize[i] == 0 || filesize[i] == SIZE_MAX)
Shutdown_M("%sFile is invalid: %s.\n", "hitasm: Error: ", path[i]);
data[i] = malloc(filesize[i]+1);
if(data[i] == NULL)
Shutdown_M("%sCould not allocate memory for file: %s.\n", "hitasm: Error: ", path[i]);
fseek(hFile, 0, SEEK_SET);
bytestransferred = fread(data[i], 1, filesize[i], hFile);
fclose(hFile); hFile = NULL;
if(bytestransferred != filesize[i])
Shutdown_M("%sCould not read file: %s.\n", "hitasm: Error: ", path[i]);
data[i][filesize[i]++] = '\0'; /* add a null character to the end of the file */
}
/****
** Open the output file for writing
*/
if(!overwrite){
hFile = fopen(path[out], "rb");
if(hFile != NULL){
/* File exists */
char c;
fclose(hFile); hFile = NULL;
fprintf(stderr, "%sFile \"%s\" exists.\nContinue anyway? (y/n) ", "hitasm: ", path[out]);
c = getchar();
if(c != 'y' && c != 'Y')
Shutdown_M("\nAborted.\n");
}
}
hFile = fopen(path[out], "wb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitasm: Error: ", path[out]);
/****
** Perform the assembly
*/
for(i=0; i<SectionCount; i++){
Section[i].Data = malloc(Section[Text].Size);
if(!Section[i].Data)
Shutdown_M("%sCould not allocate memory for %s section.\n", "hitasm: Error: ", Section[i].Name);
}
pc.NextLine = 1;
pc.NextCol = 1;
pc.GaveLastToken = 1;
pc.brc.Data = data[txt];
pc.brc.Size = filesize[txt];
bw_write_memory(&Section[SymbolTable], SymbolTableHeader, sizeof(SymbolTableHeader));
for(i=slash=0; path[txt][i]; i++)
if(path[txt][i] == '/' || path[txt][i] == '\\') slash = i+1;
bw_write8(&Section[StringTable], '\0');
bw_write_string(&Section[StringTable], path[txt] + slash);
while(parser_next_token(&pc, TK_CROSSLINES)){
/* Unimplemented commands */
if(!strcmp(pc.Token, "BASEID_TRACKDATA") || !strcmp(pc.Token, "INCLUDE")
|| !strcmp(pc.Token, "include") || !strcmp(pc.Token, "INIFILE")
|| !strcmp(pc.Token, "LIST") || !strcmp(pc.Token, "SYMBOLFILE")){
if(InBinary)
Shutdown_M("%s:%u:%u: error: invalid use of '%s' inside BINARY section\n",
path[txt], pc.Line, pc.Col, pc.Token);
while(parser_next_token(&pc, 0)); /* skip to the end of the line */
continue;
}
if(!strcmp(pc.Token, "BINARY") && !InBinary){
InBinary++;
if(!parser_next_token(&pc, TK_CROSSLINES) || strcmp(pc.Token, "["))
Shutdown_M("%s:%u:%u: error: expected '[' for beginning of BINARY section before %s%s%s\n",
path[txt], pc.Line, pc.Col,
pc.Token ? "'" : "", pc.Token ? pc.Token : "end of file", pc.Token ? "'" : "");
}
else if(!strcmp(pc.Token, "]") && InBinary)
InBinary--;
else if(!InBinary)
Shutdown_M("%s:%u:%u: error: '%s' is not a valid command\n",
path[txt], pc.Line, pc.Col, pc.Token);
/****
** Inside a BINARY section
*/
else if(pc.Col == 1) /* no indent */
parser_add_symbol(&pc);
else{ /* indent */
uint8_t opcode;
uint32_t operands;
if((pc.Token[0] >= '0' && pc.Token[0] <= '9') || pc.Token[0] == '-' || pc.Token[0] == '.'
|| pc.Token[0] == '#' || !(opcode = read_instruction(&pc, &operands))){
/* declare bytes (db and dd pseudo-instructions) */
do {
if(pc.Token[0] != '#')
bw_write8(&Section[Text], read_integer(&pc, 0x000000FF));
else
bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF));
} while(parser_next_token(&pc, 0));
continue;
}else{
const char * InstructionName = pc.Token;
bw_write8(&Section[Text], opcode);
for(i=0; (operands >>= 4) != 0; i++){
int type = operands & 15;
const char *position[] = {"one","two","three","four"};
if(!parser_next_token(&pc, 0)){
int j;
for(j=i+1; (operands >>= 4) != 0; j++);
Shutdown_M("%s:%u:%u: error: instruction '%s' wants %s operands but only %s supplied\n",
path[txt], pc.Line, pc.Col, pc.Token, InstructionName, position[j], position[i]);
}
if(type == o_byte)
bw_write8(&Section[Text], read_constant(&pc, 0, 0x000000FF));
else if(type == o_dword)
bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF));
else if(type == o_address)
bw_write32(&Section[Text], read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF));
else if(type == o_variable)
bw_write8(&Section[Text], read_variable(&pc, Variables, VariableCount));
else if(type == o_jump){
/* TODO: Change this */
bw_write32(&Section[Text], read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF));
}
}
}
}
verify_eol(&pc);
}
if(InBinary)
Shutdown_M("%s:%u:%u: error: expected ']' for end of BINARY section before end of file\n",
path[txt], pc.Line, pc.Col);
/****
** Prepare and write out the ELF object header and all sections
*/
for(i=48+8; i<Section[SymbolTable].Position; i+=16)
write_uint32(Section[SymbolTable].Data + i, 0); /* clear the st_size field we used temporarily */
if(SimsVersion == VERSION_TSO)
ObjectHeader[36]++; /* set the lsb of the processor flags to indicate that this code is for TSO */
for(i = 0, SectionHeader = ObjectHeader + 120, SectionOffset = 304; i < SectionCount; i++){
write_uint32(SectionHeader + 0, SectionOffset);
write_uint32(SectionHeader + 4, Section[i].Position);
SectionHeader += 40;
SectionOffset += Section[i].Position;
if(i == 0){
write_uint32(SectionHeader + 0, SectionOffset);
write_uint32(SectionHeader + 4, sizeof(SHStringTable));
SectionHeader += 40;
SectionOffset += sizeof(SHStringTable);
}
}
fwrite(ObjectHeader, 1, sizeof(ObjectHeader), hFile);
fwrite(Section[Text].Data, 1, Section[Text].Position, hFile);
fwrite(SHStringTable, 1, sizeof(SHStringTable), hFile);
for(i=1; i<SectionCount; i++)
fwrite(Section[i].Data, 1, Section[i].Position, hFile);
Shutdown();
return 0;
}

View file

@ -0,0 +1,638 @@
/*
hitutils - The Sims HIT (dis)assembler and linker
hitdump.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "hitutils.h"
enum {
hsm, hot, evt, hit, out, filecount
};
static __inline const char * find_variable(uint32_t x, const variable_t * Variables, size_t VariableCount){
size_t i;
for(i=0; i<VariableCount; i++)
if(Variables[i].Value == x)
return Variables[i].Name;
return NULL;
}
enum TokenizeType {
TK_STRING,
TK_ID
};
static int parser_find(ByteReaderContext *brc, ...){
va_list args;
va_start(args, brc);
while(1){
uint8_t * Start = brc->Data;
const char * Pattern;
size_t Length;
void * Destination;
enum TokenizeType Type;
Pattern = va_arg(args, const char *);
if(Pattern == NULL){
va_end(args);
return 1;
}
for(Length = strlen(Pattern); ; brc->Data++, brc->Size--){
if(brc->Size < Length){
va_end(args);
return 0;
}
if(!memcmp(brc->Data, Pattern, Length)) break;
}
*brc->Data = '\0';
brc->Data += Length; brc->Size -= Length;
Destination = va_arg(args, void *);
if(Destination == NULL)
continue;
Type = va_arg(args, enum TokenizeType);
if(Type == TK_STRING)
*((char**)Destination) = (char*)Start;
else
*((uint32_t*)Destination) = strtoul((char*)Start, NULL, 0);
}
}
typedef struct {
uint32_t LogicalAddress;
uint32_t TrackID;
uint32_t SoundID;
char * Name;
uint32_t Exported;
} address_t;
typedef struct {
size_t SizeAllocated;
size_t Count;
address_t * Entries;
} addresslist_t;
static address_t * add_address(addresslist_t * List){
if(List->Count*sizeof(address_t) == List->SizeAllocated){
void * ptr;
if(List->SizeAllocated > SIZE_MAX/2 || !(ptr = realloc(List->Entries, List->SizeAllocated<<=1)))
Shutdown_M("%sCould not allocate memory for address list.\n", "hitdump: Error: ");
List->Entries = ptr;
}
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; i<List->Count; 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; i<List->Count; 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; i<List->Count; 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; i<List->Count; 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 * TableData;
unsigned i, count = 0;
ByteReaderContext brc;
brc.Data = Data; brc.Size = Size;
if(!parser_find(&brc, "ENTP", NULL, NULL) || brc.Size < 4) return;
TableData = brc.Data;
*SymbolTable = TableData - 4 - Data;
while(memcmp(Data, "EENT", 4)){
if(Size < 12) return;
Data+=8; Size-=8;
count++;
}
for(i=0; i<count; i++){
address_t * Address = add_address(AddressList);
Address->Exported = 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){
ByteReaderContext brc;
brc.Data = Data; brc.Size = Size;
while(1){
address_t * Address;
char *Name;
uint32_t TrackID;
if(!parser_find(&brc,
",", &Name, TK_STRING,
",", NULL,
",", &TrackID, TK_ID,
NULL)) return;
Address = find_address_by_track_id(AddressList, TrackID);
if(!Address){
Address = add_address(AddressList);
Address->Exported = 1;
Address->TrackID = TrackID;
}
Address->Name = Name;
if(!parser_find(&brc, "\n", NULL, NULL)) return;
}
}
static __inline void read_hsm_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList){
ByteReaderContext brc;
brc.Data = Data; brc.Size = Size;
while(1){
address_t * Address;
char * Name;
uint32_t SoundID, LogicalAddress;
if(!parser_find(&brc,
"\ntkd_", NULL,
" ", &Name, TK_STRING,
" ", &SoundID, TK_ID,
" ", NULL,
" ", &LogicalAddress, TK_ID,
NULL)) return;
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 = Name;
Address->SoundID = SoundID;
}
}
static __inline void read_hot_trackdata(uint8_t * Data, size_t Size, addresslist_t * AddressList){
ByteReaderContext brc;
brc.Data = Data; brc.Size = Size;
if(!parser_find(&brc, "[TrackData]", NULL, NULL)) return;
while(1){
address_t * Address;
uint32_t SoundID, LogicalAddress;
if(!brc.Size || *brc.Data == '\r' || *brc.Data == '\n' || *brc.Data == '[') return;
if(!parser_find(&brc,
"=", &SoundID, TK_ID,
"\n", &LogicalAddress, TK_ID,
NULL)) return;
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;
}
}
static __inline void read_hot_track(uint8_t * Data, size_t Size, addresslist_t * AddressList){
ByteReaderContext brc;
brc.Data = Data; brc.Size = Size;
if(!parser_find(&brc, "[Track]", NULL, NULL)) return;
while(1){
address_t * Address;
char * Name;
uint32_t TrackID;
if(!brc.Size || *brc.Data == '\r' || *brc.Data == '\n' || *brc.Data == '[') return;
if(!parser_find(&brc,
"=", &TrackID, TK_ID,
",", NULL,
",", &Name, TK_STRING,
NULL)) return;
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 = Name;
} else Address->TrackID = TrackID;
Address->Exported = 1;
if(!parser_find(&brc, "\n", NULL, NULL)) 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);
}
static FILE *hFile = NULL;
static char *path[filecount] = {NULL};
static uint8_t *data[filecount] = {NULL};
static char *basename = NULL;
static addresslist_t AddressList = {0};
static void Shutdown(){
unsigned i;
for(i=0; i<filecount; i++){
free(path[i]);
free(data[i]);
}
free(basename);
free(AddressList.Entries);
if(hFile)
fclose(hFile);
}
int main(int argc, char *argv[]){
unsigned i, addr;
int SimsVersion = 0;
int overwrite = 0;
int ShowAddresses = 0;
int length;
size_t filesize[filecount-1];
const variable_t * Variables;
size_t VariableCount;
uint32_t SymbolTable = 0;
uint32_t BaseSoundID = 0, BaseSoundIDSet = 0;
/****
** 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 <X-Fi6@phppoll.org>.\n"
"hitutils is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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], "-o")) 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;
}
path[hit] = argv[i];
for(i=0; i<filecount; i++)
if(path[i])
path[i] = strdup(path[i]); /* necessary for free(path[i]) in Shutdown_M */
if(!SimsVersion)
Shutdown_M("%sSims version not specified. (Use -ts1 or -tso.)\n", "hitdump: Error: ");
if(SimsVersion == VERSION_TS1){
Variables = TS1Variables;
VariableCount = TS1VariableCount;
}else{
Variables = TSOVariables;
VariableCount = TSOVariableCount;
}
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; i<filecount-1; i++){
size_t bytestransferred;
if(!path[i]) continue;
hFile = fopen(path[i], "rb");
if(hFile == NULL){
if(i != hit){
fprintf(stderr, "%sCould not open file: %s.\n", "hitdump: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sCould not open file: %s.\n", "hitdump: Error: ", path[i]);
}
fseek(hFile, 0, SEEK_END);
filesize[i] = ftell(hFile);
if(filesize[i] == 0){
fclose(hFile); hFile = NULL;
if(i != hit){
fprintf(stderr, "%sFile is invalid: %s.\n", "hitdump: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sFile is invalid: %s.\n", "hitdump: Error: ", path[i]);
}
data[i] = malloc(filesize[i]);
if(data[i] == NULL){
fclose(hFile); hFile = NULL;
if(i != hit){
fprintf(stderr, "%sCould not allocate memory for file: %s.\n", "hitdump: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sCould not allocate memory for file: %s.\n", "hitdump: Error: ", path[i]);
}
fseek(hFile, 0, SEEK_SET);
bytestransferred = fread(data[i], 1, filesize[i], hFile);
fclose(hFile); hFile = NULL;
if(bytestransferred != filesize[i]){
free(data[i]); data[i] = NULL;
if(i != hit){
fprintf(stderr, "%sCould not read file: %s.\n", "hitdump: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sCould not read file: %s.\n", "hitdump: Error: ", path[i]);
}
}
/****
** Open the output file for writing
*/
if(!overwrite){
hFile = fopen(path[out], "rb");
if(hFile != NULL){
/* File exists */
char c;
fclose(hFile); hFile = NULL;
fprintf(stderr, "%sFile \"%s\" exists.\nContinue anyway? (y/n) ", "hitdump: ", path[out]);
c = getchar();
if(c != 'y' && c != 'Y')
Shutdown_M("\nAborted.\n");
}
}
hFile = fopen(path[out], "wb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitdump: Error: ", path[out]);
/****
** Verify the header of the HIT file
*/
if(filesize[hit] < 16 || memcmp(data[hit], HITHeader, 16))
Shutdown_M("%sFile is invalid: %s.\n", "hitdump: Error: ", path[hit]);
/****
** Build up the address list
*/
AddressList.SizeAllocated = 32 * sizeof(address_t);
AddressList.Count = 0;
AddressList.Entries = malloc(32 * sizeof(address_t));
read_hit_addresses(data[hit], filesize[hit], &AddressList, &SymbolTable);
if(data[evt]) read_evt_addresses(data[evt], filesize[evt], &AddressList);
if(data[hsm]) read_hsm_addresses(data[hsm], filesize[hsm], &AddressList);
if(data[hot]) read_hot_addresses(data[hot], filesize[hot], &AddressList);
/* scan_branch_destinations(data[hit], filesize[hit], &AddressList); */
for(i=0; i<AddressList.Count; i++){
if(AddressList.Entries[i].SoundID != 0 && (!BaseSoundIDSet || AddressList.Entries[i].SoundID < BaseSoundID)){
BaseSoundID = AddressList.Entries[i].SoundID;
BaseSoundIDSet = 1;
}
if(ShowAddresses){
printf("Address %u:\n Exported: %u\n TrackID: %u\n SoundID: %u\n Name: %s\n LogicalAddress: %u\n", i,
AddressList.Entries[i].Exported,
AddressList.Entries[i].TrackID,
AddressList.Entries[i].SoundID,
AddressList.Entries[i].Name ? AddressList.Entries[i].Name : "",
AddressList.Entries[i].LogicalAddress
);
}
}
/****
** Perform the disassembly
*/
fprintf(hFile, "BASEID_TRACKDATA %u\r\n"
"\r\n"
";\r\n"
"; generated by hitdump.\r\n"
";\r\n"
"\r\n"
"; useful symbols:\r\n"
"; kSndobPlay = 1\r\n"
"; tkd_Generic 1\r\n"
"; tkd_GenericLooped 2\r\n"
"; tkd_GenericHitList 3\r\n"
"\r\n"
"INCLUDE defaultsyms.txt\r\n"
"INCLUDE SimsVariables.txt\r\n"
"\r\n"
"LIST [Options] Version=1\r\n"
"LIST [Options] LoadPriority=2\r\n"
"\r\n"
";LIST [EventMappingEquate] kSndobPlay=1\r\n"
"; --- end of standard intro text ---\r\n"
"\r\n"
"SYMBOLFILE %s%s\r\n"
"INIFILE %s.ini", BaseSoundID, path[hsm] ? path[hsm] : basename, path[hsm] ? "" : ".hsm", basename);
fprintf(hFile, "\r\n\r\nBINARY\r\n[");
for(addr=16; addr<filesize[hit];){
unsigned i;
uint8_t opcode;
const instruction_t * instruction;
uint32_t operands;
const address_t * Address;
int HadSymbolTable = 0;
if(SymbolTable && addr == SymbolTable){
if(addr != 16)
fprintf(hFile, "\r\n]\r\n\r\nBINARY\r\n[");
fprintf(hFile, "\r\n"
"\t69\r\n"
"\t78\r\n"
"\t84\r\n"
"\t80\r\n");
for(addr+=4; memcmp(data[hit]+addr, "EENT", 4); addr+=8){
uint32_t TrackID = read_uint32(data[hit]+addr), LogicalAddress = read_uint32(data[hit]+addr+4);
Address = find_address_by_logical_address(&AddressList, LogicalAddress);
fprintf(hFile, "\r\n\t#%u\t\t#", TrackID);
if(Address && Address->Name) 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 > InstructionCount)
Shutdown_M("%sIllegal opcode 0x%02X at address 0x%08X.\n", "hitdump: Error: ", opcode, addr);
instruction = Instructions + opcode - 1;
operands = instruction->Operands;
if(operands == UNIMPLEMENTED)
Shutdown_M("%sUnimplemented instruction '%s' at address 0x%08X.\n", "hitdump: Error: ", instruction->Name, addr);
addr++;
if(filesize[hit] - addr < (operands & 15))
Shutdown_M("%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);
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){
unsigned x = data[hit][addr];
const char * Variable = find_variable(x, Variables, VariableCount);
if(Variable == NULL)
Shutdown_M("%sInvalid %s operand 0x%02X for '%s' instruction at address 0x%08X (expected variable).\n",
"hitdump: Error: ", position[i], x, instruction->Name, addr);
fprintf(hFile, " %s", Variable);
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)
Shutdown_M("%sInsufficient operand bytes for '%s' instruction at address 0x%08X (%u of %u supplied).\n",
"hitdump: Error: ", instruction->Name, addr, filesize[hit] - addr, 4);
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{
const char * Variable;
x = data[hit][addr];
Variable = find_variable(x, Variables, VariableCount);
if(Variable == NULL)
Shutdown_M("%sInvalid %s operand 0x%02X for '%s' instruction at address 0x%08X (expected variable).\n",
"hitdump: Error: ", position[i], x, instruction->Name, addr);
fprintf(hFile, " %s", Variable);
addr += (data[hit][addr] != 0x05 && data[hit][addr] != 0x06) ? 4 : 1;
}
}
}
}
fprintf(hFile, "\r\n]\r\n\r\n");
Shutdown();
return 0;
}

View file

@ -0,0 +1,174 @@
/*
hitutils - The Sims HIT (dis)assembler and linker
hitld.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "hitutils.h"
static const uint8_t ObjectHeader[] = {0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01};
static const uint8_t ArchiveHeader[] = {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'};
enum {
hsm, hot, out, filecount
};
typedef struct {
char *path;
uint8_t *data;
} object_t;
typedef struct {
size_t SizeAllocated;
size_t Count;
object_t * Entries;
} objectlist_t;
static object_t * add_object(objectlist_t * List){
if(List->Count*sizeof(object_t) == List->SizeAllocated){
void * ptr;
if(List->SizeAllocated > SIZE_MAX/2 || !(ptr = realloc(List->Entries, List->SizeAllocated<<=1)))
Shutdown_M("%sCould not allocate memory for object list.\n", "hitld: Error: ");
List->Entries = ptr;
}
return memset(List->Entries + List->Count++, 0, sizeof(object_t));
}
static FILE *hFile = NULL;
static char *path[filecount] = {NULL};
static uint8_t *data[filecount] = {NULL};
static objectlist_t ObjectList = {0};
static void Shutdown(){
unsigned i;
for(i=0; i<filecount; i++){
free(path[i]);
free(data[i]);
}
for(i=0; i<ObjectList.Count; i++){
free(ObjectList.Entries[i].path);
free(ObjectList.Entries[i].data);
}
free(ObjectList.Entries);
if(hFile)
fclose(hFile);
}
int main(int argc, char *argv[]){
unsigned i;
unsigned ObjectArg;
int SimsVersion = 0;
int overwrite = 0;
size_t filesize[filecount-1];
if(argc < 3){
printf("Usage: hitld [-ts1|-tso] [-f] [-o outfile.hit] [-hsm infile.hsm]\n"
" [-hot infile.hot] 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 <X-Fi6@phppoll.org>.\n"
"hitutils is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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(i != (unsigned)argc-2){
if(!strcmp(argv[i], "-o")) 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 break;
}
else break;
}
ObjectArg = i;
for(i=0; i<filecount; i++)
if(path[i])
path[i] = strdup(path[i]); /* necessary for free(path[i]) in Shutdown_M */
if(!SimsVersion)
Shutdown_M("%sSims version not specified. (Use -ts1 or -tso.)\n", "hitasm: Error: ");
if(path[out] == NULL){
int length = strlen(argv[ObjectArg]);
path[out] = malloc(max(length+1+2, 5));
strcpy(path[out], argv[ObjectArg]);
strcpy(path[out] + max(length-2, 0), ".hit");
}
/****
** Read all of the requested files
*/
for(i=0; i<filecount-1; i++){
size_t bytestransferred;
if(!path[i]) continue;
hFile = fopen(path[i], "rb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitasm: Error: ", path[i]);
fseek(hFile, 0, SEEK_END);
filesize[i] = ftell(hFile);
if(filesize[i] == 0)
Shutdown_M("%sFile is invalid: %s.\n", "hitasm: Error: ", path[i]);
data[i] = malloc(filesize[i]);
if(data[i] == NULL)
Shutdown_M("%sCould not allocate memory for file: %s.\n", "hitasm: Error: ", path[i]);
fseek(hFile, 0, SEEK_SET);
bytestransferred = fread(data[i], 1, filesize[i], hFile);
fclose(hFile); hFile = NULL;
if(bytestransferred != filesize[i])
Shutdown_M("%sCould not read file: %s.\n", "hitasm: Error: ", path[i]);
}
/****
** Open the output file for writing
*/
if(!overwrite){
hFile = fopen(path[out], "rb");
if(hFile != NULL){
/* File exists */
char c;
fclose(hFile); hFile = NULL;
fprintf(stderr, "%sFile \"%s\" exists.\nContinue anyway? (y/n) ", "hitdump: ", path[out]);
c = getchar();
if(c != 'y' && c != 'Y')
Shutdown_M("\nAborted.\n");
}
}
hFile = fopen(path[out], "wb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitdump: Error: ", path[out]);
fwrite(HITHeader, 1, sizeof(HITHeader), hFile);
Shutdown();
return 0;
}

View file

@ -0,0 +1,452 @@
/*
hitutils - The Sims HIT (dis)assembler and linker
hitutils.h - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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.
*/
#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
#ifndef write_int32
#define write_uint32(dest, src) do {\
(dest)[0] = ((src)&0x000000FF)>>(8*0); \
(dest)[1] = ((src)&0x0000FF00)>>(8*1); \
(dest)[2] = ((src)&0x00FF0000)>>(8*2); \
(dest)[3] = ((src)&0xFF000000)>>(8*3); \
} while(0)
#define write_uint16(dest, src) do {\
(dest)[0] = ((src)&0x00FF)>>(8*0); \
(dest)[1] = ((src)&0xFF00)>>(8*1); \
} while(0)
#endif
extern char *strdup (const char *__s);
static void Shutdown();
static void Shutdown_M(const char * Message, ...){
va_list args;
va_start(args, Message);
vfprintf(stderr, Message, args);
va_end(args);
Shutdown();
exit(EXIT_FAILURE);
}
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;
} variable_t;
typedef struct {
uint8_t * Data;
size_t Size;
} ByteReaderContext;
static const uint8_t HITHeader[] = {'H','I','T','!',0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,'T','R','A','X'};
#define InstructionCount 96
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)}
};
#define TSOVariableCount 82
static const variable_t TSOVariables[] = {
{"arg0", 0x00},
{"arg1", 0x01},
{"arg2", 0x02},
{"arg3", 0x03},
{"arg4", 0x04},
{"v1", 0x05},
{"v2", 0x06},
{"v3", 0x07},
{"v4", 0x08},
{"v5", 0x09},
{"v6", 0x0A},
{"v7", 0x0B},
{"v8", 0x0C},
{"h1", 0x0D},
{"h2", 0x0E},
{"h3", 0x0F},
{"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 TS1VariableCount 87
static const variable_t TS1Variables[] = {
{"arg0", 0x00},
{"arg1", 0x01},
{"arg2", 0x02},
{"arg3", 0x03},
{"arg4", 0x04},
{"v1", 0x05},
{"v2", 0x06},
{"v3", 0x07},
{"v4", 0x08},
{"v5", 0x09},
{"v6", 0x0a},
{"v7", 0x0b},
{"v8", 0x0c},
{"h1", 0x0d},
{"h2", 0x0e},
{"h3", 0x0f},
{"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},
};
#define ConstantCount 72
static const variable_t Constants[] = {
{"duckpri_always", 0x0},
{"duckpri_low", 0xa},
{"duckpri_normal", 0x14},
{"duckpri_high", 0x1e},
{"duckpri_higher", 0x28},
{"duckpri_evenhigher", 0x32},
{"duckpri_never", 0x64},
{"spl_infinite", 0x0},
{"spl_loud", 0xa},
{"spl_normal", 0x14},
{"spl_quiet", 0x64},
{"Instance", 0x0},
{"Gender", 0x1},
{"GroupMusic", 0x1},
{"GroupDialogMain", 0x2},
{"GroupDialogOverlay", 0x3},
{"PchDishwasherClose", 0x32},
{"PchDishwasherLoad", 0x33},
{"PchDishwasherLoad2", 0x34},
{"PchDishwasherOpen", 0x35},
{"PchDishwasherTurnDial", 0x39},
{"PchDishwasherTurnDial2", 0x3a},
{"TrkRadioStationCountry", 0x104},
{"TrkRadioStationBossaNova", 0x10e},
{"TrkRadioStationClassical", 0x10d},
{"TrkRadioStationRock", 0x118},
{"kSndobPlay", 1},
{"kSndobStop", 2},
{"kSndobKill", 3},
{"kSndobUpdate", 4},
{"kSndobSetVolume", 5},
{"kSndobSetPitch", 6},
{"kSndobSetPan", 7},
{"kSndobSetPosition", 8},
{"kSndobSetFxType", 9},
{"kSndobSetFxLevel", 10},
{"kSndobPause", 11},
{"kSndobUnpause", 12},
{"kSndobLoad", 13},
{"kSndobUnload", 14},
{"kSndobCache", 15},
{"kSndobUncache", 16},
{"kSndobCancelNote", 19},
{"kPlayPiano", 43},
{"kSetMusicMode", 36},
{"kLive", 0},
{"kBuy", 1},
{"kBuild", 2},
{"kHood", 3},
{"kFrontEnd", 4},
{"kGroupSfx", 1},
{"kGroupMusic", 2},
{"kGroupVox", 3},
{"kAction", 1000},
{"kComedy", 1001},
{"kRomance", 1002},
{"kNews", 1003},
{"kCountry", 1004},
{"kRock", 1005},
{"kJazz", 1006},
{"kClassical", 1007},
{"kArgsNormal", 0},
{"kArgsVolPan", 1},
{"kArgsIdVolPan", 2},
{"kArgsXYZ", 3},
{"kKillAll", 20},
{"kPause", 21},
{"kUnpause", 22},
{"kKillInstance", 23},
{"kTurnOnTv", 30},
{"kTurnOffTv", 31},
{"kUpdateSourceVolPan", 32},
};
static const char SHStringTable[] =
"\0.text"
"\0.shstrtab"
"\0.symtab"
"\0.strtab"
"\0.rel.text"
;
static const uint8_t SymbolTableHeader[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xF1, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00
};