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; return NULL;
} }
static bool generated = false; if(!utk_decode(InData+32, OutData, FileSize-32, UTKHeader.Frames)){
if(!generated){
UTKGenerateTables();
generated = true;
}
if(!utk_decode(InData+32, OutData, UTKHeader.Frames)){
free(OutData); free(OutData);
return NULL; return NULL;
} }

View file

@ -20,9 +20,31 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <unistd.h>
#include <time.h> #include <time.h>
#include <sys/stat.h>
#include <errno.h>
#include "far.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 { enum {
profile_ts1 = 1, profile_ts1 = 1,
profile_tso, profile_tso,
@ -32,6 +54,122 @@ enum {
profile_ts3 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 main(int argc, char *argv[]){
int profile = 0, overwrite = 0; int profile = 0, overwrite = 0;
const char * InFile = "", * OutDirectory; const char * InFile = "", * OutDirectory;
@ -62,7 +200,7 @@ int main(int argc, char *argv[]){
"\n" "\n"
"Report bugs to <X-Fi6@phppoll.org>.\n" "Report bugs to <X-Fi6@phppoll.org>.\n"
"farextract and libfar are maintained by the Niotso project.\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; return 0;
} }
@ -141,6 +279,11 @@ int main(int argc, char *argv[]){
return -1; return -1;
} }
if(chdir(OutDirectory) != 0){
fprintf(stderr, "%sOutput directory '%s' does not exist.", "farextract: error: ", OutDirectory);
return -1;
}
if(ArchiveType != FAR_TYPE_PERSIST){ if(ArchiveType != FAR_TYPE_PERSIST){
FAREntryNode * EntryNode; FAREntryNode * EntryNode;
unsigned file = 0, filescount; unsigned file = 0, filescount;
@ -177,28 +320,35 @@ int main(int argc, char *argv[]){
** Extract each entry ** Extract each entry
*/ */
for(EntryNode = FARFileInfo->FirstEntry; EntryNode; EntryNode = EntryNode->NextEntry){ for(EntryNode = FARFileInfo->FirstEntry; EntryNode; EntryNode = EntryNode->NextEntry){
char destination[256]; char destbuffer[256], * destination = destbuffer;
file++; file++;
if(EntryNode->Entry.Filename) if(EntryNode->Entry.Filename)
sprintf(destination, "%s/%s", OutDirectory, EntryNode->Entry.Filename); destination = EntryNode->Entry.Filename;
else else
sprintf(destination, "%s/%08x-%08x-%08x.dat", OutDirectory, sprintf(destbuffer, "%s%08x%s", groupid_to_dir(EntryNode->Entry.GroupID),
EntryNode->Entry.TypeID, EntryNode->Entry.GroupID, EntryNode->Entry.FileID); EntryNode->Entry.FileID, typeid_to_ext(EntryNode->Entry.TypeID));
if(!far_read_entry_data(FARFileInfo, &(EntryNode->Entry), ArchiveData)){ if(far_read_entry_data(FARFileInfo, &(EntryNode->Entry), ArchiveData)){
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, /* Decompression, if any, was successful */
"entry data is corrupt", EntryNode->Entry.Filename); 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; continue;
} }
/* Decompression, if any, was successful */
if(mkpath(destination) != 0){
fprintf(stderr, "%sCould not create path '%s'.", "farextract: error: ", destination);
return -1;
}
if(!overwrite){ if(!overwrite){
hFile = fopen(destination, "rb"); hFile = fopen(destination, "rb");
if(hFile != NULL){ if(hFile != NULL){
/* File exists */ /* File exists */
fclose(hFile); 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) if(EntryNode->Entry.DecompressedData != EntryNode->Entry.CompressedData)
libfar_free(EntryNode->Entry.DecompressedData); libfar_free(EntryNode->Entry.DecompressedData);
continue; continue;
@ -206,19 +356,15 @@ int main(int argc, char *argv[]){
} }
hFile = fopen(destination, "wb"); hFile = fopen(destination, "wb");
if(hFile == NULL){ 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) if(EntryNode->Entry.DecompressedData != EntryNode->Entry.CompressedData)
libfar_free(EntryNode->Entry.DecompressedData); libfar_free(EntryNode->Entry.DecompressedData);
continue; continue;
} }
if(EntryNode->Entry.Filename) printf(" (%u/%u) %s (Group ID: 0x%08X, File ID: 0x%08X, Type ID: 0x%08X) (%u bytes)\n", file, filescount,
printf(" (%u/%u) %s (%u bytes)\n", file, filescount, destination, EntryNode->Entry.GroupID, EntryNode->Entry.FileID, EntryNode->Entry.TypeID,
EntryNode->Entry.Filename, EntryNode->Entry.DecompressedSize); EntryNode->Entry.DecompressedSize);
else
printf(" (%u/%u) %08x-%08x-%08x (%u bytes)\n", file, filescount,
EntryNode->Entry.TypeID, EntryNode->Entry.GroupID, EntryNode->Entry.FileID,
EntryNode->Entry.DecompressedSize);
fwrite(EntryNode->Entry.DecompressedData, 1, EntryNode->Entry.DecompressedSize, hFile); fwrite(EntryNode->Entry.DecompressedData, 1, EntryNode->Entry.DecompressedSize, hFile);
fclose(hFile); fclose(hFile);
@ -232,8 +378,6 @@ int main(int argc, char *argv[]){
}else{ }else{
/* Persist file */ /* Persist file */
PersistFile * PersistInfo; PersistFile * PersistInfo;
char destination[256];
sprintf(destination, "%s/%s.out", OutDirectory, InFile);
/**** /****
** Load header information ** Load header information
@ -261,7 +405,7 @@ int main(int argc, char *argv[]){
EndingTime = clock(); EndingTime = clock();
if(!overwrite){ if(!overwrite){
hFile = fopen(destination, "rb"); hFile = fopen(InFile, "rb");
if(hFile != NULL){ if(hFile != NULL){
/* File exists */ /* File exists */
fclose(hFile); fclose(hFile);
@ -270,7 +414,7 @@ int main(int argc, char *argv[]){
return -1; return -1;
} }
} }
hFile = fopen(destination, "wb"); hFile = fopen(InFile, "wb");
if(hFile == NULL){ if(hFile == NULL){
fprintf(stderr, "%sCould not open.", "farextract: error: "); fprintf(stderr, "%sCould not open.", "farextract: error: ");
libfar_free(PersistInfo->DecompressedData); libfar_free(PersistInfo->DecompressedData);

View file

@ -35,21 +35,75 @@
#ifndef round #ifndef round
#define round(x) ((x) >= 0 ? (x)+0.5 : (x)-0.5) #define round(x) ((x) >= 0 ? (x)+0.5 : (x)-0.5)
#endif #endif
#ifndef clamp
#define clamp(x, low, high) ((x) < low ? low : (x) > high ? high : (x))
#endif
#ifndef min #ifndef min
#define min(x, y) ((x) < (y) ? (x) : (y)) #define min(x, y) ((x) < (y) ? (x) : (y))
#endif #endif
static uint8_t ReadBits(utkparams_t *p, uint8_t bits); static uint8_t ReadBits(utkcontext_t *ctx, uint8_t bits);
static void SetUTKParameters(utkparams_t *p); static void InitUTKParameters(utkcontext_t *ctx);
static void DecompressBlock(utkparams_t *p); static void DecodeFrame(utkcontext_t *ctx);
static void LatticeFilter(utkparams_t *p, int Voiced, float * Window, int Interval); static void GenerateExcitation(utkcontext_t *ctx, int Voiced, float * Window, int Interval);
static void Synthesize(utkparams_t *p, unsigned Sample, unsigned Blocks); static void Synthesize(utkcontext_t *ctx, unsigned Sample, unsigned Blocks);
static void PredictionFilter(const float *__restrict ImpulseTrain, float *__restrict Residual); static void RCtoLPC(const float *__restrict RC, float *__restrict LPC);
float UTKTable1[64]; static const float UTKCosine[64] = {
uint8_t UTKTable2[512]; 0,
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}; -.99677598476409912109375, -.99032700061798095703125, -.983879029750823974609375, -.977430999279022216796875,
float UTKTable4[29]; -.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) 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; return 1;
} }
int utk_decode(const uint8_t *__restrict InBuffer, uint8_t *__restrict OutBuffer, size_t Frames){ int utk_decode(const uint8_t *__restrict InBuffer, uint8_t *__restrict OutBuffer, size_t InSize, size_t Samples){
utkparams_t p; utkcontext_t p;
p.InData = InBuffer; p.InData = InBuffer;
SetUTKParameters(&p); p.InDataEnd = InBuffer + InSize;
InitUTKParameters(&p);
while(Frames){ while(Samples){
int i, BlockSize = min(Frames, 432); int i, BlockSize = min(Samples, 432);
DecompressBlock(&p); DecodeFrame(&p);
for(i=0; i<BlockSize; i++){ for(i=0; i<BlockSize; i++){
int value = round(p.DecompressedBlock[i]); int value = round(p.DecompressedFrame[i]);
value = clamp(value, -32768, 32767);
if(value < -32768)
value = -32768;
else if(value > 32767)
value = 32767;
write_uint16(OutBuffer, value); write_uint16(OutBuffer, value);
OutBuffer += 2; OutBuffer += 2;
} }
Frames -= BlockSize; Samples -= BlockSize;
} }
return 1; return 1;
} }
void UTKGenerateTables(void){ static uint8_t ReadBits(utkcontext_t *ctx, uint8_t bits){
/* Call once per runtime */ unsigned value = ctx->UnreadBitsValue & (255>>(8-bits));
int i; ctx->UnreadBitsValue >>= bits;
ctx->UnreadBitsCount -= bits;
/* UTKTable1 */ if(ctx->UnreadBitsCount < 8 && ctx->InData != ctx->InDataEnd){
UTKTable1[0] = 0; ctx->UnreadBitsValue |= *(ctx->InData++) << ctx->UnreadBitsCount;
for(i=-31; i<32; i++){ ctx->UnreadBitsCount += 8;
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;
} }
return value; return value;
} }
static void SetUTKParameters(utkparams_t *p){ static void InitUTKParameters(utkcontext_t *ctx){
/* Call once per file */
int i; int i;
float s; float base;
p->UnreadBitsValue = *(p->InData++); ctx->UnreadBitsValue = *(ctx->InData++);
p->UnreadBitsCount = 8; ctx->UnreadBitsCount = 8;
p->UseLattice = (int)ReadBits(p, 1); ctx->HalvedExcitation = ReadBits(ctx, 1);
p->NoiseFloor = 32 - ReadBits(p, 4); ctx->VoicedThreshold = 32 - ReadBits(ctx, 4);
p->FixedCodebook[0] = (ReadBits(p, 4)+1)*8; 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++) 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(ctx->RC, 0, 12*sizeof(float));
memset(p->R, 0, 12*sizeof(float)); memset(ctx->History, 0, 12*sizeof(float));
memset(p->Delay, 0, 324*sizeof(float)); memset(ctx->Delay, 0, 324*sizeof(float));
} }
static void DecompressBlock(utkparams_t *p){ static void DecodeFrame(utkcontext_t *ctx){
int i,j; int i,j;
float Window[118]; float Excitation[118]; /* includes 5 0-valued samples to both the left and the right */
float Matrix[12]; float RCDelta[12];
int Voiced = 0; int Voiced = 0;
memset(&Window[0], 0, 5*sizeof(float)); memset(&Excitation[0], 0, 5*sizeof(float));
memset(&Window[113], 0, 5*sizeof(float)); memset(&Excitation[113], 0, 5*sizeof(float));
for(i=0; i<12; i++){ for(i=0; i<12; i++){
unsigned result = ReadBits(p, (i<4) ? 6 : 5); unsigned result = ReadBits(ctx, (i<4) ? 6 : 5);
if(i==0 && p->NoiseFloor > result) Voiced++; if(i==0 && result < ctx->VoicedThreshold) Voiced++;
Matrix[i] = (UTKTable1[result + ((i<4)?0:16)] - p->ImpulseTrain[i])/4; RCDelta[i] = (UTKCosine[result + ((i<4)?0:16)] - ctx->RC[i])/4;
} }
for(i=0; i<4; i++){ for(i=0; i<4; i++){
float PitchGain, InnovationGain; float PitchGain, InnovationGain;
int Phase = (int)ReadBits(p, 8); int Phase = ReadBits(ctx, 8);
PitchGain = (float)ReadBits(p, 4)/15; PitchGain = (float)ReadBits(ctx, 4)/15;
InnovationGain = p->FixedCodebook[ReadBits(p, 6)]; InnovationGain = ctx->InnovationPower[ReadBits(ctx, 6)];
if(!p->UseLattice){ if(!ctx->HalvedExcitation){
LatticeFilter(p, Voiced, &Window[5], 1); GenerateExcitation(ctx, Voiced, &Excitation[5], 1);
}else{ }else{
int o = ReadBits(p, 1); /* Order */ /* Fill the excitation window with half as many samples and interpolate the rest */
int y = ReadBits(p, 1); int Alignment = ReadBits(ctx, 1); /* whether to fill the even or odd samples */
LatticeFilter(p, Voiced, &Window[5+o], 2); int FillWithZero = ReadBits(ctx, 1);
GenerateExcitation(ctx, Voiced, &Excitation[5+Alignment], 2);
if(y){ if(FillWithZero){
for(j=0; j<108; j+=2) for(j=0; j<108; j+=2)
Window[6-o + j] = 0; Excitation[5 + (1-Alignment) + j] = 0.0;
}else{ }else{
/* Vector quantization */ /* Use sinc interpolation with 6 neighboring samples */
float *z = &Window[6-o]; float *x = &Excitation[5 + (1-Alignment)];
for(j=0; j<54; j++, z+=2) for(j=0; j<54; j++, x+=2)
*z = *x = (x[-1]+x[+1]) * .5973859429f
(z[-5]+z[+5]) * .0180326793f - (x[-3]+x[+3]) * .1145915613f
- (z[-3]+z[+3]) * .1145915613f + (x[-5]+x[+5]) * .0180326793f;
+ (z[-1]+z[+1]) * .5973859429f;
InnovationGain /= 2; 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++) 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++){ for(i=0; i<4; i++){
/* Linearly interpolate the reflection coefficients for the current subframe */
for(j=0; j<12; j++) 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){ if(Voiced){
int t = 0; int Table = 0;
int i = 0; int i = 0;
while(i<108){ while(i<108){
unsigned code = UTKTable2[(t<<8) | (p->UnreadBitsValue&0xFF)]; unsigned code = UTKCodebook[(Table<<8) | (ctx->UnreadBitsValue&0xFF)];
t = (code<2 || code>8); Table = (code<2 || code>8);
ReadBits(p, UTKTable3[code]); ReadBits(ctx, UTKCodeSkips[code]);
if(code >= 4){ 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; i += Interval;
}else{ }else if(code >= 2){
if(code > 1){ /* Fill between 7 and 70 samples with 0s */
int x = (int)ReadBits(p, 6)+7; int x = ReadBits(ctx, 6) + 7;
if(x > (108 - i)/Interval) x = min(x, (108 - i)/Interval);
x = (108 - i)/Interval;
while(x--){
Window[i] = 0;
i += Interval;
}
}else{
Window[i] = 7;
while(ReadBits(p, 1))
Window[i]++;
if(!ReadBits(p, 1))
Window[i] *= -1;
while(x--){
Window[i] = 0.0;
i += Interval; i += Interval;
} }
}else{
/* Fill a sample with a custom value with magnitude >= 7.0 */
Window[i] = 7.0;
while(ReadBits(ctx, 1))
Window[i]++;
if(!ReadBits(ctx, 1))
Window[i] *= -1;
i += Interval;
} }
} }
}else{ }else{
/* Unvoiced signal; load noise */ /* Unvoiced: restrict all samples to 0.0, -2.0, or +2.0 without using the codebook */
int i; int i;
for(i=0; i<108; i+=Interval){ for(i=0; i<108; i+=Interval){
uint8_t b; if(!ReadBits(ctx, 1)) Window[i] = 0.0;
switch(p->UnreadBitsValue & 3){ else if(!ReadBits(ctx, 1)) Window[i] = -2.0;
case 3: else Window[i] = 2.0;
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);
} }
} }
} }
static void Synthesize(utkparams_t *p, unsigned Sample, unsigned Blocks){ static void Synthesize(utkcontext_t *ctx, size_t Sample, size_t Samples){
float Residual[12]; float LPC[12];
unsigned Samples = Blocks*12;
int offset = -1; int offset = -1;
PredictionFilter(p->ImpulseTrain, Residual); RCtoLPC(ctx->RC, LPC);
while(Samples--){ while(Samples--){
int i; int i;
float x = p->DecompressedBlock[Sample];
for(i=0; i<12; i++){ for(i=0; i<12; i++){
if(++offset == 12) offset = 0; if(++offset == 12) offset = 0;
x += p->R[offset] * Residual[i]; ctx->DecompressedFrame[Sample] += LPC[i] * ctx->History[offset];
} }
p->R[offset--] = x; ctx->History[offset--] = ctx->DecompressedFrame[Sample++];
p->DecompressedBlock[Sample++] = x;
} }
} }
static void PredictionFilter(const float *__restrict ImpulseTrain, float *__restrict Residual){ static void RCtoLPC(const float *__restrict RC, float *__restrict LPC){
int i,j; int i,j;
float ResidualGain[12]; float RCTemp[12], LPCTemp[12];
float ImpulseGain[12]; RCTemp[0] = 1.0;
ImpulseGain[0] = 1; memcpy(&RCTemp[1], RC, 11*sizeof(float));
memcpy(&ImpulseGain[1], ImpulseTrain, 11*sizeof(float));
for(i=0; i<12; i++){ for(i=0; i<12; i++){
float x = 0; LPC[i] = 0.0;
for(j=11; j>=0; j--){ for(j=11; j>=0; j--){
x -= ImpulseTrain[j] * ImpulseGain[j]; LPC[i] -= RC[j] * RCTemp[j];
if(j != 11) if(j != 11)
ImpulseGain[j+1] = x*ImpulseTrain[j] + ImpulseGain[j]; RCTemp[j+1] = RCTemp[j] + RC[j] * LPC[i];
} }
ImpulseGain[0] = x; RCTemp[0] = LPCTemp[i] = LPC[i];
ResidualGain[i] = x;
for(j=0; j<i; j++) for(j=0; j<i; j++)
x -= ResidualGain[i-j-1] * Residual[j]; LPC[i] -= LPCTemp[i-j-1] * LPC[j];
Residual[i] = x;
} }
} }

View file

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

View file

@ -107,10 +107,8 @@ int main(int argc, char *argv[]){
if(WaveData == NULL) if(WaveData == NULL)
Shutdown_M("Memory for this file could not be allocated"); Shutdown_M("Memory for this file could not be allocated");
UTKGenerateTables();
BeginningTime = clock(); 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"); Shutdown_M("Memory for this file could not be allocated");
EndingTime = clock(); EndingTime = clock();

View file

@ -311,7 +311,7 @@ static void AdvanceFrame(Skeleton_t& Skeleton, Animation_t& Animation, float Tim
Bone.Rotation.y = w1*Rotation.y + w2*NextRotation.y; Bone.Rotation.y = w1*Rotation.y + w2*NextRotation.y;
Bone.Rotation.z = w1*Rotation.z + w2*NextRotation.z; Bone.Rotation.z = w1*Rotation.z + w2*NextRotation.z;
Bone.Rotation.w = w1*Rotation.w + w2*NextRotation.w; Bone.Rotation.w = w1*Rotation.w + w2*NextRotation.w;
Normalize(&Bone.Rotation); Normalize(&Bone.Rotation);
} }
} }

View file

@ -7,15 +7,4 @@ to complete the criteria for the next development phase, which can be found here
//----------// //----------//
Development Phase: Planning Tasks will be added for the server when the development phase reaches Alpha.
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

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: ELF Header:
* e_ident: * e_ident:
* Elf32_Word EI_MAG0: (7F 45 4C 46) * Elf32_Word EI_MAG0: (7F 45 4C 46)

View file

@ -22,12 +22,13 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
#define HITASM_HEADERS
#include "hitutils.h" #include "hitutils.h"
static uint8_t ObjectHeader[] = { static uint8_t ObjectHeader[] = {
0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 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, 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, 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 CN_LABELONLY = 1
}; };
enum Sections {
Text, SymbolTable, StringTable, RelocationTable, SectionCount
};
static FILE *hFile = NULL; static FILE *hFile = NULL;
static char *path[filecount] = {NULL}; static char *path[filecount] = {NULL};
static uint8_t *data[filecount] = {NULL}; static uint8_t *data[filecount] = {NULL};
static ByteWriterContext TextSection = {0,0,1024,"text"}; static ByteWriterContext Section[] = {
static ByteWriterContext SymbolTable = {0,0,1024,"symtab"}; {0,0,1024,"text"},
static ByteWriterContext StringTable = {0,0,1024,"strtab"}; {0,0,1024,"symtab"},
static ByteWriterContext RelocationTable = {0,0,1024,".rel.text"}; {0,0,1024,"strtab"},
{0,0,1024,".rel.text"}
};
static uint8_t * add_symbol(const char * Name){ static uint8_t * add_symbol(const char * Name){
bw_write32(&SymbolTable, StringTable.Position); bw_write32(&Section[SymbolTable], Section[StringTable].Position);
bw_write32(&SymbolTable, 0); bw_write32(&Section[SymbolTable], 0);
bw_write32(&SymbolTable, 0); bw_write32(&Section[SymbolTable], 0);
bw_write32(&SymbolTable, 18); bw_write32(&Section[SymbolTable], 18);
bw_write_string(&StringTable, Name); bw_write_string(&Section[StringTable], Name);
return SymbolTable.Data + SymbolTable.Position - 16; return Section[SymbolTable].Data + Section[SymbolTable].Position - 16;
} }
static uint8_t * find_symbol_by_name(const char * Name, uint32_t * SymbolIndex){ static uint8_t * find_symbol_by_name(const char * Name, uint32_t * SymbolIndex){
uint32_t p; uint32_t p;
for(p=48; p<SymbolTable.Position; p+=16){ for(p=48; p<Section[SymbolTable].Position; p+=16){
if(!strcmp((char*) StringTable.Data + read_uint32(SymbolTable.Data + p), Name)){ if(!strcmp((char*) Section[StringTable].Data + read_uint32(Section[SymbolTable].Data + p), Name)){
if(SymbolIndex) *SymbolIndex = p>>4; 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)); path[txt], pc->Line, pc->Col, pc->Token, path[txt], read_uint32(Symbol+8));
}else Symbol = add_symbol(pc->Token); }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); 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)) if(!find_symbol_by_name(pc->Token, &SymbolIndex))
add_symbol(pc->Token); add_symbol(pc->Token);
bw_write32(&RelocationTable, TextSection.Position); bw_write32(&Section[RelocationTable], Section[Text].Position);
bw_write32(&RelocationTable, (SymbolIndex<<8)|0x02); bw_write32(&Section[RelocationTable], (SymbolIndex<<8)|0x02);
} }
static uint32_t read_integer(const ParserContext *pc, uint32_t maxval){ static uint32_t read_integer(const ParserContext *pc, uint32_t maxval){
@ -339,10 +346,8 @@ static void Shutdown(){
free(path[i]); free(path[i]);
free(data[i]); free(data[i]);
} }
free(TextSection.Data); for(i=0; i<SectionCount; i++)
free(SymbolTable.Data); free(Section[i].Data);
free(StringTable.Data);
free(RelocationTable.Data);
if(hFile) if(hFile)
fclose(hFile); fclose(hFile);
} }
@ -351,12 +356,14 @@ int main(int argc, char *argv[]){
unsigned i; unsigned i;
int SimsVersion = 0; int SimsVersion = 0;
int overwrite = 0; int overwrite = 0;
size_t filesize[filecount-1] = {0}; size_t filesize[filecount-1];
unsigned slash; unsigned slash;
const variable_t * Variables; const variable_t * Variables;
size_t VariableCount; size_t VariableCount;
ParserContext pc; ParserContext pc;
int InBinary = 0; int InBinary = 0;
uint8_t * SectionHeader;
size_t SectionOffset;
if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){
printf("Usage: hitasm [-ts1|-tso] [-f] [-o outfile.o] infile.txt\n" 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){ if(path[out] == NULL){
int length = strlen(path[txt]); 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], path[txt]);
strcpy(path[out] + max(length-4, 0), ".o"); 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++){ for(i=0; i<filecount-1; i++){
size_t bytestransferred; size_t bytestransferred;
if(!path[i]) continue;
hFile = fopen(path[i], "rb"); hFile = fopen(path[i], "rb");
if(hFile == NULL){ if(hFile == NULL)
if(i != txt){ Shutdown_M("%sCould not open file: %s.\n", "hitasm: Error: ", path[i]);
fprintf(stderr, "%sCould not open file: %s.\n", "hitasm: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sCould not open file: %s.\n", "hitasm: Error: ", path[i]);
}
fseek(hFile, 0, SEEK_END); fseek(hFile, 0, SEEK_END);
filesize[i] = ftell(hFile); filesize[i] = ftell(hFile);
if(filesize[i] == 0 || filesize[i] == SIZE_MAX){ if(filesize[i] == 0 || filesize[i] == SIZE_MAX)
fclose(hFile); hFile = NULL; Shutdown_M("%sFile is invalid: %s.\n", "hitasm: Error: ", path[i]);
if(i != txt){
fprintf(stderr, "%sFile is invalid: %s.\n", "hitasm: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sFile is invalid: %s.\n", "hitasm: Error: ", path[i]);
}
data[i] = malloc(filesize[i]+1); data[i] = malloc(filesize[i]+1);
if(data[i] == NULL){ if(data[i] == NULL)
fclose(hFile); hFile = NULL; Shutdown_M("%sCould not allocate memory for file: %s.\n", "hitasm: Error: ", path[i]);
if(i != txt){
fprintf(stderr, "%sCould not allocate memory for file: %s.\n", "hitasm: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sCould not allocate memory for file: %s.\n", "hitasm: Error: ", path[i]);
}
fseek(hFile, 0, SEEK_SET); fseek(hFile, 0, SEEK_SET);
bytestransferred = fread(data[i], 1, filesize[i], hFile); bytestransferred = fread(data[i], 1, filesize[i], hFile);
fclose(hFile); hFile = NULL; fclose(hFile); hFile = NULL;
if(bytestransferred != filesize[i]){ if(bytestransferred != filesize[i])
free(data[i]); data[i] = NULL; Shutdown_M("%sCould not read file: %s.\n", "hitasm: Error: ", path[i]);
if(i != txt){
fprintf(stderr, "%sCould not read file: %s.\n", "hitasm: Warning: ", path[i]);
continue;
}else
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 */ data[i][filesize[i]++] = '\0'; /* add a null character to the end of the file */
} }
/**** /****
** Open the output file for writing ** Open the output file for writing
*/ */
@ -480,26 +462,25 @@ int main(int argc, char *argv[]){
** Perform the assembly ** Perform the assembly
*/ */
TextSection.Data = malloc(TextSection.Size); for(i=0; i<SectionCount; i++){
SymbolTable.Data = malloc(SymbolTable.Size); Section[i].Data = malloc(Section[Text].Size);
StringTable.Data = malloc(StringTable.Size); if(!Section[i].Data)
RelocationTable.Data = malloc(RelocationTable.Size); Shutdown_M("%sCould not allocate memory for %s section.\n", "hitasm: Error: ", Section[i].Name);
}
pc.NextLine = 1; pc.NextLine = 1;
pc.NextCol = 1; pc.NextCol = 1;
pc.GaveLastToken = 1; pc.GaveLastToken = 1;
pc.brc.Data = data[txt]; pc.brc.Data = data[txt];
pc.brc.Size = filesize[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++) for(i=slash=0; path[txt][i]; i++)
if(path[txt][i] == '/' || path[txt][i] == '\\') slash = i+1; if(path[txt][i] == '/' || path[txt][i] == '\\') slash = i+1;
bw_write8(&StringTable, '\0'); bw_write8(&Section[StringTable], '\0');
bw_write_string(&StringTable, path[txt] + slash); bw_write_string(&Section[StringTable], path[txt] + slash);
while(parser_next_token(&pc, TK_CROSSLINES)){ while(parser_next_token(&pc, TK_CROSSLINES)){
printf("Token: %s\n", pc.Token);
/* Unimplemented commands */ /* Unimplemented commands */
if(!strcmp(pc.Token, "BASEID_TRACKDATA") || !strcmp(pc.Token, "INCLUDE") if(!strcmp(pc.Token, "BASEID_TRACKDATA") || !strcmp(pc.Token, "INCLUDE")
|| !strcmp(pc.Token, "include") || !strcmp(pc.Token, "INIFILE") || !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) */ /* declare bytes (db and dd pseudo-instructions) */
do { do {
if(pc.Token[0] != '#') if(pc.Token[0] != '#')
bw_write8(&TextSection, read_integer(&pc, 0x000000FF)); bw_write8(&Section[Text], read_integer(&pc, 0x000000FF));
else else
bw_write32(&TextSection, read_constant(&pc, 0, 0xFFFFFFFF)); bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF));
} while(parser_next_token(&pc, 0)); } while(parser_next_token(&pc, 0));
continue; continue;
}else{ }else{
const char * InstructionName = pc.Token; const char * InstructionName = pc.Token;
bw_write8(&Section[Text], opcode);
printf("Instruction: %s\n", pc.Token);
bw_write8(&TextSection, opcode);
for(i=0; (operands >>= 4) != 0; i++){ for(i=0; (operands >>= 4) != 0; i++){
int type = operands & 15; 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]); path[txt], pc.Line, pc.Col, pc.Token, InstructionName, position[j], position[i]);
} }
printf("Operand: %s\n", pc.Token);
if(type == o_byte) 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) 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) 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) 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){ else if(type == o_jump){
/* TODO: Change this */ /* 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", Shutdown_M("%s:%u:%u: error: expected ']' for end of BINARY section before end of file\n",
path[txt], pc.Line, pc.Col); 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; for(i=48+8; i<Section[SymbolTable].Position; i+=16)
write_uint32(ObjectHeader + 120, i); write_uint32(Section[SymbolTable].Data + i, 0); /* clear the st_size field we used temporarily */
write_uint32(ObjectHeader + 124, TextSection.Position);
i += TextSection.Position; if(SimsVersion == VERSION_TSO)
write_uint32(ObjectHeader + 160, i); ObjectHeader[36]++; /* set the lsb of the processor flags to indicate that this code is for TSO */
write_uint32(ObjectHeader + 164, sizeof(SHStringTable));
i += sizeof(SHStringTable); for(i = 0, SectionHeader = ObjectHeader + 120, SectionOffset = 304; i < SectionCount; i++){
write_uint32(ObjectHeader + 200, i); write_uint32(SectionHeader + 0, SectionOffset);
write_uint32(ObjectHeader + 204, SymbolTable.Position); write_uint32(SectionHeader + 4, Section[i].Position);
i += SymbolTable.Position; SectionHeader += 40;
write_uint32(ObjectHeader + 240, i); SectionOffset += Section[i].Position;
write_uint32(ObjectHeader + 244, StringTable.Position);
i += StringTable.Position; if(i == 0){
write_uint32(ObjectHeader + 280, i); write_uint32(SectionHeader + 0, SectionOffset);
write_uint32(ObjectHeader + 284, RelocationTable.Position); write_uint32(SectionHeader + 4, sizeof(SHStringTable));
SectionHeader += 40;
SectionOffset += sizeof(SHStringTable);
}
}
fwrite(ObjectHeader, 1, sizeof(ObjectHeader), hFile); 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(SHStringTable, 1, sizeof(SHStringTable), hFile);
fwrite(SymbolTable.Data, 1, SymbolTable.Position, hFile); for(i=1; i<SectionCount; i++)
fwrite(StringTable.Data, 1, StringTable.Position, hFile); fwrite(Section[i].Data, 1, Section[i].Position, hFile);
fwrite(RelocationTable.Data, 1, RelocationTable.Position, hFile);
Shutdown(); Shutdown();

View file

@ -96,7 +96,7 @@ typedef struct {
static address_t * add_address(addresslist_t * List){ static address_t * add_address(addresslist_t * List){
if(List->Count*sizeof(address_t) == List->SizeAllocated){ if(List->Count*sizeof(address_t) == List->SizeAllocated){
void * ptr; 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: "); Shutdown_M("%sCould not allocate memory for address list.\n", "hitdump: Error: ");
List->Entries = ptr; List->Entries = ptr;
} }

View file

@ -16,17 +16,159 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.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();
int main(){
printf("Usage: hitld [-f] [-hsm infile.hsm] [-hot infile.hot]\n"
" outfile.hit INFILES\n"
"Link object files produced by hitasm into a HIT binary, and\n"
"relink the game's HSM and HOT files.\n"
"Use -f to force overwriting without confirmation.\n"
"\n"
"Report bugs to <X-Fi6@phppoll.org>.\n"
"hitutils is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\n");
return 0; return 0;
} }