mirror of
https://github.com/simtactics/niotso.git
synced 2025-03-15 08:11:22 +00:00
New farextract naming logic and a rewritten UTalk decompressor
This commit is contained in:
parent
558726a9f1
commit
66ce473514
11 changed files with 584 additions and 352 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
Server/TODO
13
Server/TODO
|
@ -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
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue