1use crate::lzss;
11use anyhow::{anyhow, bail, Result};
12use encoding_rs::SHIFT_JIS;
13use std::collections::HashMap;
14use std::fs;
15use std::path::Path;
16
17pub const CG_TABLE_DATA_CODE_MAX: usize = 5;
18const AVG_CG_TABLE_NAME_LEN: usize = 32;
19
20#[derive(Debug, Clone)]
21pub struct CgTableEntry {
22 pub name: String,
23 pub flag_no: i32,
24 pub code_exist_cnt: i32,
25 pub code: [i32; CG_TABLE_DATA_CODE_MAX],
26 pub list_no: i32,
27 pub group: [i32; CG_TABLE_DATA_CODE_MAX],
28}
29
30#[derive(Debug, Clone)]
31pub struct CgGroupTree {
32 pub sub_index: usize,
34 pub tree: Vec<CgGroupTree>,
35}
36
37impl CgGroupTree {
38 fn new(sub_index: usize) -> Self {
39 Self {
40 sub_index,
41 tree: Vec::new(),
42 }
43 }
44}
45
46#[derive(Debug, Clone)]
47pub struct CgTableData {
48 pub entries: Vec<CgTableEntry>,
49
50 name_find: HashMap<String, usize>,
51 flag_find: HashMap<i32, usize>,
52 sort_list: Vec<usize>,
53
54 group_tree_root: CgGroupTree,
56}
57
58impl CgTableData {
59 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
60 let buf = fs::read(path)?;
61 Self::from_bytes(&buf)
62 }
63
64 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
65 let mut buf = bytes.to_vec();
66 let entries = expand_cgm_in_place(&mut buf)?;
67 let mut out = Self {
68 entries,
69 name_find: HashMap::new(),
70 flag_find: HashMap::new(),
71 sort_list: Vec::new(),
72 group_tree_root: CgGroupTree::new(0),
73 };
74 out.create_name_find_map();
75 out.create_flag_find_map();
76 out.create_sort_list();
77 out.create_group_tree();
78 Ok(out)
79 }
80
81 pub fn get_cg_cnt(&self) -> usize {
82 self.entries.len()
83 }
84
85 pub fn get_sub_from_name(&self, name: &str) -> Option<&CgTableEntry> {
86 let upper = name.to_ascii_uppercase();
87 let idx = self.name_find.get(&upper).copied()?;
88 self.entries.get(idx)
89 }
90
91 pub fn get_sub_from_list_no(&self, list_no: i32) -> Option<&CgTableEntry> {
92 if list_no < 0 {
93 return None;
94 }
95 self.entries.get(list_no as usize)
96 }
97
98 pub fn get_sub_from_flag_no(&self, flag_no: i32) -> Option<&CgTableEntry> {
99 let idx = self.flag_find.get(&flag_no).copied()?;
100 self.entries.get(idx)
101 }
102
103 pub fn get_sub_from_group_code(
105 &self,
106 gc0: i32,
107 gc1: i32,
108 gc2: i32,
109 gc3: i32,
110 gc4: i32,
111 ) -> Option<&CgTableEntry> {
112 let g = self.get_group_tree_pointer(gc0, gc1, gc2, gc3, gc4)?;
113 self.entries.get(g.sub_index)
114 }
115
116 pub fn get_flag_list(&self, gc0: i32, gc1: i32, gc2: i32, gc3: i32, gc4: i32) -> Vec<i32> {
118 let mut out = Vec::new();
119 let Some(g) = self.get_group_tree_pointer(gc0, gc1, gc2, gc3, gc4) else {
120 return out;
121 };
122 if g.tree.is_empty() {
123 return out;
124 }
125 self.get_flag_list_rec(g, &mut out);
126 out
127 }
128
129 fn create_name_find_map(&mut self) {
130 self.name_find.clear();
131 for (i, e) in self.entries.iter().enumerate() {
132 self.name_find.entry(e.name.clone()).or_insert(i);
134 }
135 }
136
137 fn create_flag_find_map(&mut self) {
138 self.flag_find.clear();
139 for (i, e) in self.entries.iter().enumerate() {
140 self.flag_find.entry(e.flag_no).or_insert(i);
141 }
142 }
143
144 fn create_sort_list(&mut self) {
145 self.sort_list.clear();
146 self.sort_list.extend(0..self.entries.len());
147 self.sort_list.sort_by(|&a, &b| {
148 let lhs = &self.entries[a];
149 let rhs = &self.entries[b];
150 for i in 0..CG_TABLE_DATA_CODE_MAX {
151 if lhs.code[i] < rhs.code[i] {
152 return std::cmp::Ordering::Less;
153 }
154 if lhs.code[i] > rhs.code[i] {
155 return std::cmp::Ordering::Greater;
156 }
157 }
158 lhs.list_no.cmp(&rhs.list_no)
159 });
160 }
161
162 fn create_group_tree(&mut self) {
163 if self.sort_list.is_empty() {
164 self.group_tree_root = CgGroupTree::new(0);
165 return;
166 }
167
168 let mut root = CgGroupTree::new(self.sort_list[0]);
177 let mut code = [-1i32; CG_TABLE_DATA_CODE_MAX];
178 let entries: &mut [CgTableEntry] = &mut self.entries;
179 let sort_list: &[usize] = &self.sort_list;
180 Self::create_group_tree_rec(entries, sort_list, &mut root, 0, &mut code, 0);
181 self.group_tree_root = root;
182 }
183
184 fn create_group_tree_rec(
185 entries: &mut [CgTableEntry],
186 sort_list: &[usize],
187 node: &mut CgGroupTree,
188 sort_list_index: usize,
189 code: &mut [i32; CG_TABLE_DATA_CODE_MAX],
190 code_index: usize,
191 ) {
192 let sort_list_index_backup = sort_list_index;
193
194 let mut end = sort_list_index;
196 while end < sort_list.len() {
197 let sub = &entries[sort_list[end]];
198 let mut loop_out = false;
199 for i in 0..code_index {
200 if sub.code[i] != code[i] {
201 loop_out = true;
202 break;
203 }
204 }
205 if loop_out {
206 break;
207 }
208 end += 1;
209 }
210
211 if sort_list_index_backup >= end {
212 return;
213 }
214
215 let mut group_cnt = 0usize;
217 let mut now_code = -1i32;
218 for idx in sort_list_index_backup..end {
219 let sub = &entries[sort_list[idx]];
220 if sub.code[code_index] != now_code || code_index == (CG_TABLE_DATA_CODE_MAX - 1) {
221 now_code = sub.code[code_index];
222 group_cnt += 1;
223 }
224 }
225 if group_cnt == 0 {
226 return;
227 }
228
229 node.tree.clear();
230 node.tree.reserve(group_cnt);
231
232 node.sub_index = sort_list[sort_list_index_backup];
234
235 let mut now_code = -1i32;
237 let mut groupe_no = 0i32;
238 let mut tree_index = 0usize;
239 let mut idx = sort_list_index_backup;
240 while idx < end {
241 let sub_idx = sort_list[idx];
242 let sub_code = entries[sub_idx].code[code_index];
243 if sub_code != now_code || code_index == (CG_TABLE_DATA_CODE_MAX - 1) {
244 if now_code != -1 {
245 groupe_no += 1;
246 }
247 now_code = sub_code;
248
249 node.tree.push(CgGroupTree::new(sub_idx));
251
252 if code_index + 1 < CG_TABLE_DATA_CODE_MAX {
253 code[code_index] = now_code;
254 let child = node.tree.get_mut(tree_index).unwrap();
255 Self::create_group_tree_rec(
256 entries,
257 sort_list,
258 child,
259 idx,
260 code,
261 code_index + 1,
262 );
263 }
264
265 node.tree[tree_index].sub_index = sub_idx;
267 tree_index += 1;
268 }
269
270 entries[sub_idx].group[code_index] = groupe_no;
271 idx += 1;
272 }
273 }
274
275 fn get_group_tree_pointer(
278 &self,
279 gc0: i32,
280 gc1: i32,
281 gc2: i32,
282 gc3: i32,
283 gc4: i32,
284 ) -> Option<&CgGroupTree> {
285 if self.group_tree_root.tree.is_empty() {
286 return None;
287 }
288 let code = [gc0, gc1, gc2, gc3, gc4];
289 Self::get_group_tree_pointer_rec(self.group_tree_root.tree.get(0)?, &code, 0)
291 }
292
293 fn get_group_tree_pointer_rec<'a>(
294 group: &'a CgGroupTree,
295 code: &[i32; CG_TABLE_DATA_CODE_MAX],
300 code_index: usize,
301 ) -> Option<&'a CgGroupTree> {
302 let gc = code[code_index];
303 if gc == -1 {
304 return Some(group);
305 }
306 if gc < 0 {
307 return None;
308 }
309 let gc_u = gc as usize;
310 if gc_u >= group.tree.len() {
311 return None;
312 }
313 if code_index + 1 >= CG_TABLE_DATA_CODE_MAX {
314 return group.tree.get(gc_u);
315 }
316 if code[code_index + 1] == -1 {
317 return group.tree.get(gc_u);
318 }
319 let child = group.tree.get(gc_u)?;
320 Self::get_group_tree_pointer_rec(child, code, code_index + 1)
321 }
322
323 fn get_flag_list_rec(&self, group: &CgGroupTree, out: &mut Vec<i32>) {
324 if group.tree.len() <= 1 {
325 out.push(self.entries[group.sub_index].flag_no);
326 return;
327 }
328 for child in &group.tree {
329 self.get_flag_list_rec(child, out);
330 }
331 }
332}
333
334#[derive(Debug, Clone, Copy)]
335struct AvgCgTableHeader {
336 head: [u8; 16],
337 cnt: i32,
338 auto_flag: i32,
339 rev0: i32,
340 rev1: i32,
341}
342
343fn expand_cgm_in_place(buf: &mut [u8]) -> Result<Vec<CgTableEntry>> {
344 if buf.len() < 16 + 4 * 4 {
345 bail!("CGM: input too short");
346 }
347
348 let mut off = 0usize;
349 let mut head = [0u8; 16];
350 head.copy_from_slice(&buf[0..16]);
351 off += 16;
352
353 let cnt = i32::from_le_bytes(buf[off..off + 4].try_into().unwrap());
354 off += 4;
355 let auto_flag = i32::from_le_bytes(buf[off..off + 4].try_into().unwrap());
356 off += 4;
357 let rev0 = i32::from_le_bytes(buf[off..off + 4].try_into().unwrap());
358 off += 4;
359 let rev1 = i32::from_le_bytes(buf[off..off + 4].try_into().unwrap());
360 off += 4;
361
362 let header = AvgCgTableHeader {
363 head,
364 cnt,
365 auto_flag,
366 rev0,
367 rev1,
368 };
369
370 if header.cnt <= 0 {
371 bail!("CGM: invalid cnt={}", header.cnt);
372 }
373
374 let ident = c_string_prefix(&header.head);
375
376 let wp = &mut buf[off..];
377 tpc_angou_in_place(wp);
378
379 let expand_data = lzss::lzss_unpack_lenient(wp)?;
380
381 match ident.as_str() {
382 "CGTABLE2" => parse_table2(&expand_data, header.cnt as usize),
383 "CGTABLE" => parse_table1(&expand_data, header.cnt as usize),
384 _ => bail!("CGM: unsupported identifier: {ident}"),
385 }
386}
387
388fn parse_table2(expand_data: &[u8], cnt: usize) -> Result<Vec<CgTableEntry>> {
389 let entry_size = AVG_CG_TABLE_NAME_LEN + 4 + (CG_TABLE_DATA_CODE_MAX * 4) + 4;
390 let need = cnt
391 .checked_mul(entry_size)
392 .ok_or_else(|| anyhow!("CGM: size overflow"))?;
393 if expand_data.len() < need {
394 bail!(
395 "CGM: expanded data too short (need={}, got={})",
396 need,
397 expand_data.len()
398 );
399 }
400
401 let mut out = Vec::with_capacity(cnt);
402 let mut off = 0usize;
403 for i in 0..cnt {
404 let name_raw = &expand_data[off..off + AVG_CG_TABLE_NAME_LEN];
405 off += AVG_CG_TABLE_NAME_LEN;
406
407 let flag_no = i32::from_le_bytes(expand_data[off..off + 4].try_into().unwrap());
408 off += 4;
409
410 let mut code = [0i32; CG_TABLE_DATA_CODE_MAX];
411 for j in 0..CG_TABLE_DATA_CODE_MAX {
412 code[j] = i32::from_le_bytes(expand_data[off..off + 4].try_into().unwrap());
413 off += 4;
414 }
415
416 let code_exist_cnt = i32::from_le_bytes(expand_data[off..off + 4].try_into().unwrap());
417 off += 4;
418
419 let mut name = decode_name(name_raw);
420 name = name.to_ascii_uppercase();
421
422 out.push(CgTableEntry {
423 name,
424 flag_no,
425 code_exist_cnt,
426 code,
427 list_no: i as i32,
428 group: [-1; CG_TABLE_DATA_CODE_MAX],
429 });
430 }
431 Ok(out)
432}
433
434fn parse_table1(expand_data: &[u8], cnt: usize) -> Result<Vec<CgTableEntry>> {
435 let entry_size = AVG_CG_TABLE_NAME_LEN + 4;
436 let need = cnt
437 .checked_mul(entry_size)
438 .ok_or_else(|| anyhow!("CGM: size overflow"))?;
439 if expand_data.len() < need {
440 bail!(
441 "CGM: expanded data too short (need={}, got={})",
442 need,
443 expand_data.len()
444 );
445 }
446
447 let mut out = Vec::with_capacity(cnt);
448 let mut off = 0usize;
449 for i in 0..cnt {
450 let name_raw = &expand_data[off..off + AVG_CG_TABLE_NAME_LEN];
451 off += AVG_CG_TABLE_NAME_LEN;
452
453 let flag_no = i32::from_le_bytes(expand_data[off..off + 4].try_into().unwrap());
454 off += 4;
455
456 let mut name = decode_name(name_raw);
457 name = name.to_ascii_uppercase();
458
459 out.push(CgTableEntry {
460 name,
461 flag_no,
462 code_exist_cnt: 0,
463 code: [0; CG_TABLE_DATA_CODE_MAX],
464 list_no: i as i32,
465 group: [-1; CG_TABLE_DATA_CODE_MAX],
466 });
467 }
468 Ok(out)
469}
470
471fn decode_name(name_raw: &[u8]) -> String {
472 let end = name_raw
473 .iter()
474 .position(|&b| b == 0)
475 .unwrap_or(name_raw.len());
476 let (cow, _, _) = SHIFT_JIS.decode(&name_raw[..end]);
477 cow.into_owned()
478}
479
480fn c_string_prefix(buf: &[u8]) -> String {
481 let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
482 let (cow, _, _) = SHIFT_JIS.decode(&buf[..end]);
483 cow.into_owned()
484}
485
486fn tpc_angou_in_place(src: &mut [u8]) {
488 for (i, b) in src.iter_mut().enumerate() {
489 *b ^= TPC_ANGOU_TABLE[i & 0xFF];
490 }
491}
492
493const TPC_ANGOU_TABLE: [u8; 256] = [
495 0x8b, 0xe5, 0x5d, 0xc3, 0xa1, 0xe0, 0x30, 0x44, 0x00, 0x85, 0xc0, 0x74, 0x09, 0x5f, 0x5e, 0x33,
496 0xc0, 0x5b, 0x8b, 0xe5, 0x5d, 0xc3, 0x8b, 0x45, 0x0c, 0x85, 0xc0, 0x75, 0x14, 0x8b, 0x55, 0xec,
497 0x83, 0xc2, 0x20, 0x52, 0x6a, 0x00, 0xe8, 0xf5, 0x28, 0x01, 0x00, 0x83, 0xc4, 0x08, 0x89, 0x45,
498 0x0c, 0x8b, 0x45, 0xe4, 0x6a, 0x00, 0x6a, 0x00, 0x50, 0x53, 0xff, 0x15, 0x34, 0xb1, 0x43, 0x00,
499 0x8b, 0x45, 0x10, 0x85, 0xc0, 0x74, 0x05, 0x8b, 0x4d, 0xec, 0x89, 0x08, 0x8a, 0x45, 0xf0, 0x84,
500 0xc0, 0x75, 0x78, 0xa1, 0xe0, 0x30, 0x44, 0x00, 0x8b, 0x7d, 0xe8, 0x8b, 0x75, 0x0c, 0x85, 0xc0,
501 0x75, 0x44, 0x8b, 0x1d, 0xd0, 0xb0, 0x43, 0x00, 0x85, 0xff, 0x76, 0x37, 0x81, 0xff, 0x00, 0x00,
502 0x04, 0x00, 0x6a, 0x00, 0x76, 0x43, 0x8b, 0x45, 0xf8, 0x8d, 0x55, 0xfc, 0x52, 0x68, 0x00, 0x00,
503 0x04, 0x00, 0x56, 0x50, 0xff, 0x15, 0x2c, 0xb1, 0x43, 0x00, 0x6a, 0x05, 0xff, 0xd3, 0xa1, 0xe0,
504 0x30, 0x44, 0x00, 0x81, 0xef, 0x00, 0x00, 0x04, 0x00, 0x81, 0xc6, 0x00, 0x00, 0x04, 0x00, 0x85,
505 0xc0, 0x74, 0xc5, 0x8b, 0x5d, 0xf8, 0x53, 0xe8, 0xf4, 0xfb, 0xff, 0xff, 0x8b, 0x45, 0x0c, 0x83,
506 0xc4, 0x04, 0x5f, 0x5e, 0x5b, 0x8b, 0xe5, 0x5d, 0xc3, 0x8b, 0x55, 0xf8, 0x8d, 0x4d, 0xfc, 0x51,
507 0x57, 0x56, 0x52, 0xff, 0x15, 0x2c, 0xb1, 0x43, 0x00, 0xeb, 0xd8, 0x8b, 0x45, 0xe8, 0x83, 0xc0,
508 0x20, 0x50, 0x6a, 0x00, 0xe8, 0x47, 0x28, 0x01, 0x00, 0x8b, 0x7d, 0xe8, 0x89, 0x45, 0xf4, 0x8b,
509 0xf0, 0xa1, 0xe0, 0x30, 0x44, 0x00, 0x83, 0xc4, 0x08, 0x85, 0xc0, 0x75, 0x56, 0x8b, 0x1d, 0xd0,
510 0xb0, 0x43, 0x00, 0x85, 0xff, 0x76, 0x49, 0x81, 0xff, 0x00, 0x00, 0x04, 0x00, 0x6a, 0x00, 0x76,
511];