Skip to content

Commit 3dc27e3

Browse files
authored
decompiler: Extract Jak3 VAGs (#3328)
1 parent d4c21c7 commit 3dc27e3

File tree

4 files changed

+115
-69
lines changed

4 files changed

+115
-69
lines changed

common/audio/audio_formats.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ void write_wave_file(const std::vector<s16>& left_samples,
5656
writer.write_to_file(name);
5757
}
5858

59-
std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader, const bool mono) {
59+
std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader,
60+
const bool stereo) {
6061
std::vector<s16> left_samples;
6162
std::vector<s16> right_samples;
6263
s32 left_sample_prev[2] = {0, 0};
6364
s32 right_sample_prev[2] = {0, 0};
65+
bool first_left = true;
66+
bool first_right = true;
6467
constexpr s32 f1[5] = {0, 60, 115, 98, 122};
6568
constexpr s32 f2[5] = {0, 0, -52, -55, -60};
6669

@@ -71,15 +74,29 @@ std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader,
7174
// instead they are partitioned into contiguous 8kb (thats 8192 bytes) chunks
7275
// alternating left/right
7376
bool processing_left_chunk = true;
77+
// We need to skip the vag header for each channel
78+
if (first_left) {
79+
reader.ffwd(48);
80+
bytes_read += 48;
81+
first_left = false;
82+
}
7483
while (true) {
7584
if (!reader.bytes_left()) {
7685
break;
7786
}
7887

79-
if (bytes_read == 0x2000) {
88+
if (stereo && bytes_read == 0x2000) {
8089
// switch streams
8190
processing_left_chunk = !processing_left_chunk;
8291
bytes_read = 0;
92+
93+
// We need to skip the vag header for each channel
94+
if (first_right) {
95+
// skip the header
96+
reader.ffwd(48);
97+
bytes_read += 48;
98+
first_right = false;
99+
}
83100
}
84101

85102
u8 shift_filter = reader.read<u8>();
@@ -107,7 +124,7 @@ std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader,
107124
s32 sample = (s32)(s16)(nibble << 12);
108125
sample >>= shift;
109126

110-
if (mono || processing_left_chunk) {
127+
if (!stereo || processing_left_chunk) {
111128
sample += (left_sample_prev[0] * f1[filter] + left_sample_prev[1] * f2[filter] + 32) / 64;
112129

113130
if (sample > 0x7fff) {

decompiler/config/jak3/ntsc_v1/inputs.jsonc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,15 @@
310310
// "audio_dir_file_name": "jak3/VAG",
311311
"audio_dir_file_name": "",
312312

313-
"streamed_audio_file_names": [],
313+
"streamed_audio_file_names": [
314+
"VAGWAD.ENG",
315+
"VAGWAD.FRE",
316+
"VAGWAD.GER",
317+
"VAGWAD.SPA",
318+
"VAGWAD.ITA",
319+
"VAGWAD.COM",
320+
"VAGWAD.INT"
321+
],
314322

315323
"levels_to_extract": [
316324
"LJKDMPK.DGO",

decompiler/data/streamed_audio.cpp

Lines changed: 84 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "third-party/json.hpp"
1212

1313
namespace decompiler {
14+
using std::string;
1415

1516
// number of bytes per "audio page" in the VAG directory file.
1617
constexpr int AUDIO_PAGE_SIZE = 2048;
@@ -27,85 +28,77 @@ uint32_t swap32(uint32_t in) {
2728
struct 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
*/
6350
struct 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
*/
10295
AudioDir 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

191212
AudioFileInfo 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
}

game/overlord/jak1/iso.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,15 +1195,15 @@ static u32 ProcessVAGData(IsoMessage* _cmd, IsoBufferHeader* buffer_header) {
11951195
if (vag->buffer_number == 0) {
11961196
// first buffer, set stuff up
11971197
u32* data = (u32*)buffer_header->data;
1198-
if (data[0] != 0x70474156 /* 'pGAV' */ && data[0] != 0x56414770 /* 'VAGp' */) {
1198+
if (data[0] != 0x70474156 /* 'VAGp' */ && data[0] != 0x56414770 /* 'pGAV' */) {
11991199
vag->stop = true;
12001200
buffer_header->data_size = 0;
12011201
return CMD_STATUS_IN_PROGRESS;
12021202
}
12031203

12041204
vag->sample_rate = data[4];
12051205
vag->data_left = data[3];
1206-
if (data[0] == 0x70474156 /* 'pGAV' */) {
1206+
if (data[0] == 0x70474156 /* 'VAGp' */) {
12071207
vag->sample_rate = bswap(vag->sample_rate);
12081208
vag->data_left = bswap(vag->data_left);
12091209
}

0 commit comments

Comments
 (0)