New farextract naming logic and a rewritten UTalk decompressor

This commit is contained in:
Fatbag 2013-03-07 16:17:18 -06:00
parent 558726a9f1
commit 66ce473514
11 changed files with 584 additions and 352 deletions

View file

@ -139,13 +139,7 @@ static uint8_t * ReadUTK(Sound_t * Sound, const uint8_t * InData, size_t FileSiz
return NULL;
}
static bool generated = false;
if(!generated){
UTKGenerateTables();
generated = true;
}
if(!utk_decode(InData+32, OutData, UTKHeader.Frames)){
if(!utk_decode(InData+32, OutData, FileSize-32, UTKHeader.Frames)){
free(OutData);
return NULL;
}

View file

@ -20,9 +20,31 @@
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <errno.h>
#include "far.h"
#ifndef read_uint32
#define read_uint32(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1)) | ((x)[2]<<(8*2)) | ((x)[3]<<(8*3)))
#endif
#ifdef _WIN32
#define mkdir(path, x) mkdir(path)
#endif
static int mkpath(char * path){
char * p;
for(p = strpbrk(path+1, "/\\"); p; p = strpbrk(p+1, "/\\")){
char c = *p;
int value;
*p = '\0'; value = mkdir(path, 0644); *p = c;
if(value != 0 && errno != EEXIST)
return -1;
}
return 0;
}
enum {
profile_ts1 = 1,
profile_tso,
@ -32,6 +54,122 @@ enum {
profile_ts3
};
typedef struct {
uint32_t GroupID;
const char * Directory;
} GroupMap_t;
typedef struct {
uint32_t TypeID;
const char * Extension;
} TypeMap_t;
typedef struct {
uint8_t Size;
const char * Header;
const char * Extension;
} HeaderMap_t;
static const GroupMap_t GroupMap[] = {
{0x0A3C55C7, "Music/Stations/Horror/"},
{0x0A3C55CE, "Music/Stations/OldWorld/"},
{0x0A3C55D3, "Music/Stations/SciFi/"},
{0x1D6962CF, "SoundData/HitLabUI/"},
{0x1D8A8B4F, "SoundData/HitLabTestSamples/"},
{0x29D9359D, "SoundData/CustomTrks/"},
{0x29DAA4A6, "SoundData/Custom/"},
{0x29DD0888, "SoundData/Multiplayer/"},
{0x69C6C943, "SoundData/tsov2/"},
{0x8A6FCC30, "SoundData/EP5Samps/"},
{0x9DBDBF74, "SoundData/HitLists/"},
{0x9DBDBF89, "SoundData/Samples/"},
{0x9DF26DAD, "Music/Stations/Country/"},
{0x9DF26DAE, "Music/Stations/CountryD/"},
{0x9DF26DB1, "Music/Stations/Latin/"},
{0x9DF26DB3, "Music/Stations/Rap/"},
{0x9DF26DB6, "Music/Stations/Rock/"},
{0xA9C6C89A, "SoundData/Tracks/"},
{0xBD6E5937, "SoundData/HitLabTest/"},
{0xBDF26DB0, "Music/Stations/Disco/"},
{0xC9C6C9B3, "SoundData/HitListsTemp/"},
{0xDDBDBF8C, "SoundData/Stings/"},
{0xDDE8F5C6, "SoundData/EP2/"},
{0xDDF26DA9, "Music/Stations/Beach/"},
{0xDDF26DB4, "Music/Stations/Rave/"},
{0xFDBDBF87, "SoundData/TrackDefs/"},
{0xFDF26DAB, "Music/Stations/Classica/"}
};
static const TypeMap_t TypeMap[] = {
{0, ".dat"},
{1, ".bmp"},
{2, ".tga"},
{5, ".skel"},
{7, ".anim"},
{9, ".mesh"},
{11, ".bnd"},
{12, ".apr"},
{13, ".oft"},
{14, ".png"},
{15, ".po"},
{16, ".col"},
{18, ".hag"},
{20, ".jpg"},
{24, ".png"},
{0x0A8B0E70, ".mad"},
{0x1B6B9806, ".utk"},
{0x1D07EB4B, ".xa"},
{0x1D968538, ".xa"},
{0x2026960B, ".dat"},
{0x3CEC2B47, ".mp3"},
{0x3D968536, ".mp3"},
{0x5D73A611, ".trk"},
{0x7B1ACFCD, ".hls"},
{0x856DDBAC, ".bmp"},
{0x9D796DB4, ".tlo"},
{0x9D96853A, ".wav"},
{0xA3CD96CF, ".tkd"},
{0xBB7051F5, ".wav"}
};
static const HeaderMap_t AudioHeaders[] = {
{2, "XA", ".xa"},
{4, "RIFF", ".wav"},
{4, "UTM0", ".utk"},
{4, "\xFF\xFB\x90\x40", ".mp3"},
{24, "# Generated by UI editor", ".scr"}
};
static const char * groupid_to_dir(uint32_t GroupID){
size_t i;
for(i=0; i<sizeof(GroupMap)/sizeof(GroupMap_t); i++){
if(GroupMap[i].GroupID == GroupID)
return GroupMap[i].Directory;
}
fprintf(stderr, "%sUnrecognized Group ID 0x%08X.\n", "farextract: warning: ", GroupID);
return "./";
}
static const char * typeid_to_ext(uint32_t TypeID){
size_t i;
for(i=0; i<sizeof(TypeMap)/sizeof(TypeMap_t); i++){
if(TypeMap[i].TypeID == TypeID)
return TypeMap[i].Extension;
}
fprintf(stderr, "%sUnrecognized Type ID 0x%08X.\n", "farextract: warning: ", TypeID);
return ".dat";
}
static const char * header_to_ext(const uint8_t * Buffer, size_t Size){
size_t i;
for(i=0; i<sizeof(AudioHeaders)/sizeof(HeaderMap_t); i++){
if(Size >= AudioHeaders[i].Size && !memcmp(Buffer, AudioHeaders[i].Header, AudioHeaders[i].Size))
return AudioHeaders[i].Extension;
}
return ".str";
}
int main(int argc, char *argv[]){
int profile = 0, overwrite = 0;
const char * InFile = "", * OutDirectory;
@ -62,7 +200,7 @@ int main(int argc, char *argv[]){
"\n"
"Report bugs to <X-Fi6@phppoll.org>.\n"
"farextract and libfar are maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>");
"Home page: <http://www.niotso.org/>\n");
return 0;
}
@ -141,6 +279,11 @@ int main(int argc, char *argv[]){
return -1;
}
if(chdir(OutDirectory) != 0){
fprintf(stderr, "%sOutput directory '%s' does not exist.", "farextract: error: ", OutDirectory);
return -1;
}
if(ArchiveType != FAR_TYPE_PERSIST){
FAREntryNode * EntryNode;
unsigned file = 0, filescount;
@ -177,28 +320,35 @@ int main(int argc, char *argv[]){
** Extract each entry
*/
for(EntryNode = FARFileInfo->FirstEntry; EntryNode; EntryNode = EntryNode->NextEntry){
char destination[256];
char destbuffer[256], * destination = destbuffer;
file++;
if(EntryNode->Entry.Filename)
sprintf(destination, "%s/%s", OutDirectory, EntryNode->Entry.Filename);
destination = EntryNode->Entry.Filename;
else
sprintf(destination, "%s/%08x-%08x-%08x.dat", OutDirectory,
EntryNode->Entry.TypeID, EntryNode->Entry.GroupID, EntryNode->Entry.FileID);
sprintf(destbuffer, "%s%08x%s", groupid_to_dir(EntryNode->Entry.GroupID),
EntryNode->Entry.FileID, typeid_to_ext(EntryNode->Entry.TypeID));
if(!far_read_entry_data(FARFileInfo, &(EntryNode->Entry), ArchiveData)){
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount,
"entry data is corrupt", EntryNode->Entry.Filename);
if(far_read_entry_data(FARFileInfo, &(EntryNode->Entry), ArchiveData)){
/* Decompression, if any, was successful */
if(!EntryNode->Entry.Filename && EntryNode->Entry.TypeID == 0x2026960B)
sprintf(destbuffer, "%s%08x%s", groupid_to_dir(EntryNode->Entry.GroupID), EntryNode->Entry.FileID,
header_to_ext(EntryNode->Entry.DecompressedData, EntryNode->Entry.DecompressedSize));
}else{
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "entry data is corrupt", destination);
continue;
}
/* Decompression, if any, was successful */
if(mkpath(destination) != 0){
fprintf(stderr, "%sCould not create path '%s'.", "farextract: error: ", destination);
return -1;
}
if(!overwrite){
hFile = fopen(destination, "rb");
if(hFile != NULL){
/* File exists */
fclose(hFile);
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "could not open", EntryNode->Entry.Filename);
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "file exists", destination);
if(EntryNode->Entry.DecompressedData != EntryNode->Entry.CompressedData)
libfar_free(EntryNode->Entry.DecompressedData);
continue;
@ -206,18 +356,14 @@ int main(int argc, char *argv[]){
}
hFile = fopen(destination, "wb");
if(hFile == NULL){
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "could not open", EntryNode->Entry.Filename);
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "could not open", destination);
if(EntryNode->Entry.DecompressedData != EntryNode->Entry.CompressedData)
libfar_free(EntryNode->Entry.DecompressedData);
continue;
}
if(EntryNode->Entry.Filename)
printf(" (%u/%u) %s (%u bytes)\n", file, filescount,
EntryNode->Entry.Filename, EntryNode->Entry.DecompressedSize);
else
printf(" (%u/%u) %08x-%08x-%08x (%u bytes)\n", file, filescount,
EntryNode->Entry.TypeID, EntryNode->Entry.GroupID, EntryNode->Entry.FileID,
printf(" (%u/%u) %s (Group ID: 0x%08X, File ID: 0x%08X, Type ID: 0x%08X) (%u bytes)\n", file, filescount,
destination, EntryNode->Entry.GroupID, EntryNode->Entry.FileID, EntryNode->Entry.TypeID,
EntryNode->Entry.DecompressedSize);
fwrite(EntryNode->Entry.DecompressedData, 1, EntryNode->Entry.DecompressedSize, hFile);
@ -232,8 +378,6 @@ int main(int argc, char *argv[]){
}else{
/* Persist file */
PersistFile * PersistInfo;
char destination[256];
sprintf(destination, "%s/%s.out", OutDirectory, InFile);
/****
** Load header information
@ -261,7 +405,7 @@ int main(int argc, char *argv[]){
EndingTime = clock();
if(!overwrite){
hFile = fopen(destination, "rb");
hFile = fopen(InFile, "rb");
if(hFile != NULL){
/* File exists */
fclose(hFile);
@ -270,7 +414,7 @@ int main(int argc, char *argv[]){
return -1;
}
}
hFile = fopen(destination, "wb");
hFile = fopen(InFile, "wb");
if(hFile == NULL){
fprintf(stderr, "%sCould not open.", "farextract: error: ");
libfar_free(PersistInfo->DecompressedData);

View file

@ -35,21 +35,75 @@
#ifndef round
#define round(x) ((x) >= 0 ? (x)+0.5 : (x)-0.5)
#endif
#ifndef clamp
#define clamp(x, low, high) ((x) < low ? low : (x) > high ? high : (x))
#endif
#ifndef min
#define min(x, y) ((x) < (y) ? (x) : (y))
#endif
static uint8_t ReadBits(utkparams_t *p, uint8_t bits);
static void SetUTKParameters(utkparams_t *p);
static void DecompressBlock(utkparams_t *p);
static void LatticeFilter(utkparams_t *p, int Voiced, float * Window, int Interval);
static void Synthesize(utkparams_t *p, unsigned Sample, unsigned Blocks);
static void PredictionFilter(const float *__restrict ImpulseTrain, float *__restrict Residual);
static uint8_t ReadBits(utkcontext_t *ctx, uint8_t bits);
static void InitUTKParameters(utkcontext_t *ctx);
static void DecodeFrame(utkcontext_t *ctx);
static void GenerateExcitation(utkcontext_t *ctx, int Voiced, float * Window, int Interval);
static void Synthesize(utkcontext_t *ctx, unsigned Sample, unsigned Blocks);
static void RCtoLPC(const float *__restrict RC, float *__restrict LPC);
float UTKTable1[64];
uint8_t UTKTable2[512];
const uint8_t UTKTable3[29] = {8,7,8,7,2,2,2,3,3,4,4,3,3,5,5,4,4,6,6,5,5,7,7,6,6,8,8,7,7};
float UTKTable4[29];
static const float UTKCosine[64] = {
0,
-.99677598476409912109375, -.99032700061798095703125, -.983879029750823974609375, -.977430999279022216796875,
-.970982015132904052734375, -.964533984661102294921875, -.958085000514984130859375, -.9516370296478271484375,
-.930754005908966064453125, -.904959976673126220703125, -.879167020320892333984375, -.853372991085052490234375,
-.827579021453857421875, -.801786005496978759765625, -.775991976261138916015625, -.75019800662994384765625,
-.724404990673065185546875, -.6986110210418701171875, -.6706349849700927734375, -.61904799938201904296875,
-.567460000514984130859375, -.515873014926910400390625, -.4642859995365142822265625, -.4126980006694793701171875,
-.361110985279083251953125, -.309523999691009521484375, -.257937014102935791015625, -.20634900033473968505859375,
-.1547619998455047607421875, -.10317499935626983642578125, -.05158700048923492431640625,
0,
+.05158700048923492431640625, +.10317499935626983642578125, +.1547619998455047607421875, +.20634900033473968505859375,
+.257937014102935791015625, +.309523999691009521484375, +.361110985279083251953125, +.4126980006694793701171875,
+.4642859995365142822265625, +.515873014926910400390625, +.567460000514984130859375, +.61904799938201904296875,
+.6706349849700927734375, +.6986110210418701171875, +.724404990673065185546875, +.75019800662994384765625,
+.775991976261138916015625, +.801786005496978759765625, +.827579021453857421875, +.853372991085052490234375,
+.879167020320892333984375, +.904959976673126220703125, +.930754005908966064453125, +.9516370296478271484375,
+.958085000514984130859375, +.964533984661102294921875, +.970982015132904052734375, +.977430999279022216796875,
+.983879029750823974609375, +.99032700061798095703125, +.99677598476409912109375
};
static const uint8_t UTKCodebook[512] = {
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 25,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 0,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 26,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 2,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3
};
static const uint8_t UTKCodeSkips[29] = {8,7,8,7,2,2,2,3,3,4,4,3,3,5,5,4,4,6,6,5,5,7,7,6,6,8,8,7,7};
int utk_read_header(utkheader_t * UTKHeader, const uint8_t * Buffer, size_t FileSize)
{
@ -83,258 +137,199 @@ int utk_read_header(utkheader_t * UTKHeader, const uint8_t * Buffer, size_t File
return 1;
}
int utk_decode(const uint8_t *__restrict InBuffer, uint8_t *__restrict OutBuffer, size_t Frames){
utkparams_t p;
int utk_decode(const uint8_t *__restrict InBuffer, uint8_t *__restrict OutBuffer, size_t InSize, size_t Samples){
utkcontext_t p;
p.InData = InBuffer;
SetUTKParameters(&p);
p.InDataEnd = InBuffer + InSize;
InitUTKParameters(&p);
while(Frames){
int i, BlockSize = min(Frames, 432);
DecompressBlock(&p);
while(Samples){
int i, BlockSize = min(Samples, 432);
DecodeFrame(&p);
for(i=0; i<BlockSize; i++){
int value = round(p.DecompressedBlock[i]);
if(value < -32768)
value = -32768;
else if(value > 32767)
value = 32767;
int value = round(p.DecompressedFrame[i]);
value = clamp(value, -32768, 32767);
write_uint16(OutBuffer, value);
OutBuffer += 2;
}
Frames -= BlockSize;
Samples -= BlockSize;
}
return 1;
}
void UTKGenerateTables(void){
/* Call once per runtime */
int i;
static uint8_t ReadBits(utkcontext_t *ctx, uint8_t bits){
unsigned value = ctx->UnreadBitsValue & (255>>(8-bits));
ctx->UnreadBitsValue >>= bits;
ctx->UnreadBitsCount -= bits;
/* UTKTable1 */
UTKTable1[0] = 0;
for(i=-31; i<32; i++){
int s = (i>=0) ? 1 : -1;
if (s*i<14) UTKTable1[i+32] = i*.051587f;
else if(s*i<25) UTKTable1[i+32] = i*.051587f/2 + s*.337503f;
else UTKTable1[i+32] = i*.051587f/8 + s*.796876f;
}
/* UTKTable2 */
for(i=0; i<512; i++){
switch(i%4){
case 0: UTKTable2[i] = 4; break;
case 1: UTKTable2[i] = (i<256) ? 6 : (11 + (i%8 > 4)); break;
case 2: UTKTable2[i] = (i<256) ? 5 : (7 + (i%8 > 4)); break;
case 3: {
const uint8_t l1[] = {9,15,13,19,10,16},
l2[] = {17,21,18,25,17,22,18,00,17,21,18,26,17,22,18,02,
23,27,24,01,23,28,24,03,23,27,24,01,23,28,24,03};
if(i%16 < 4) UTKTable2[i] = l1[0 + (i>256)];
else if(i%16 < 8) UTKTable2[i] = l1[2 + (i>256)] + (i%32 > 16);
else if(i%16 < 12) UTKTable2[i] = l1[4 + (i>256)];
else UTKTable2[i] = l2[i/16];
} break;
}
}
/* UTKTable4 */
UTKTable4[0] = 0;
for(i=0; i<7; i++){
UTKTable4[4*i+1] = -i;
UTKTable4[4*i+2] = +i;
UTKTable4[4*i+3] = -i;
UTKTable4[4*i+4] = +i;
}
}
static uint8_t ReadBits(utkparams_t *p, uint8_t bits){
unsigned value = p->UnreadBitsValue & (255>>(8-bits));
p->UnreadBitsValue >>= bits;
p->UnreadBitsCount -= bits;
if(p->UnreadBitsCount < 8){
p->UnreadBitsValue |= *(p->InData++) << p->UnreadBitsCount;
p->UnreadBitsCount += 8;
if(ctx->UnreadBitsCount < 8 && ctx->InData != ctx->InDataEnd){
ctx->UnreadBitsValue |= *(ctx->InData++) << ctx->UnreadBitsCount;
ctx->UnreadBitsCount += 8;
}
return value;
}
static void SetUTKParameters(utkparams_t *p){
/* Call once per file */
static void InitUTKParameters(utkcontext_t *ctx){
int i;
float s;
p->UnreadBitsValue = *(p->InData++);
p->UnreadBitsCount = 8;
p->UseLattice = (int)ReadBits(p, 1);
p->NoiseFloor = 32 - ReadBits(p, 4);
p->FixedCodebook[0] = (ReadBits(p, 4)+1)*8;
float base;
ctx->UnreadBitsValue = *(ctx->InData++);
ctx->UnreadBitsCount = 8;
ctx->HalvedExcitation = ReadBits(ctx, 1);
ctx->VoicedThreshold = 32 - ReadBits(ctx, 4);
ctx->InnovationPower[0] = (ReadBits(ctx, 4)+1) * 8; /* significand */
s = (float)ReadBits(p, 6)/1000 + 1.04;
base = 1.04f + (float)ReadBits(ctx, 6)/1000;
for(i=1; i<64; i++)
p->FixedCodebook[i] = p->FixedCodebook[i-1]*s;
ctx->InnovationPower[i] = ctx->InnovationPower[i-1]*base;
memset(p->ImpulseTrain, 0, 12*sizeof(float));
memset(p->R, 0, 12*sizeof(float));
memset(p->Delay, 0, 324*sizeof(float));
memset(ctx->RC, 0, 12*sizeof(float));
memset(ctx->History, 0, 12*sizeof(float));
memset(ctx->Delay, 0, 324*sizeof(float));
}
static void DecompressBlock(utkparams_t *p){
static void DecodeFrame(utkcontext_t *ctx){
int i,j;
float Window[118];
float Matrix[12];
float Excitation[118]; /* includes 5 0-valued samples to both the left and the right */
float RCDelta[12];
int Voiced = 0;
memset(&Window[0], 0, 5*sizeof(float));
memset(&Window[113], 0, 5*sizeof(float));
memset(&Excitation[0], 0, 5*sizeof(float));
memset(&Excitation[113], 0, 5*sizeof(float));
for(i=0; i<12; i++){
unsigned result = ReadBits(p, (i<4) ? 6 : 5);
if(i==0 && p->NoiseFloor > result) Voiced++;
Matrix[i] = (UTKTable1[result + ((i<4)?0:16)] - p->ImpulseTrain[i])/4;
unsigned result = ReadBits(ctx, (i<4) ? 6 : 5);
if(i==0 && result < ctx->VoicedThreshold) Voiced++;
RCDelta[i] = (UTKCosine[result + ((i<4)?0:16)] - ctx->RC[i])/4;
}
for(i=0; i<4; i++){
float PitchGain, InnovationGain;
int Phase = (int)ReadBits(p, 8);
PitchGain = (float)ReadBits(p, 4)/15;
InnovationGain = p->FixedCodebook[ReadBits(p, 6)];
int Phase = ReadBits(ctx, 8);
PitchGain = (float)ReadBits(ctx, 4)/15;
InnovationGain = ctx->InnovationPower[ReadBits(ctx, 6)];
if(!p->UseLattice){
LatticeFilter(p, Voiced, &Window[5], 1);
if(!ctx->HalvedExcitation){
GenerateExcitation(ctx, Voiced, &Excitation[5], 1);
}else{
int o = ReadBits(p, 1); /* Order */
int y = ReadBits(p, 1);
LatticeFilter(p, Voiced, &Window[5+o], 2);
/* Fill the excitation window with half as many samples and interpolate the rest */
int Alignment = ReadBits(ctx, 1); /* whether to fill the even or odd samples */
int FillWithZero = ReadBits(ctx, 1);
GenerateExcitation(ctx, Voiced, &Excitation[5+Alignment], 2);
if(y){
if(FillWithZero){
for(j=0; j<108; j+=2)
Window[6-o + j] = 0;
Excitation[5 + (1-Alignment) + j] = 0.0;
}else{
/* Vector quantization */
float *z = &Window[6-o];
for(j=0; j<54; j++, z+=2)
*z =
(z[-5]+z[+5]) * .0180326793f
- (z[-3]+z[+3]) * .1145915613f
+ (z[-1]+z[+1]) * .5973859429f;
/* Use sinc interpolation with 6 neighboring samples */
float *x = &Excitation[5 + (1-Alignment)];
for(j=0; j<54; j++, x+=2)
*x = (x[-1]+x[+1]) * .5973859429f
- (x[-3]+x[+3]) * .1145915613f
+ (x[-5]+x[+5]) * .0180326793f;
InnovationGain /= 2;
}
}
/* Excitation */
/* If 216-Phase is negative on the first subframe, it will read into RC and History
as the reference decoder does, which have been initialized to 0 in InitUTKParameters(). */
for(j=0; j<108; j++)
p->DecompressedBlock[108*i + j] = InnovationGain*Window[5+j] + PitchGain*p->Delay[216 - Phase + 108*i + j];
ctx->DecompressedFrame[108*i + j] = InnovationGain*Excitation[5+j] + PitchGain*ctx->Delay[108*i + j + (216-Phase)];
for(j=0; j<108; j++)
ctx->WhatIsThis[108*i + j] = PitchGain*ctx->Delay[108*i + j + (216-Phase)];
}
memcpy(p->Delay, &p->DecompressedBlock[108], 324*sizeof(float));
memcpy(ctx->Delay, &ctx->DecompressedFrame[108], 324*sizeof(float));
for(i=0; i<4; i++){
/* Linearly interpolate the reflection coefficients for the current subframe */
for(j=0; j<12; j++)
p->ImpulseTrain[j] += Matrix[j];
ctx->RC[j] += RCDelta[j];
Synthesize(p, i*12, (i!=3) ? 1 : 33);
Synthesize(ctx, i*12, (i!=3) ? 12 : 396);
}
}
static void LatticeFilter(utkparams_t *p, int Voiced, float * Window, int Interval){
static void GenerateExcitation(utkcontext_t *ctx, int Voiced, float * Window, int Interval){
if(Voiced){
int t = 0;
int Table = 0;
int i = 0;
while(i<108){
unsigned code = UTKTable2[(t<<8) | (p->UnreadBitsValue&0xFF)];
t = (code<2 || code>8);
ReadBits(p, UTKTable3[code]);
unsigned code = UTKCodebook[(Table<<8) | (ctx->UnreadBitsValue&0xFF)];
Table = (code<2 || code>8);
ReadBits(ctx, UTKCodeSkips[code]);
if(code >= 4){
Window[i] = UTKTable4[code];
/* Fill a sample with a value specified by the code; magnitude is limited to 6.0 */
Window[i] = (code-1)/4;
if(code&1)
Window[i] *= -1.0;
i += Interval;
}else{
if(code > 1){
int x = (int)ReadBits(p, 6)+7;
if(x > (108 - i)/Interval)
x = (108 - i)/Interval;
}else if(code >= 2){
/* Fill between 7 and 70 samples with 0s */
int x = ReadBits(ctx, 6) + 7;
x = min(x, (108 - i)/Interval);
while(x--){
Window[i] = 0;
Window[i] = 0.0;
i += Interval;
}
}else{
Window[i] = 7;
while(ReadBits(p, 1))
/* Fill a sample with a custom value with magnitude >= 7.0 */
Window[i] = 7.0;
while(ReadBits(ctx, 1))
Window[i]++;
if(!ReadBits(p, 1))
if(!ReadBits(ctx, 1))
Window[i] *= -1;
i += Interval;
}
}
}
}else{
/* Unvoiced signal; load noise */
/* Unvoiced: restrict all samples to 0.0, -2.0, or +2.0 without using the codebook */
int i;
for(i=0; i<108; i+=Interval){
uint8_t b;
switch(p->UnreadBitsValue & 3){
case 3:
Window[i] = 2.0;
b = 2;
break;
case 1:
Window[i] = -2.0;
b = 2;
break;
default:
Window[i] = 0.0;
b = 1;
}
ReadBits(p, b);
if(!ReadBits(ctx, 1)) Window[i] = 0.0;
else if(!ReadBits(ctx, 1)) Window[i] = -2.0;
else Window[i] = 2.0;
}
}
}
static void Synthesize(utkparams_t *p, unsigned Sample, unsigned Blocks){
float Residual[12];
unsigned Samples = Blocks*12;
static void Synthesize(utkcontext_t *ctx, size_t Sample, size_t Samples){
float LPC[12];
int offset = -1;
PredictionFilter(p->ImpulseTrain, Residual);
RCtoLPC(ctx->RC, LPC);
while(Samples--){
int i;
float x = p->DecompressedBlock[Sample];
for(i=0; i<12; i++){
if(++offset == 12) offset = 0;
x += p->R[offset] * Residual[i];
ctx->DecompressedFrame[Sample] += LPC[i] * ctx->History[offset];
}
p->R[offset--] = x;
p->DecompressedBlock[Sample++] = x;
ctx->History[offset--] = ctx->DecompressedFrame[Sample++];
}
}
static void PredictionFilter(const float *__restrict ImpulseTrain, float *__restrict Residual){
static void RCtoLPC(const float *__restrict RC, float *__restrict LPC){
int i,j;
float ResidualGain[12];
float ImpulseGain[12];
ImpulseGain[0] = 1;
memcpy(&ImpulseGain[1], ImpulseTrain, 11*sizeof(float));
float RCTemp[12], LPCTemp[12];
RCTemp[0] = 1.0;
memcpy(&RCTemp[1], RC, 11*sizeof(float));
for(i=0; i<12; i++){
float x = 0;
LPC[i] = 0.0;
for(j=11; j>=0; j--){
x -= ImpulseTrain[j] * ImpulseGain[j];
LPC[i] -= RC[j] * RCTemp[j];
if(j != 11)
ImpulseGain[j+1] = x*ImpulseTrain[j] + ImpulseGain[j];
RCTemp[j+1] = RCTemp[j] + RC[j] * LPC[i];
}
ImpulseGain[0] = x;
ResidualGain[i] = x;
RCTemp[0] = LPCTemp[i] = LPC[i];
for(j=0; j<i; j++)
x -= ResidualGain[i-j-1] * Residual[j];
Residual[i] = x;
LPC[i] -= LPCTemp[i-j-1] * LPC[j];
}
}

View file

@ -35,24 +35,24 @@ typedef struct
} utkheader_t;
typedef struct {
const uint8_t *InData;
const uint8_t *InData, *InDataEnd;
unsigned UnreadBitsValue, UnreadBitsCount;
int UseLattice;
unsigned NoiseFloor;
float FixedCodebook[64]; /* Fixed codebook gain matrix */
float ImpulseTrain[12]; /* Impulse train matrix */
float R[12]; /* Autocorrelation coefficient matrix */
int HalvedExcitation;
unsigned VoicedThreshold;
float InnovationPower[64];
float RC[12];
float History[12];
float Delay[324];
float DecompressedBlock[432];
} utkparams_t;
float DecompressedFrame[432];
float WhatIsThis[432];
} utkcontext_t;
#ifdef __cplusplus
extern "C" {
#endif
int utk_read_header(utkheader_t * UTKHeader, const uint8_t * Buffer, size_t FileSize);
int utk_decode(const uint8_t *__restrict InBuffer, uint8_t *__restrict OutBuffer, size_t Frames);
void UTKGenerateTables(void);
int utk_decode(const uint8_t *__restrict InBuffer, uint8_t *__restrict OutBuffer, size_t InSize, size_t Samples);
#ifdef __cplusplus
}

View file

@ -107,10 +107,8 @@ int main(int argc, char *argv[]){
if(WaveData == NULL)
Shutdown_M("Memory for this file could not be allocated");
UTKGenerateTables();
BeginningTime = clock();
if(!utk_decode(UTKData+32, WaveData+44, UTKHeader.Frames))
if(!utk_decode(UTKData+32, WaveData+44, FileSize-32, UTKHeader.Frames))
Shutdown_M("Memory for this file could not be allocated");
EndingTime = clock();

View file

@ -7,15 +7,4 @@ to complete the criteria for the next development phase, which can be found here
//----------//
Development Phase: Planning
Technical Preview 1
Schedule: (Not very subject to change)
1. Implement cst, uis, ini, and xml parsers with support for UTF-8 [20%]
2. Replicate functionality up until the login dialog [90%]
3. Implement the audio engine
4. Implement the OpenGL-based windowing system
5. Replicate character selection and creation features and the city selection dialog
6. Implement debug logging and support for configuration files
7. Implement the code needed to allow the game to read all necessary files from the TSOClient folder
8. Port the client to Linux
Tasks will be added for the server when the development phase reaches Alpha.

View file

@ -1,14 +1,3 @@
* Elf32_Word sh_name:
* Elf32_Word sh_type:
* Elf32_Word sh_flags:
* Elf32_Addr sh_addr:
* Elf32_Off sh_offset: (?? ?? ?? ??)
* Elf32_Word sh_size: (?? ?? ?? ??)
* Elf32_Word sh_link:
* Elf32_Word sh_info:
* Elf32_Word sh_addralign: 1 (01 00 00 00)
* Elf32_Word sh_entsize:
ELF Header:
* e_ident:
* Elf32_Word EI_MAG0: (7F 45 4C 46)

View file

@ -22,12 +22,13 @@
#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, 0x01, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 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,
@ -103,29 +104,35 @@ enum {
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 TextSection = {0,0,1024,"text"};
static ByteWriterContext SymbolTable = {0,0,1024,"symtab"};
static ByteWriterContext StringTable = {0,0,1024,"strtab"};
static ByteWriterContext RelocationTable = {0,0,1024,".rel.text"};
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(&SymbolTable, StringTable.Position);
bw_write32(&SymbolTable, 0);
bw_write32(&SymbolTable, 0);
bw_write32(&SymbolTable, 18);
bw_write_string(&StringTable, Name);
return SymbolTable.Data + SymbolTable.Position - 16;
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<SymbolTable.Position; p+=16){
if(!strcmp((char*) StringTable.Data + read_uint32(SymbolTable.Data + p), Name)){
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 SymbolTable.Data + p;
return Section[SymbolTable].Data + p;
}
}
@ -233,7 +240,7 @@ static __inline void parser_add_symbol(const ParserContext *pc){
path[txt], pc->Line, pc->Col, pc->Token, path[txt], read_uint32(Symbol+8));
}else Symbol = add_symbol(pc->Token);
write_uint32(Symbol+4, TextSection.Position);
write_uint32(Symbol+4, Section[Text].Position);
write_uint32(Symbol+8, pc->Line);
}
@ -244,8 +251,8 @@ static __inline void parser_add_reference(const ParserContext *pc){
if(!find_symbol_by_name(pc->Token, &SymbolIndex))
add_symbol(pc->Token);
bw_write32(&RelocationTable, TextSection.Position);
bw_write32(&RelocationTable, (SymbolIndex<<8)|0x02);
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){
@ -339,10 +346,8 @@ static void Shutdown(){
free(path[i]);
free(data[i]);
}
free(TextSection.Data);
free(SymbolTable.Data);
free(StringTable.Data);
free(RelocationTable.Data);
for(i=0; i<SectionCount; i++)
free(Section[i].Data);
if(hFile)
fclose(hFile);
}
@ -351,12 +356,14 @@ int main(int argc, char *argv[]){
unsigned i;
int SimsVersion = 0;
int overwrite = 0;
size_t filesize[filecount-1] = {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"
@ -398,7 +405,7 @@ int main(int argc, char *argv[]){
if(path[out] == NULL){
int length = strlen(path[txt]);
path[out] = malloc(max(length+1, 5));
path[out] = malloc(max(length+1, 3));
strcpy(path[out], path[txt]);
strcpy(path[out] + max(length-4, 0), ".o");
}
@ -409,53 +416,28 @@ int main(int argc, char *argv[]){
for(i=0; i<filecount-1; i++){
size_t bytestransferred;
if(!path[i]) continue;
hFile = fopen(path[i], "rb");
if(hFile == NULL){
if(i != txt){
fprintf(stderr, "%sCould not open file: %s.\n", "hitasm: Warning: ", path[i]);
continue;
}else
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){
fclose(hFile); hFile = NULL;
if(i != txt){
fprintf(stderr, "%sFile is invalid: %s.\n", "hitasm: Warning: ", path[i]);
continue;
}else
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){
fclose(hFile); hFile = NULL;
if(i != txt){
fprintf(stderr, "%sCould not allocate memory for file: %s.\n", "hitasm: Warning: ", path[i]);
continue;
}else
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]){
free(data[i]); data[i] = NULL;
if(i != txt){
fprintf(stderr, "%sCould not read file: %s.\n", "hitasm: Warning: ", path[i]);
continue;
}else
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
*/
@ -480,26 +462,25 @@ int main(int argc, char *argv[]){
** Perform the assembly
*/
TextSection.Data = malloc(TextSection.Size);
SymbolTable.Data = malloc(SymbolTable.Size);
StringTable.Data = malloc(StringTable.Size);
RelocationTable.Data = malloc(RelocationTable.Size);
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(&SymbolTable, SymbolTableHeader, sizeof(SymbolTableHeader));
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(&StringTable, '\0');
bw_write_string(&StringTable, path[txt] + slash);
bw_write8(&Section[StringTable], '\0');
bw_write_string(&Section[StringTable], path[txt] + slash);
while(parser_next_token(&pc, TK_CROSSLINES)){
printf("Token: %s\n", pc.Token);
/* Unimplemented commands */
if(!strcmp(pc.Token, "BASEID_TRACKDATA") || !strcmp(pc.Token, "INCLUDE")
|| !strcmp(pc.Token, "include") || !strcmp(pc.Token, "INIFILE")
@ -542,16 +523,14 @@ int main(int argc, char *argv[]){
/* declare bytes (db and dd pseudo-instructions) */
do {
if(pc.Token[0] != '#')
bw_write8(&TextSection, read_integer(&pc, 0x000000FF));
bw_write8(&Section[Text], read_integer(&pc, 0x000000FF));
else
bw_write32(&TextSection, read_constant(&pc, 0, 0xFFFFFFFF));
bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF));
} while(parser_next_token(&pc, 0));
continue;
}else{
const char * InstructionName = pc.Token;
printf("Instruction: %s\n", pc.Token);
bw_write8(&TextSection, opcode);
bw_write8(&Section[Text], opcode);
for(i=0; (operands >>= 4) != 0; i++){
int type = operands & 15;
@ -564,19 +543,17 @@ int main(int argc, char *argv[]){
path[txt], pc.Line, pc.Col, pc.Token, InstructionName, position[j], position[i]);
}
printf("Operand: %s\n", pc.Token);
if(type == o_byte)
bw_write8(&TextSection, read_constant(&pc, 0, 0x000000FF));
bw_write8(&Section[Text], read_constant(&pc, 0, 0x000000FF));
else if(type == o_dword)
bw_write32(&TextSection, read_constant(&pc, 0, 0xFFFFFFFF));
bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF));
else if(type == o_address)
bw_write32(&TextSection, read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF));
bw_write32(&Section[Text], read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF));
else if(type == o_variable)
bw_write8(&TextSection, read_variable(&pc, Variables, VariableCount));
bw_write8(&Section[Text], read_variable(&pc, Variables, VariableCount));
else if(type == o_jump){
/* TODO: Change this */
bw_write32(&TextSection, read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF));
bw_write32(&Section[Text], read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF));
}
}
}
@ -589,31 +566,35 @@ int main(int argc, char *argv[]){
Shutdown_M("%s:%u:%u: error: expected ']' for end of BINARY section before end of file\n",
path[txt], pc.Line, pc.Col);
for(i=48+8; i<SymbolTable.Position; i+=16)
write_uint32(SymbolTable.Data + i, 0);
/****
** Prepare and write out the ELF object header and all sections
*/
i = 304;
write_uint32(ObjectHeader + 120, i);
write_uint32(ObjectHeader + 124, TextSection.Position);
i += TextSection.Position;
write_uint32(ObjectHeader + 160, i);
write_uint32(ObjectHeader + 164, sizeof(SHStringTable));
i += sizeof(SHStringTable);
write_uint32(ObjectHeader + 200, i);
write_uint32(ObjectHeader + 204, SymbolTable.Position);
i += SymbolTable.Position;
write_uint32(ObjectHeader + 240, i);
write_uint32(ObjectHeader + 244, StringTable.Position);
i += StringTable.Position;
write_uint32(ObjectHeader + 280, i);
write_uint32(ObjectHeader + 284, RelocationTable.Position);
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(TextSection.Data, 1, TextSection.Position, hFile);
fwrite(Section[Text].Data, 1, Section[Text].Position, hFile);
fwrite(SHStringTable, 1, sizeof(SHStringTable), hFile);
fwrite(SymbolTable.Data, 1, SymbolTable.Position, hFile);
fwrite(StringTable.Data, 1, StringTable.Position, hFile);
fwrite(RelocationTable.Data, 1, RelocationTable.Position, hFile);
for(i=1; i<SectionCount; i++)
fwrite(Section[i].Data, 1, Section[i].Position, hFile);
Shutdown();

View file

@ -96,7 +96,7 @@ typedef struct {
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))))
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;
}

View file

@ -16,11 +16,71 @@
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"
int main(){
printf("Usage: hitld [-f] [-hsm infile.hsm] [-hot infile.hot]\n"
" outfile.hit INFILES\n"
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"
@ -29,4 +89,86 @@ int main(){
"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;
}