1111#include " third-party/json.hpp"
1212
1313namespace decompiler {
14+ using std::string;
1415
1516// number of bytes per "audio page" in the VAG directory file.
1617constexpr int AUDIO_PAGE_SIZE = 2048 ;
@@ -27,85 +28,77 @@ uint32_t swap32(uint32_t in) {
2728struct AudioDir {
2829 struct Entry {
2930 std::string name;
31+ bool stereo = false ;
32+ bool international = false ;
3033 s64 start_byte = -1 ;
31- s64 end_byte = -1 ;
3234 };
3335
3436 std::vector<Entry> entries;
3537
36- void set_file_size (u64 size) {
37- if (!entries.empty ()) {
38- entries.back ().end_byte = size;
39- }
40- }
41-
4238 int entry_count () const { return entries.size (); }
4339
4440 void debug_print () const {
4541 for (auto & e : entries) {
46- lg::debug (" \" {}\" 0x{:07x} - 0x{:07x}" , e.name , e.start_byte , e.end_byte );
42+ // lg::debug("\"{}\" 0x{:07x} - 0x{:07x}", e.name, e.start_byte, e.end_byte);
4743 }
4844 }
4945};
5046
51- /* !
52- * Read an entry from a WAD and return the binary data.
53- */
54- std::vector<u8 > read_entry (const AudioDir& dir, const std::vector<u8 >& data, int entry_idx) {
55- const auto & entry = dir.entries .at (entry_idx);
56- ASSERT (entry.end_byte > 0 );
57- return std::vector<u8 >(data.begin () + entry.start_byte , data.begin () + entry.end_byte );
58- }
59-
6047/* !
6148 * Matches the format in file.
6249 */
6350struct VagFileHeader {
64- char magic[ 4 ] ;
51+ u32 magic;
6552 u32 version;
6653 u32 zero;
67- u32 channel_size ;
54+ u32 size ;
6855 u32 sample_rate;
6956 u32 z[3 ];
7057 char name[16 ];
7158
72- VagFileHeader swapped_endian_jak1 () const {
59+ VagFileHeader swap_endian () const {
7360 VagFileHeader result (*this );
7461 result.version = swap32 (result.version );
75- result.channel_size = swap32 (result.channel_size );
62+ result.size = swap32 (result.size );
7663 result.sample_rate = swap32 (result.sample_rate );
7764 return result;
7865 }
7966
80- VagFileHeader swapped_endian_jak2 () const {
81- VagFileHeader result (*this );
82- result.version = swap32 (result.version );
83- result.channel_size = swap32 (result.channel_size );
84- // for some reason, the sample rate is big endian for jak2
85- // result.sample_rate = swap32(result.sample_rate);
86- return result;
87- }
88-
8967 void debug_print () {
9068 char temp_name[17 ];
9169 memcpy (temp_name, name, 16 );
9270 temp_name[16 ] = ' \0 ' ;
93- lg::debug (" {}{}{}{} v {} zero {} chan {} samp {} z {} {} {} name {}" , magic[0 ], magic[1 ],
94- magic[2 ], magic[3 ], version, zero, channel_size, sample_rate, z[0 ], z[1 ], z[2 ],
95- temp_name);
71+ lg::debug (" {:x} v {} zero {} chan {} samp {} z {} {} {} name {}" , magic, version, zero, size,
72+ sample_rate, z[0 ], z[1 ], z[2 ], temp_name);
9673 }
9774};
9875
76+ static std::string unpack_vag_name_jak3 (u64 compressed) {
77+ const char * char_map = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-" ;
78+ u32 chars = compressed & 0x1fffff ;
79+ std::array<char , 9 > buf;
80+ buf.fill (0 );
81+ for (int i = 0 ; i < 8 ; i++) {
82+ if (i == 4 ) {
83+ chars = (compressed >> 21 ) & 0x1fffff ;
84+ }
85+ buf[7 - i] = char_map[chars % 38 ];
86+ chars /= 38 ;
87+ }
88+
89+ return {buf.data ()};
90+ }
91+
9992/* !
10093 * Read the DIR file into an AudioDir
10194 */
10295AudioDir read_audio_dir (const decompiler::Config& config, const fs::path& path) {
10396 auto data = file_util::read_binary_file (path);
10497 lg::info (" Got {} bytes of audio dir." , data.size ());
10598 auto reader = BinaryReader (data);
106- u32 count = reader.read <u32 >();
10799 AudioDir result;
108100 if (config.game_version == GameVersion::Jak1) {
101+ u32 count = reader.read <u32 >();
109102 // matches the format in file.
110103 struct DirEntryJak1 {
111104 char name[8 ];
@@ -129,20 +122,15 @@ AudioDir read_audio_dir(const decompiler::Config& config, const fs::path& path)
129122 e.name .push_back (c);
130123 }
131124 e.start_byte = AUDIO_PAGE_SIZE * entries[i].value ;
132- if (i + 1 < (entries.size ())) {
133- e.end_byte = AUDIO_PAGE_SIZE * entries[i + 1 ].value ;
134- } else {
135- e.end_byte = -1 ;
136- }
137125 result.entries .push_back (e);
138126 }
139127 } else if (config.game_version == GameVersion::Jak2) {
128+ u32 count = reader.read <u32 >();
140129 // matches the format in file.
141130 struct DirEntryJak2 {
142131 char name[8 ];
143132 u32 value;
144- // TODO - no idea what this is
145- u32 boolean;
133+ u32 stereo;
146134 };
147135 u32 data_end = sizeof (u32 ) + sizeof (DirEntryJak2) * count;
148136 ASSERT (data_end <= data.size ());
@@ -161,12 +149,45 @@ AudioDir read_audio_dir(const decompiler::Config& config, const fs::path& path)
161149 // padded with spaces, no null terminator.
162150 e.name .push_back (c);
163151 }
152+ e.stereo = entries[i].stereo ;
164153 e.start_byte = AUDIO_PAGE_SIZE * entries[i].value ;
165- if (i + 1 < (entries.size ())) {
166- e.end_byte = AUDIO_PAGE_SIZE * entries[i + 1 ].value ;
167- } else {
168- e.end_byte = -1 ;
169- }
154+ result.entries .push_back (e);
155+ }
156+ } else if (config.game_version == GameVersion::Jak3) {
157+ struct VagDirJak3 {
158+ u32 id[2 ];
159+ u32 version;
160+ u32 count;
161+ } dir;
162+ struct DirEntryJak3 {
163+ union {
164+ u64 data;
165+ struct {
166+ u64 name : 42 ;
167+ bool stereo : 1 ;
168+ bool international : 1 ;
169+ u8 param : 4 ;
170+ u64 offset : 16 ;
171+ };
172+ };
173+ };
174+ dir = reader.read <VagDirJak3>();
175+ ASSERT (dir.id [0 ] == 0x41574756 );
176+ ASSERT (dir.id [1 ] == 0x52494444 );
177+ lg::warn (" version {} count {}" , dir.version , dir.count );
178+
179+ std::vector<DirEntryJak3> entries;
180+
181+ for (size_t i = 0 ; i < dir.count ; i++) {
182+ entries.push_back (reader.read <DirEntryJak3>());
183+ }
184+
185+ for (size_t i = 0 ; i < entries.size (); i++) {
186+ AudioDir::Entry e;
187+ e.name = unpack_vag_name_jak3 (entries[i].name );
188+ e.stereo = entries[i].stereo ;
189+ e.international = entries[i].international ;
190+ e.start_byte = 0x8000 * entries[i].offset ;
170191 result.entries .push_back (e);
171192 }
172193 } else {
@@ -189,27 +210,22 @@ struct AudioFileInfo {
189210};
190211
191212AudioFileInfo process_audio_file (const fs::path& output_folder,
192- const std::vector< u8 >& data,
213+ nonstd::span< const uint8_t > data,
193214 const std::string& name,
194- const std::string& suffix) {
215+ const std::string& suffix,
216+ bool stereo) {
195217 BinaryReader reader (data);
196218
197219 auto header = reader.read <VagFileHeader>();
198- if (header.magic [0 ] == ' V' ) {
199- header = header.swapped_endian_jak1 ();
200- } else if (header.magic [0 ] == ' p' ) {
201- header = header.swapped_endian_jak2 ();
202- } else {
220+ if (header.magic == 0x70474156 /* big endian (VAGp)*/ ) {
221+ header = header.swap_endian ();
222+ } else if (header.magic != 0x56414770 /* little endian (pGAV) */ ) {
203223 ASSERT (false );
204224 }
205225 header.debug_print ();
206226
207- for (int i = 0 ; i < 16 ; i++) {
208- ASSERT (reader.read <u8 >() == 0 );
209- }
210-
211- const auto [left_samples, right_samples] =
212- decode_adpcm (reader, !str_util::starts_with (std::string (header.name ), " Stereo" ));
227+ reader = BinaryReader (data.subspan (0 , header.size ));
228+ const auto [left_samples, right_samples] = decode_adpcm (reader, stereo);
213229
214230 while (reader.bytes_left ()) {
215231 ASSERT (reader.read <u8 >() == 0 );
@@ -251,12 +267,17 @@ void process_streamed_audio(const decompiler::Config& config,
251267 auto & file = audio_files[lang_id];
252268 auto wad_data = file_util::read_binary_file (input_dir / " VAG" / file);
253269 auto suffix = fs::path (file).extension ().u8string ().substr (1 );
270+ bool int_bank_p = suffix.compare (" INT" ) == 0 ;
254271 langs.push_back (suffix);
255- dir_data.set_file_size (wad_data.size ());
256272 for (int i = 0 ; i < dir_data.entry_count (); i++) {
257- auto audio_data = read_entry (dir_data, wad_data, i);
258- lg::info (" File {}, total {:.2f} minutes" , dir_data.entries .at (i).name , audio_len / 60.0 );
259- auto info = process_audio_file (output_path, audio_data, dir_data.entries .at (i).name , suffix);
273+ auto entry = dir_data.entries .at (i);
274+ if (entry.international != int_bank_p) {
275+ continue ;
276+ }
277+
278+ lg::info (" File {}, total {:.2f} minutes" , entry.name , audio_len / 60.0 );
279+ auto data = nonstd::span (wad_data).subspan (entry.start_byte );
280+ auto info = process_audio_file (output_path, data, entry.name , suffix, entry.stereo );
260281 audio_len += info.length_seconds ;
261282 filename_data[i][lang_id + 1 ] = info.filename ;
262283 }
0 commit comments