| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385 |
- #include <cstdlib>
- #include <cstring>
- #include <cmath>
- #include <fstream>
- #include <iostream>
- #include <iomanip>
- #include <sstream>
- #include <algorithm>
- #include <string>
- #include <list>
- #include <vector>
- // TODO: This tool needs the "libupse" fixing so it can build properly
- #include <zlib.h>
- extern "C"
- {
- //#include <upse.h>
- }
- #include <tinyxml/tinyxml.h>
- #ifdef QGears_SOUND
- #include <vorbis/codec.h>
- #include <vorbis/vorbisenc.h>
- #endif
- #include <boost/filesystem/path.hpp>
- #include <boost/filesystem/operations.hpp>
- #include "typedefs.h"
- #include "walkthrough.hh"
- using namespace std;
- using namespace boost::filesystem;
- #define SIZEOF(arg__) (sizeof(arg__) / sizeof(arg__[0]))
- #define MATH_ROUNDUP(x, base) (((x) + ((base)-1)) & ~((base)-1))
- #ifdef linux
- #define ESC_RED "\33[31m"
- #define ESC_GREEN "\33[32m"
- #define ESC_ORANGE "\33[33m"
- #define ESC_BLUE "\33[34m"
- #define ESC_MAGENTA "\33[35m"
- #define ESC_CYAN "\33[36m"
- #define ESC_GREY "\33[37m"
- #define ESC_RESET "\33[0m"
- #else
- #define ESC_RED ""
- #define ESC_GREEN ""
- #define ESC_ORANGE ""
- #define ESC_BLUE ""
- #define ESC_MAGENTA ""
- #define ESC_CYAN ""
- #define ESC_GREY ""
- #define ESC_RESET ""
- #endif
- static const u32 g_PSF_FREQUENCY = 44100;
- static const u32 g_PSF_CHANNELS_COUNT = 2;
- static const u32 g_TIME_DELTA = g_PSF_FREQUENCY * 5 / 100; // 0.05 seconds
- static const char *g_MAGIC = "AKAO";
- static const string g_AKAO_EXT("akao");
- static const string g_PSF_EXT("psf");
- static const string g_PCM_EXT("pcm");
- static const string g_OGG_EXT("ogg");
- static const string g_PSF_DRV_FN("ff7_scus94163_psf_v10.bin");
- static const u32 g_PSF_DRV_OFFSET = 0x1a8800;
- static const u32 g_PSF_SEQ_OFFSET = 0x1c0800;
- static const u32 g_PSX_TEXT_START = 0x800;
- static const u32 g_PSX_TEXT_SIZE = 0x1e0000;
- static const u32 g_PSX_PSF_JUMP_OFFSET = 0x1960;
- static const u32 g_PSX_PSF_JUMP_DATA = 0x0806e00f;
- static const u32 g_AKAO_OPCODE_TABLE_OFFSET = 0x3a028;
- static const string g_CONFIG_FN("config.xml");
- static const struct
- {
- u8 id;
- u8 count;
- } g_OP_PARAMS[] =
- {
- {0xa0, 0}, {0xa6, 0}, {0xa7, 0}, {0xb3, 0}, {0xb6, 0}, {0xba, 0}, {0xbe, 0}, {0xc2, 0},
- {0xc3, 0}, {0xc4, 0}, {0xc5, 0}, {0xc6, 0}, {0xc7, 0}, {0xc8, 0}, {0xca, 0}, {0xcb, 0},
- {0xcc, 0}, {0xcd, 0}, {0xd0, 0}, {0xd1, 0}, {0xd4, 0}, {0xd5, 0}, {0xd6, 0}, {0xd7, 0},
- {0xdb, 0}, {0xe0, 0}, {0xe1, 0}, {0xe2, 0}, {0xe3, 0}, {0xe4, 0}, {0xe5, 0}, {0xe6, 0},
- {0xe7, 0}, {0xed, 0}, {0xf3, 0}, {0xf5, 0}, {0xf9, 0}, {0xfa, 0}, {0xfb, 0}, {0xfc, 0},
- {0xff, 0}, {0xa1, 1}, {0xa2, 1}, {0xa3, 1}, {0xa5, 1}, {0xa8, 1}, {0xaa, 1}, {0xac, 1},
- {0xad, 1}, {0xae, 1}, {0xaf, 1}, {0xb1, 1}, {0xb2, 1}, {0xb5, 1}, {0xb7, 1}, {0xb9, 1},
- {0xbb, 1}, {0xbd, 1}, {0xbf, 1}, {0xc0, 1}, {0xc1, 1}, {0xc9, 1}, {0xce, 1}, {0xcf, 1},
- {0xd2, 1}, {0xd3, 1}, {0xd8, 1}, {0xd9, 1}, {0xda, 1}, {0xdc, 1}, {0xf2, 1}, {0xf6, 1},
- {0xf8, 1}, {0xa4, 2}, {0xa9, 2}, {0xab, 2}, {0xb0, 2}, {0xbc, 2}, {0xdd, 2}, {0xde, 2},
- {0xdf, 2}, {0xe8, 2}, {0xea, 2}, {0xec, 2}, {0xee, 2}, {0xf4, 2}, {0xf7, 2}, {0xfd, 2},
- {0xfe, 2}, {0xb4, 3}, {0xb8, 3}, {0xe9, 3}, {0xeb, 3}, {0xef, 3}, {0xf0, 3}, {0xf1, 3}
- };
- static const struct OpcodeTable
- {
- u8 data[0x100];
- OpcodeTable()
- {
- memset((char *)data, 0x00, sizeof(data));
- for(u32 i = 0; i < SIZEOF(g_OP_PARAMS); ++i)
- data[g_OP_PARAMS[i].id] = g_OP_PARAMS[i].count;
- }
- } g_OPCODE_TABLE;
- static const struct PatchEntry
- {
- string filename;
- u32 offset;
- }g_PSX_PSF_PATCHES[4] =
- {
- {"SOUND/INSTR.ALL" , 0x000e0800},
- {"SOUND/INSTR.DAT" , 0x00156800},
- {"SOUND/INSTR2.ALL", 0x00158800},
- {"SOUND/INSTR2.DAT", 0x00196800}
- };
- enum ReaderState
- {
- RS_MAGIC,
- RS_FOUND,
- RS_COUNT
- };
- struct PsxExeHeader
- {
- char magic[8];
- u32 text;
- u32 data;
- u32 pc0;
- u32 gp0;
- u32 t_addr;
- u32 t_size;
- u32 d_addr;
- u32 d_size;
- u32 b_addr;
- u32 b_size;
- u32 s_addr;
- u32 s_size;
- u32 saved_sp;
- u32 saved_fp;
- u32 saved_gp;
- u32 saved_ra;
- u32 saved_s0;
- };
- struct PsfHeader
- {
- char magic[3];
- u8 version;
- u32 reserved_size;
- u32 program_size;
- u32 program_crc;
- };
- struct AkaoHeader
- {
- u8 magic[4];
- u16 id;
- u16 length;
- u16 reverb_mode;
- struct TimeStamp
- {
- u8 year;
- u8 month;
- u8 day;
- u8 hour;
- u8 minute;
- u8 second;
- //} __attribute__((packed)) timestamp;
- //} __attribute__((packed));
- };
- };
- struct Channel
- {
- u32 index;
- u8 *stream;
- u32 length;
- u32 length_file;
- u32 loop_start;
- u32 loop_end;
- bool control_only;
- };
- struct ChannelParam
- {
- u32 attack_size;
- u32 loop_size;
- };
- struct VorbisEncoderContext
- {
- #ifdef QGears_SOUND
- vorbis_info vi;
- vorbis_dsp_state vds;
- vorbis_comment vc;
- vorbis_block vb;
- ogg_stream_state os;
- ogg_page og;
- ogg_packet op;
- #endif
- ofstream ofs_file;
- };
- struct PsfContext
- {
- u32 samples_decoded;
- u32 samples_total;
- ofstream of_pcm;
- VorbisEncoderContext venc_ctx;
- };
- template <typename T>
- string to_string(T arg)
- {
- string result;
- stringstream sS;
- sS << arg;
- sS >> result;
- return result;
- }
- vector<Channel> akao_frame_parse(u8 *, u32);
- void akao_frame_print(const vector<Channel> &, u8 *, u32);
- bool akao_timestamp_valid(const AkaoHeader::TimeStamp &);
- bool operator<(const Channel &, const Channel &);
- void akao_extract_cb(void *, path);
- void file_list_cb(void *, path);
- void files_delete_duplicates(list<path> &);
- void audio_tracks_create(path, list<path> &, path, path, path, const char *, float, bool, bool);
- bool lzs_detect(const u8 *, u32);
- u8 *lzs_extract(u32 &, const u8 *, u32);
- void psf_decode(path, path, bool, char *, u32, u32, u32, float);
- u32 psf_decode_time(char *, u32);
- void psf_write(string, const u8 *, u32);
- char *psf_alloc(u32 &, const u8 *, u32);
- void psf_free(char *);
- void upse_write_audio_cb(unsigned char *, long, void *);
- void upse_breakpoint_cb(u32, void *);
- static void *upse_open_cb(char *, char *);
- static size_t upse_read_cb(void *, size_t, size_t, void *);
- static int upse_seek_cb(void *, long, int);
- static int upse_close_cb(void *);
- int vorbis_encoder_init(VorbisEncoderContext &, path, u32, float);
- void vorbis_encoder_free(VorbisEncoderContext &);
- int vorbis_encoder_update(VorbisEncoderContext &, s16 *, u32, u32, bool);
- string str_lowercase(string);
- streampos file_get_size(ifstream &);
- void file_read(u8 *r_data, string a_path);
- void print_channel_stat(ChannelParam);
- string samples_to_time(u32);
- /*
- static upse_iofuncs_t upse_iofuncs =
- {
- upse_open_cb,
- upse_read_cb,
- upse_seek_cb,
- upse_close_cb
- };
- */
- // this is needed as workaround for dumb libupse io callbacks
- u32 g_psf_data_size;
- u32 g_psf_data_offset;
- int main(int argc, char *argv[])
- {
- bool print_usage = true;
- bool mode_print_frame = false;
- if(argc == 2)
- {
- path argument(argv[1]);
- // passed akao frame
- string extension(argument.extension().string());
- if(extension.size() && string(extension, 1, argument.extension().string().size() - 1) == g_AKAO_EXT)
- {
- mode_print_frame = true;
- print_usage = false;
- }
- // passed game directory
- else if(is_directory(argument) && exists(argument))
- {
- print_usage = false;
- }
- }
- if(print_usage)
- {
- cout << "usage: " << argv[0] << " <game_directory | akao_frame_file>" << endl;
- return 1;
- }
- string argument(argv[1]);
- if(mode_print_frame)
- {
- ifstream iF(argument.c_str(), ios::binary);
- if(iF.is_open())
- {
- u32 frame_size = static_cast<u32>(file_get_size(iF));
- u8 *frame = new u8[frame_size];
- iF.read((char *)frame, frame_size);
- iF.close();
- vector<Channel> channels = akao_frame_parse(frame, frame_size);
- akao_frame_print(channels, frame, frame_size);
- delete [] frame;
- }
- else
- {
- cout << "error: unable to open file " << argument << endl;
- }
- }
- else
- {
- TiXmlDocument config_xml(g_CONFIG_FN.c_str());
- if(config_xml.LoadFile())
- {
- TiXmlElement *config_node = config_xml.RootElement();
- // dump akao frames to directory
- cout << "searching data files for akao frames:" << endl;
- create_directory(g_AKAO_EXT);
- walkthrough(argument, akao_extract_cb, NULL);
- cout << endl;
- // remove duplicates
- cout << "removing duplicates:" << endl;
- list<path> akao_file_list;
- walkthrough(g_AKAO_EXT, file_list_cb, &akao_file_list);
- akao_file_list.sort();
- files_delete_duplicates(akao_file_list);
- cout << endl;
- // create audio tracks
- int dump_psf = 0;
- int dump_pcm = 0;
- float ogg_quality = 0.4f;
- if(config_node->QueryIntAttribute("dump_psf", &dump_psf) != TIXML_SUCCESS)
- cout << "error: dump_psf isn't integer" << endl;
- if(config_node->QueryIntAttribute("dump_pcm", &dump_pcm) != TIXML_SUCCESS)
- cout << "error: dump_pcm isn't integer" << endl;
- if(config_node->QueryFloatAttribute("ogg_quality", &ogg_quality) != TIXML_SUCCESS)
- cout << "error: ogg_quality isn't float" << endl;
- cout << "creating audio tracks:" << endl;
- audio_tracks_create(argument, akao_file_list,
- config_node->Attribute("output_directory"),
- config_node->Attribute("music_xml"),
- config_node->Attribute("music_directory"),
- config_node->Attribute("custom_bios"),
- ogg_quality, dump_psf > 0, dump_pcm > 0);
- cout << endl;
- }
- else
- {
- cout << "error: unable to load configuration file" << endl;
- return 1;
- }
- }
- return 0;
- }
- vector<Channel> akao_frame_parse(u8 *a_frame, u32 a_frame_size)
- {
- vector<Channel> channels;
- AkaoHeader &header = *((AkaoHeader *)a_frame);
- u32 file_index = sizeof(header);
- u32 channels_mask = *(u32 *)&a_frame[file_index];
- file_index += sizeof(u32);
- Channel ch;
- for(u32 i = 0; i < sizeof(u32) * 8; ++i)
- if(channels_mask & (1 << i))
- {
- ch.index = i;
- ch.stream = &a_frame[file_index + sizeof(u16) + *(u16 *)&a_frame[file_index]];
- file_index += sizeof(u16);
- ch.control_only = true;
- channels.push_back(ch);
- }
- sort(channels.begin(), channels.end());
- // loop start and end detection
- for(u32 i = 0; i < channels.size(); ++i)
- {
- u32 state = 0;
- u32 c8_start = channels[i].loop_start = channels[i].length = channels[i].length_file =
- (i < channels.size() - 1 ? channels[i + 1].stream
- : &a_frame[min((u32)(sizeof(header) + header.length), (u32)a_frame_size)]) - channels[i].stream;
- for(u32 j = 0; j < channels[i].length; ++j)
- {
- if(state)
- {
- --state;
- }
- else
- {
- if(channels[i].stream[j] < 0xa0)
- channels[i].control_only = false;
- state = g_OPCODE_TABLE.data[channels[i].stream[j]];
- channels[i].loop_end = j;
- switch(channels[i].stream[j])
- {
- case 0xc8:
- if(c8_start == channels[i].length)
- c8_start = j;
- break;
- case 0xee:
- channels[i].loop_start = j + 3 + *(s16 *)&channels[i].stream[j + 1];
- channels[i].length = j + 1 + state;
- break;
- case 0xa0:
- channels[i].length = j + 1 + state;
- break;
- case 0xca:
- channels[i].loop_start = c8_start;
- channels[i].length = j + 1 + state;
- }
- }
- }
- }
- return channels;
- }
- void akao_frame_print(const vector<Channel> &a_channels, u8 *a_frame, u32 a_frame_size)
- {
- AkaoHeader &header = *((AkaoHeader *)a_frame);
- cout << setfill('0');
- cout << dec;
- cout << "id: " << ESC_GREEN << header.id << ESC_RESET << endl;
- cout << "size: " << ESC_GREEN << header.length << ESC_RESET << endl;
- cout << "reverb mode: " << ESC_GREEN << header.reverb_mode << ESC_RESET << endl;
- cout << hex;
- #ifdef QGears_SOUND
- cout << "date: " << ESC_GREEN
- << setw(2) << (int)header.timestamp.year << '/'
- << setw(2) << (int)header.timestamp.month << '/'
- << setw(2) << (int)header.timestamp.day << ' '
- << setw(2) << (int)header.timestamp.hour << ':'
- << setw(2) << (int)header.timestamp.minute << ':'
- << setw(2) << (int)header.timestamp.second
- << ESC_RESET << endl;
- #endif
- cout << "channels count: " << dec << ESC_GREEN << a_channels.size() << ESC_RESET << hex << endl;
- for(u32 i = 0; i < a_channels.size(); ++i)
- {
- u32 state = 0;
- const char *color = ESC_RED;
- cout << "channel " << dec << (a_channels[i].index + 1) << hex << " [size: "
- << dec << a_channels[i].length << hex << "]: " << endl;
- for(u32 j = 0; j < a_channels[i].length; ++j)
- {
- if(state)
- {
- color = ESC_ORANGE;
- --state;
- }
- else
- {
- color = a_channels[i].stream[j] < 0xa0 ? ESC_MAGENTA : ESC_RED;
- if(j == a_channels[i].loop_start || j == a_channels[i].loop_end)
- color = ESC_CYAN;
- state = g_OPCODE_TABLE.data[a_channels[i].stream[j]];
- }
- cout << color << setw(2) << (int)a_channels[i].stream[j] << ' ';
- }
- cout << ESC_RESET << endl;
- }
- }
- bool akao_timestamp_valid(const AkaoHeader::TimeStamp &a_timestamp)
- {
- static const u8 LIMIT_YEAR = 0x99;
- static const u8 LIMIT_MONTH = 0x12;
- static const u8 LIMIT_DAY = 0x31;
- static const u8 LIMIT_HOUR = 0x23;
- static const u8 LIMIT_MINUTE = 0x59;
- static const u8 LIMIT_SECOND = 0x59;
- return a_timestamp.year <= LIMIT_YEAR &&
- a_timestamp.month <= LIMIT_MONTH &&
- a_timestamp.day <= LIMIT_DAY &&
- a_timestamp.hour <= LIMIT_HOUR &&
- a_timestamp.minute <= LIMIT_MINUTE &&
- a_timestamp.second <= LIMIT_SECOND;
- }
- bool operator<(const Channel &a_ch1, const Channel &a_ch2)
- {
- return a_ch1.stream < a_ch2.stream;
- }
- void akao_extract_cb(void *a_data, path a_file)
- {
- ifstream iF(a_file.string().c_str(), ios::binary);
- if(!iF.is_open())
- {
- cout << "error: unable to open " << a_file << endl;
- return;
- }
- // read full file to memory
- u32 buffer_size = static_cast<u32>(file_get_size(iF));
- u8 *buffer = new u8[buffer_size];
- iF.read((char *)buffer, buffer_size);
- iF.close();
- cout << "searching in " << a_file << "... " << endl;
- // detect and extract if compressed
- if(lzs_detect(buffer, buffer_size))
- {
- u8 *buffer_old = buffer;
- buffer = lzs_extract(buffer_size, buffer_old, buffer_size);
- delete [] buffer_old;
- }
- cout << setfill('0');
- ReaderState state = RS_MAGIC;
- u32 magic_pos = 0;
- u32 input_offset = 0;
- int akao_counter = 0;
- while(input_offset < buffer_size)
- {
- switch(state)
- {
- case RS_MAGIC:
- magic_pos = buffer[input_offset++] == g_MAGIC[magic_pos] ? magic_pos + 1 : 0;
- if(magic_pos >= strlen(g_MAGIC))
- state = RS_FOUND;
- break;
- case RS_FOUND:
- {
- // fill akao header
- AkaoHeader header;
- memcpy(header.magic, g_MAGIC, strlen(g_MAGIC));
- memcpy(&header.id, &buffer[input_offset], sizeof(header) - sizeof(header.magic));
- #ifdef QGears_SOUND
- if(akao_timestamp_valid(header.timestamp))
- {
- input_offset += sizeof(header) - sizeof(header.magic);
- ++akao_counter;
- // construct save path
- string output_fn(a_file.filename().string());
- size_t dot_pos;
- while((dot_pos = output_fn.find_last_of(".")) != string::npos)
- output_fn.replace(dot_pos, 1, "_");
- output_fn += "_" + to_string(akao_counter);
- output_fn = str_lowercase(output_fn);
- path output_path(g_AKAO_EXT / path(output_fn));
- output_path.replace_extension(g_AKAO_EXT);
- ofstream oF;
- oF.open(output_path.string().c_str(), ios::binary);
- if(oF.is_open())
- {
- cout << "\tfound id 0x" << hex << setw(2) << header.id << dec << ", dumping to " << output_path << "... ";
- oF.write((char *)&header, sizeof(header));
- oF.write((char *)&buffer[input_offset], header.length);
- input_offset += header.length;
- oF.close();
- cout << "done" << endl;
- }
- else
- {
- cout << "error: unable to open " << output_path << endl;
- return;
- }
- }
- #endif
- magic_pos = 0;
- state = RS_MAGIC;
- }
- break;
- default:
- ;
- }
- }
- delete [] buffer;
- }
- void file_list_cb(void *a_data, path a_file)
- {
- list<path> &file_list = *(list<path> *)a_data;
- file_list.push_back(a_file);
- }
- void files_delete_duplicates(list<path> &r_file_list)
- {
- for(list<path>::iterator it = r_file_list.begin(); it != r_file_list.end(); ++it)
- {
- ifstream iF1(it->string().c_str(), ios::binary);
- if(!iF1.is_open())
- {
- cout << "error: unable to open " << *it << endl;
- continue;
- }
- u32 buffer1_size = static_cast<u32>(file_get_size(iF1));
- u8 *buffer1 = new u8[buffer1_size];
- iF1.read((char *)buffer1, buffer1_size);
- iF1.close();
- list<path>::iterator jt = it;
- ++jt;
- while(jt != r_file_list.end())
- {
- bool erased = false;
- ifstream iF2(jt->string().c_str(), ios::binary);
- if(iF2.is_open())
- {
- u32 buffer2_size = static_cast<u32>(file_get_size(iF2));
- if(buffer1_size == buffer2_size)
- {
- u8 *buffer2 = new u8[buffer2_size];
- iF2.read((char *)buffer2, buffer2_size);
- if(!memcmp(buffer1, buffer2, buffer1_size))
- {
- cout << "\t file " << *it << " equal to " << *jt << ", removing the latter" << endl;
- remove(path(*jt));
- jt = r_file_list.erase(jt);
- erased = true;
- }
- delete [] buffer2;
- }
- }
- else
- {
- cout << "error: unable to open " << *jt << endl;
- }
- if(!erased)
- ++jt;
- }
- delete [] buffer1;
- }
- }
- void audio_tracks_create(path a_game_path, list<path> &a_file_list, path a_output_path, path a_music_xml_fn,
- path a_music_path, const char *a_bios_fn, float a_ogg_quality, bool a_dump_psf, bool a_dump_pcm)
- {
- /*
- cout << "creating psf driver template... ";
- // read full file to memory
- u32 psf_driver_size = g_PSX_TEXT_START + g_PSX_TEXT_SIZE;
- u8 *psf_driver = new u8[psf_driver_size];
- memset(psf_driver, 0, psf_driver_size);
- file_read(psf_driver, (a_game_path / "SCUS_941.63").string());
- // convert psx executable to psf driver
- PsxExeHeader &header = *(PsxExeHeader *)psf_driver;
- // set maximal possible size to executable
- header.t_size = g_PSX_TEXT_SIZE;
- // set jump offset to psf driver
- *(u32 *)&psf_driver[g_PSX_PSF_JUMP_OFFSET] = g_PSX_PSF_JUMP_DATA;
- // inject sound data to executable
- for(u32 i = 0; i < SIZEOF(g_PSX_PSF_PATCHES); ++i)
- file_read(&psf_driver[g_PSX_PSF_PATCHES[i].offset], (a_game_path / g_PSX_PSF_PATCHES[i].filename).string());
- // inject psf driver payload
- file_read(&psf_driver[g_PSF_DRV_OFFSET], g_PSF_DRV_FN);
- cout << "done" << endl;
- if(a_bios_fn != NULL && strlen(a_bios_fn))
- {
- cout << "setting custom bios " << a_bios_fn << "... ";
- upse_set_custom_bios(a_bios_fn);
- cout << "done" << endl;
- }
- create_directory(a_output_path);
- create_directory(a_output_path / a_music_path);
- // create xml structure
- TiXmlDocument music_xml;
- music_xml.LinkEndChild(new TiXmlDeclaration("1.0", "utf-8", ""));
- TiXmlElement *music_xml_root = new TiXmlElement("musics");
- music_xml.LinkEndChild(music_xml_root);
- for(list<path>::iterator it = a_file_list.begin(); it != a_file_list.end(); ++it)
- {
- // read file
- ifstream iF(it->string().c_str(), ios::binary);
- if(!iF.is_open())
- {
- cout << "error: unable to open file " << *it << endl;
- continue;
- }
- cout << "processing " << *it << "... " << endl;
- u32 file_size = file_get_size(iF);
- u8 *file_data = new u8[file_size];
- iF.read((char *)file_data, file_size);
- iF.close();
- // analyze channels and get loop and total channel times
- vector<Channel> channels = akao_frame_parse(file_data, file_size);
- vector<ChannelParam> channel_params;
- for(u32 i = 0; i < channels.size(); ++i)
- {
- cout << "\tchannel " << setw(2) << channels[i].index + 1;
- if(channels[i].control_only)
- {
- cout << " contains only control opcodes, skipping" << endl;
- continue;
- }
- cout << " detecting properties... ";
- u32 time_loop = 0;
- u8 opcode_backup;
- bool loop_enabled = channels[i].loop_start != channels[i].length_file;
- // if track is looped, calculate channel loop start time
- if(loop_enabled)
- {
- // place a0 opcode to loop start, place breakpoint at a0 and get time
- u8 &loop_start = channels[i].stream[channels[i].loop_start];
- opcode_backup = loop_start;
- // patch opcode, write psf and get time
- loop_start = 0xa0;
- memcpy(&psf_driver[g_PSF_SEQ_OFFSET], file_data, file_size);
- u32 psf_data_size;
- char *psf_data = psf_alloc(psf_data_size, psf_driver, psf_driver_size);
- memset(&psf_driver[g_PSF_SEQ_OFFSET], 0, file_size);
- time_loop = psf_decode_time(psf_data, psf_data_size);
- psf_free(psf_data);
- // restore previous opcode
- loop_start = opcode_backup;
- }
- // calculate channel end time
- u8 &loop_end = channels[i].stream[channels[i].loop_end];
- opcode_backup = loop_end;
- // patch opcode, write psf and get time
- loop_end = 0xa0;
- memcpy(&psf_driver[g_PSF_SEQ_OFFSET], file_data, file_size);
- u32 psf_data_size;
- char *psf_data = psf_alloc(psf_data_size, psf_driver, psf_driver_size);
- memset(&psf_driver[g_PSF_SEQ_OFFSET], 0, file_size);
- u32 time_end = psf_decode_time(psf_data, psf_data_size);
- psf_free(psf_data);
- // restore previous opcode
- loop_end = opcode_backup;
- ChannelParam ch_param;
- if(loop_enabled)
- {
- ch_param.attack_size = time_loop;
- ch_param.loop_size = time_end - time_loop;
- }
- else
- {
- ch_param.attack_size = time_end;
- ch_param.loop_size = 0;
- }
- channel_params.push_back(ch_param);
- cout << "attack/loop/total: ";
- print_channel_stat(ch_param);
- cout << endl;
- }
- // if full track isn't empty
- if(channel_params.size())
- {
- // find maximal attack and loop sizes
- u32 attack_max_index = 0;
- u32 loop_max_index = 0;
- for(u32 i = 0; i < channel_params.size(); ++i)
- {
- if(channel_params[i].attack_size > channel_params[attack_max_index].attack_size)
- attack_max_index = i;
- if(channel_params[i].loop_size > channel_params[loop_max_index].loop_size)
- loop_max_index = i;
- }
- // take average loop size
- u32 loop_sum = 0;
- u32 loop_count = 0;
- for(u32 i = 0; i < channel_params.size(); ++i)
- {
- if(abs((s64)channel_params[i].loop_size -
- (s64)channel_params[loop_max_index].loop_size) <= g_TIME_DELTA)
- {
- loop_sum += channel_params[i].loop_size;
- ++loop_count;
- }
- }
- ChannelParam ch_final;
- ch_final.attack_size = channel_params[attack_max_index].attack_size;
- ch_final.loop_size = loop_sum / loop_count;
- cout << "final attack/loop/total: ";
- print_channel_stat(ch_final);
- cout << endl;
- // decode psf to wav
- memcpy(&psf_driver[g_PSF_SEQ_OFFSET], file_data, file_size);
- u32 psf_data_size;
- char *psf_data = psf_alloc(psf_data_size, psf_driver, psf_driver_size);
- if(a_dump_psf)
- {
- path psf_fn(a_output_path / a_music_path / it->filename());
- psf_fn.replace_extension(g_PSF_EXT);
- cout << psf_fn << endl;
- ofstream oF(psf_fn.string().c_str(), ios::binary);
- if(oF.is_open())
- {
- cout << "\twriting file " << psf_fn << "... ";
- oF.write(psf_data, psf_data_size);
- cout << "done" << endl;
- }
- else
- {
- cout << "error: unable to open " << psf_fn << endl;
- }
- }
- memset(&psf_driver[g_PSF_SEQ_OFFSET], 0, file_size);
- path ogg_fn(a_music_path / it->filename());
- ogg_fn.replace_extension(g_OGG_EXT);
- path raw_fn(a_music_path / it->filename());
- raw_fn.replace_extension(g_PCM_EXT);
- // add xml record about track
- TiXmlElement *xml_node = new TiXmlElement("music");
- music_xml_root->LinkEndChild(xml_node);
- xml_node->SetAttribute("name", ogg_fn.stem().string() );
- xml_node->SetAttribute("file_name", ogg_fn.string() );
- xml_node->SetAttribute("loop", ch_final.attack_size);
- psf_decode(a_output_path / ogg_fn, a_output_path / raw_fn, a_dump_pcm,
- psf_data, psf_data_size, ch_final.attack_size, ch_final.loop_size, a_ogg_quality);
- psf_free(psf_data);
- }
- else
- {
- cout << "\ttrack contains only control opcodes, skipping" << endl;
- }
- delete [] file_data;
- }
- music_xml.SaveFile((a_output_path / a_music_xml_fn).string().c_str());
- delete [] psf_driver;*/
- }
- bool lzs_detect(const u8 *a_buffer, u32 a_size)
- {
- return *(u32 *)a_buffer == a_size - sizeof(u32);
- }
- u8 *lzs_extract(u32 &r_output_size, const u8 *a_input, u32 a_size)
- {
- const u8 *input_ptr = a_input + sizeof(u32);
- u32 input_size = *(u32 *)a_input;
- u32 buffer_size = MATH_ROUNDUP(a_size, 0xff);
- u8 *buffer = (u8 *)malloc(buffer_size);
- u32 input_offset = 0;
- r_output_size = 0;
- u8 control_byte = 0;
- u8 control_bit = 0;
- while(input_offset < input_size)
- {
- if(control_bit == 0)
- {
- control_byte = input_ptr[input_offset++];
- control_bit = 8;
- }
- if(control_byte & 1)
- {
- buffer[r_output_size++] = input_ptr[input_offset++];
- if(r_output_size == buffer_size)
- {
- buffer_size += 0xff;
- buffer = (u8*)realloc(buffer, buffer_size);
- }
- }
- else
- {
- u8 reference1 = input_ptr[input_offset++];
- u8 reference2 = input_ptr[input_offset++];
- u16 reference_offset = reference1 | ((reference2 & 0xF0) << 4);
- u8 reference_length = (reference2 & 0xF) + 3;
- int real_offset = r_output_size - ((r_output_size - 18 - reference_offset) & 0xFFF);
- for(u32 j = 0; j < reference_length; ++j)
- {
- buffer[r_output_size++] = real_offset < 0 ? 0 : buffer[real_offset];
- if(r_output_size == buffer_size)
- {
- buffer_size += 0xff;
- buffer = (u8*)realloc(buffer, buffer_size);
- }
- ++real_offset;
- }
- }
- control_byte >>= 1;
- --control_bit;
- }
- return buffer;
- }
- void psf_decode(path a_ogg_fn, path a_raw_fn, bool a_dump_pcm, char *a_psf_data, u32 a_psf_data_size,
- u32 a_attack_size, u32 a_loop_size, float a_ogg_quality)
- {
- /*
- PsfContext ctx;
- upse_set_audio_callback(upse_write_audio_cb, (void *)&ctx);
- g_psf_data_size = a_psf_data_size;
- g_psf_data_offset = 0;
- upse_psf_t *psf_ctx = upse_load(a_psf_data, &upse_iofuncs);
- if(psf_ctx != NULL)
- {
- ctx.samples_decoded = 0;
- ctx.samples_total = a_attack_size + a_loop_size;
- int result = vorbis_encoder_init(ctx.venc_ctx, a_ogg_fn, a_attack_size, a_ogg_quality);
- if(a_dump_pcm)
- ctx.of_pcm.open(a_raw_fn.string().c_str(), ios::binary);
- if(!result && (!a_dump_pcm || ctx.of_pcm.is_open()))
- {
- upse_execute();
- vorbis_encoder_free(ctx.venc_ctx);
- if(a_dump_pcm)
- ctx.of_pcm.close();
- }
- else
- {
- cout << "error: failed to initialize vorbis encoder" << endl;
- }
- upse_free_psf_metadata(psf_ctx);
- }
- else
- {
- cout << "error: failed to load psf data" << endl;
- }
- upse_set_audio_callback(NULL, NULL);*/
- }
- u32 psf_decode_time(char *a_psf_data, u32 a_psf_data_size)
- {
- u32 time = 0;
- /*
- upse_set_breakpoint_callback(upse_breakpoint_cb, (void *)&time, 0x80033894); //@[0x80049828 + 0xa0 * 4]
- g_psf_data_size = a_psf_data_size;
- g_psf_data_offset = 0;
- upse_psf_t *psf_ctx = upse_load(a_psf_data, &upse_iofuncs);
- if(psf_ctx != NULL)
- {
- upse_execute();
- upse_free_psf_metadata(psf_ctx);
- }
- else
- {
- cout << "error: failed to load psf data" << endl;
- }
- upse_set_breakpoint_callback(NULL, NULL, 0);
- */
- return time;
- }
- void psf_write(string a_out_fn, const u8 *a_buffer, u32 a_buffer_size)
- {
- // compress data
- uLong zlib_length = compressBound(a_buffer_size);
- u8 *psf_data = (u8 *)malloc(sizeof(PsfHeader) + zlib_length);
- Bytef *zlib_data = (Bytef *)(psf_data + sizeof(PsfHeader));
- if(compress2(zlib_data, &zlib_length, a_buffer, a_buffer_size, 9) == Z_OK)
- {
- // write psf
- PsfHeader &psf_header = *(PsfHeader *)psf_data;
- memcpy(psf_header.magic, "PSF", sizeof(psf_header.magic));
- psf_header.version = 1;
- psf_header.reserved_size = 0;
- psf_header.program_size = zlib_length;
- psf_header.program_crc = crc32(crc32(0, Z_NULL, 0), zlib_data, zlib_length);
- u32 psf_length = sizeof(psf_header) + zlib_length;
- ofstream oF(a_out_fn.c_str(), ios::binary);
- oF.write((char *)psf_data, psf_length);
- oF.close();
- }
- else
- {
- cout << "error: unable to compress psx executable" << endl;
- }
- free(psf_data);
- }
- char *psf_alloc(u32 &r_size, const u8 *a_buffer, u32 a_buffer_size)
- {
- // compress data
- uLong zlib_length = compressBound(a_buffer_size);
- char *psf_data = (char *)malloc(sizeof(PsfHeader) + zlib_length);
- Bytef *zlib_data = (Bytef *)(psf_data + sizeof(PsfHeader));
- if(compress2(zlib_data, &zlib_length, a_buffer, a_buffer_size, 9) == Z_OK)
- {
- // write psf
- PsfHeader &psf_header = *(PsfHeader *)psf_data;
- memcpy(psf_header.magic, "PSF", sizeof(psf_header.magic));
- psf_header.version = 1;
- psf_header.reserved_size = 0;
- psf_header.program_size = zlib_length;
- psf_header.program_crc = crc32(crc32(0, Z_NULL, 0), zlib_data, zlib_length);
- r_size = sizeof(psf_header) + zlib_length;
- }
- else
- {
- cout << "error: unable to compress psx executable" << endl;
- }
- return psf_data;
- }
- void psf_free(char *a_psf_data)
- {
- free(a_psf_data);
- }
- void upse_write_audio_cb(unsigned char *data, long bytes, void *user_data)
- {
- PsfContext &ctx = *(PsfContext *)user_data;
- u32 samples_to_write = min<u32>(bytes / (sizeof(s16) * g_PSF_CHANNELS_COUNT), ctx.samples_total - ctx.samples_decoded);
- ctx.samples_decoded += samples_to_write;
- if(ctx.of_pcm.is_open())
- ctx.of_pcm.write((char *)data, samples_to_write * sizeof(s16) * g_PSF_CHANNELS_COUNT);
- vorbis_encoder_update(ctx.venc_ctx, (s16 *)data, samples_to_write, g_PSF_CHANNELS_COUNT, ctx.samples_decoded >= ctx.samples_total);
- cout << "\r" << "\tdecoding " << samples_to_time(ctx.samples_decoded)
- << "/" << samples_to_time(ctx.samples_total) << "... " << flush;
- if(ctx.samples_decoded >= ctx.samples_total)
- {
- // upse_stop();
- cout << "done" << endl;
- }
- }
- void upse_breakpoint_cb(u32 samples_count, void *user_data)
- {
- *(u32 *)user_data = samples_count;
- //upse_stop();
- }
- void *upse_open_cb(char *path, char *mode)
- {
- return path;
- }
- size_t upse_read_cb(void *ptr, size_t sz, size_t nmemb, void *file)
- {
- char *data = (char *)file;
- size_t bytes_to_write = min((g_psf_data_size - g_psf_data_offset) / sz, nmemb) * sz;
- memcpy(ptr, &data[g_psf_data_offset], bytes_to_write);
- g_psf_data_offset += bytes_to_write;
- return bytes_to_write / sz;
- }
- int upse_seek_cb(void *ptr, long offset, int whence)
- {
- switch(whence)
- {
- case SEEK_SET:
- g_psf_data_offset = offset;
- break;
- case SEEK_CUR:
- g_psf_data_offset += offset;
- break;
- case SEEK_END:
- g_psf_data_offset = g_psf_data_offset + offset;
- break;
- default:
- return 1;
- }
- return 0;
- }
- int upse_close_cb(void *ptr)
- {
- return 0;
- }
- int vorbis_encoder_init(VorbisEncoderContext &ctx, path a_ogg_fn, u32 a_attack_size, float a_ogg_quality)
- {
- int result = 1;
- #ifdef QGears_SOUND
- ctx.ofs_file.open(a_ogg_fn.string().c_str(), ios::binary);
- if(!ctx.ofs_file.is_open())
- return result;
- vorbis_info_init(&ctx.vi);
- if((result = vorbis_encode_init_vbr(&ctx.vi, 2, 44100, a_ogg_quality)))
- return result;
- if((result = vorbis_analysis_init(&ctx.vds, &ctx.vi)))
- return result;
- vorbis_comment_init(&ctx.vc);
- vorbis_comment_add_tag(&ctx.vc, "LOOP", to_string(a_attack_size).c_str());
- if((result = vorbis_block_init(&ctx.vds, &ctx.vb)))
- return result;
- srand(time(NULL));
- if((result = ogg_stream_init(&ctx.os, rand())))
- return result;
- ogg_packet headers[3];
- if((result = vorbis_analysis_headerout(&ctx.vds, &ctx.vc, &headers[0], &headers[1], &headers[2])))
- return result;
- for(u32 i = 0; i < SIZEOF(headers); ++i)
- if((result = ogg_stream_packetin(&ctx.os, &headers[i])))
- return result;
- // this ensures the actual audio data will start on a new page, as per spec
- while(ogg_stream_flush(&ctx.os, &ctx.og))
- {
- ctx.ofs_file.write((char *)ctx.og.header, ctx.og.header_len);
- ctx.ofs_file.write((char *)ctx.og.body, ctx.og.body_len);
- }
- result = 0;
- #endif
- return result;
- }
- void vorbis_encoder_free(VorbisEncoderContext &ctx)
- {
- #ifdef QGears_SOUND
- ogg_stream_clear(&ctx.os);
- vorbis_block_clear(&ctx.vb);
- vorbis_comment_clear(&ctx.vc);
- vorbis_dsp_clear(&ctx.vds);
- vorbis_info_clear(&ctx.vi);
- #endif
- }
- int vorbis_encoder_update(VorbisEncoderContext &ctx, s16 *a_samples, u32 a_samples_count, u32 a_channels_count, bool a_end)
- {
- int result = 0;
- #ifdef QGears_SOUND
- // 3rd argument done in tricky way because we can encode audio in chunks later
- bool end = a_end;
- for(u32 samples = a_samples_count; samples || end; samples = 0)
- {
- if(samples)
- {
- // uninterleave and submit samples
- float **buffer = vorbis_analysis_buffer(&ctx.vds, samples);
- for(u32 i = 0; i < samples * a_channels_count; ++i)
- buffer[i % a_channels_count][i / a_channels_count] = a_samples[i] / 32768.f;
- }
- else
- {
- end = false;
- }
- if((result = vorbis_analysis_wrote(&ctx.vds, samples)))
- return result;
- // vorbis does some data preanalysis, then divvies up blocks for
- // more involved (potentially parallel) processing. Get a single
- // block for encoding now
- while(vorbis_analysis_blockout(&ctx.vds, &ctx.vb) == 1)
- {
- // analysis, assume we want to use bitrate management
- if((result = vorbis_analysis(&ctx.vb, NULL)))
- return result;
- if((result = vorbis_bitrate_addblock(&ctx.vb)))
- return result;
- while((result = vorbis_bitrate_flushpacket(&ctx.vds, &ctx.op)) == 1)
- {
- if((result = ogg_stream_packetin(&ctx.os, &ctx.op)))
- return result;
- while(ogg_stream_pageout(&ctx.os, &ctx.og))
- {
- ctx.ofs_file.write((char *)ctx.og.header, ctx.og.header_len);
- ctx.ofs_file.write((char *)ctx.og.body, ctx.og.body_len);
- if(ogg_page_eos(&ctx.og))
- break;
- }
- }
- if(result < 0)
- return result;
- }
- }
- result = 0;
- #endif
- return result;
- }
- string str_lowercase(string a_str)
- {
- string result;
- result.resize(a_str.size());
- transform(a_str.begin(), a_str.end(), result.begin(), ::tolower);
- return result;
- }
- streampos file_get_size(ifstream &a_if)
- {
- streampos last_pos = a_if.tellg();
- a_if.seekg(0, ios::end);
- streampos result = a_if.tellg();
- a_if.seekg(last_pos);
- return result;
- }
- void file_read(u8 *r_data, string a_path)
- {
- ifstream iF(a_path.c_str(), ios::binary);
- if(iF.is_open())
- iF.read((char *)r_data, file_get_size(iF));
- else
- cout << "error: unable to open file " << a_path << endl;
- }
- void print_channel_stat(ChannelParam a_ch_param)
- {
- u32 total_size = a_ch_param.attack_size + a_ch_param.loop_size;
- cout << samples_to_time(a_ch_param.attack_size) << "(" << a_ch_param.attack_size << ")/"
- << samples_to_time(a_ch_param.loop_size) << "(" << a_ch_param.loop_size << ")/"
- << samples_to_time(total_size) << "(" << total_size << ")";
- }
- string samples_to_time(u32 a_samples)
- {
- string result;
- u32 time_seconds = a_samples / g_PSF_FREQUENCY;
- u32 milli = (a_samples % g_PSF_FREQUENCY) / (g_PSF_FREQUENCY / 100);
- u32 seconds = time_seconds % 60;
- u32 minutes = time_seconds / 60;
- stringstream sS;
- sS << setfill('0');
- sS << setw(2) << minutes << ':' << setw(2) << seconds << '.' << setw(2) << milli;
- sS >> result;
- return result;
- }
|