1use crate::lzss;
11use anyhow::{Result, anyhow, bail};
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(
277 &self,
278 gc0: i32,
279 gc1: i32,
280 gc2: i32,
281 gc3: i32,
282 gc4: i32,
283 ) -> Option<&CgGroupTree> {
284 if self.group_tree_root.tree.is_empty() {
285 return None;
286 }
287 let code = [gc0, gc1, gc2, gc3, gc4];
288 Self::get_group_tree_pointer_rec(self.group_tree_root.tree.first()?, &code, 0)
290 }
291
292 fn get_group_tree_pointer_rec<'a>(
293 group: &'a CgGroupTree,
294 code: &[i32; CG_TABLE_DATA_CODE_MAX],
299 code_index: usize,
300 ) -> Option<&'a CgGroupTree> {
301 let gc = code[code_index];
302 if gc == -1 {
303 return Some(group);
304 }
305 if gc < 0 {
306 return None;
307 }
308 let gc_u = gc as usize;
309 if gc_u >= group.tree.len() {
310 return None;
311 }
312 if code_index + 1 >= CG_TABLE_DATA_CODE_MAX {
313 return group.tree.get(gc_u);
314 }
315 if code[code_index + 1] == -1 {
316 return group.tree.get(gc_u);
317 }
318 let child = group.tree.get(gc_u)?;
319 Self::get_group_tree_pointer_rec(child, code, code_index + 1)
320 }
321
322 fn get_flag_list_rec(&self, group: &CgGroupTree, out: &mut Vec<i32>) {
323 if group.tree.len() <= 1 {
324 out.push(self.entries[group.sub_index].flag_no);
325 return;
326 }
327 for child in &group.tree {
328 self.get_flag_list_rec(child, out);
329 }
330 }
331}
332
333#[derive(Debug, Clone, Copy)]
334struct AvgCgTableHeader {
335 head: [u8; 16],
336 cnt: i32,
337 auto_flag: i32,
338 rev0: i32,
339 rev1: i32,
340}
341
342fn expand_cgm_in_place(buf: &mut [u8]) -> Result<Vec<CgTableEntry>> {
343 if buf.len() < 16 + 4 * 4 {
344 bail!("CGM: input too short");
345 }
346
347 let mut off = 0usize;
348 let mut head = [0u8; 16];
349 head.copy_from_slice(&buf[0..16]);
350 off += 16;
351
352 let cnt = i32::from_le_bytes(buf[off..off + 4].try_into().unwrap());
353 off += 4;
354 let auto_flag = i32::from_le_bytes(buf[off..off + 4].try_into().unwrap());
355 off += 4;
356 let rev0 = i32::from_le_bytes(buf[off..off + 4].try_into().unwrap());
357 off += 4;
358 let rev1 = i32::from_le_bytes(buf[off..off + 4].try_into().unwrap());
359 off += 4;
360
361 let header = AvgCgTableHeader {
362 head,
363 cnt,
364 auto_flag,
365 rev0,
366 rev1,
367 };
368
369 if header.cnt <= 0 {
370 bail!("CGM: invalid cnt={}", header.cnt);
371 }
372
373 let ident = c_string_prefix(&header.head);
374
375 let wp = &mut buf[off..];
376 tpc_angou_in_place(wp);
377
378 let expand_data = lzss::lzss_unpack_lenient(wp)?;
379
380 match ident.as_str() {
381 "CGTABLE2" => parse_table2(&expand_data, header.cnt as usize),
382 "CGTABLE" => parse_table1(&expand_data, header.cnt as usize),
383 _ => bail!("CGM: unsupported identifier: {ident}"),
384 }
385}
386
387fn parse_table2(expand_data: &[u8], cnt: usize) -> Result<Vec<CgTableEntry>> {
388 let entry_size = AVG_CG_TABLE_NAME_LEN + 4 + (CG_TABLE_DATA_CODE_MAX * 4) + 4;
389 let need = cnt
390 .checked_mul(entry_size)
391 .ok_or_else(|| anyhow!("CGM: size overflow"))?;
392 if expand_data.len() < need {
393 bail!(
394 "CGM: expanded data too short (need={}, got={})",
395 need,
396 expand_data.len()
397 );
398 }
399
400 let mut out = Vec::with_capacity(cnt);
401 let mut off = 0usize;
402 for i in 0..cnt {
403 let name_raw = &expand_data[off..off + AVG_CG_TABLE_NAME_LEN];
404 off += AVG_CG_TABLE_NAME_LEN;
405
406 let flag_no = i32::from_le_bytes(expand_data[off..off + 4].try_into().unwrap());
407 off += 4;
408
409 let mut code = [0i32; CG_TABLE_DATA_CODE_MAX];
410 for j in 0..CG_TABLE_DATA_CODE_MAX {
411 code[j] = i32::from_le_bytes(expand_data[off..off + 4].try_into().unwrap());
412 off += 4;
413 }
414
415 let code_exist_cnt = i32::from_le_bytes(expand_data[off..off + 4].try_into().unwrap());
416 off += 4;
417
418 let mut name = decode_name(name_raw);
419 name = name.to_ascii_uppercase();
420
421 out.push(CgTableEntry {
422 name,
423 flag_no,
424 code_exist_cnt,
425 code,
426 list_no: i as i32,
427 group: [-1; CG_TABLE_DATA_CODE_MAX],
428 });
429 }
430 Ok(out)
431}
432
433fn parse_table1(expand_data: &[u8], cnt: usize) -> Result<Vec<CgTableEntry>> {
434 let entry_size = AVG_CG_TABLE_NAME_LEN + 4;
435 let need = cnt
436 .checked_mul(entry_size)
437 .ok_or_else(|| anyhow!("CGM: size overflow"))?;
438 if expand_data.len() < need {
439 bail!(
440 "CGM: expanded data too short (need={}, got={})",
441 need,
442 expand_data.len()
443 );
444 }
445
446 let mut out = Vec::with_capacity(cnt);
447 let mut off = 0usize;
448 for i in 0..cnt {
449 let name_raw = &expand_data[off..off + AVG_CG_TABLE_NAME_LEN];
450 off += AVG_CG_TABLE_NAME_LEN;
451
452 let flag_no = i32::from_le_bytes(expand_data[off..off + 4].try_into().unwrap());
453 off += 4;
454
455 let mut name = decode_name(name_raw);
456 name = name.to_ascii_uppercase();
457
458 out.push(CgTableEntry {
459 name,
460 flag_no,
461 code_exist_cnt: 0,
462 code: [0; CG_TABLE_DATA_CODE_MAX],
463 list_no: i as i32,
464 group: [-1; CG_TABLE_DATA_CODE_MAX],
465 });
466 }
467 Ok(out)
468}
469
470fn decode_name(name_raw: &[u8]) -> String {
471 let end = name_raw
472 .iter()
473 .position(|&b| b == 0)
474 .unwrap_or(name_raw.len());
475 let (cow, _, _) = SHIFT_JIS.decode(&name_raw[..end]);
476 cow.into_owned()
477}
478
479fn c_string_prefix(buf: &[u8]) -> String {
480 let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
481 let (cow, _, _) = SHIFT_JIS.decode(&buf[..end]);
482 cow.into_owned()
483}
484
485fn tpc_angou_in_place(src: &mut [u8]) {
487 for (i, b) in src.iter_mut().enumerate() {
488 *b ^= TPC_ANGOU_TABLE[i & 0xFF];
489 }
490}
491
492const TPC_ANGOU_TABLE: [u8; 256] = [
494 0x8b, 0xe5, 0x5d, 0xc3, 0xa1, 0xe0, 0x30, 0x44, 0x00, 0x85, 0xc0, 0x74, 0x09, 0x5f, 0x5e, 0x33,
495 0xc0, 0x5b, 0x8b, 0xe5, 0x5d, 0xc3, 0x8b, 0x45, 0x0c, 0x85, 0xc0, 0x75, 0x14, 0x8b, 0x55, 0xec,
496 0x83, 0xc2, 0x20, 0x52, 0x6a, 0x00, 0xe8, 0xf5, 0x28, 0x01, 0x00, 0x83, 0xc4, 0x08, 0x89, 0x45,
497 0x0c, 0x8b, 0x45, 0xe4, 0x6a, 0x00, 0x6a, 0x00, 0x50, 0x53, 0xff, 0x15, 0x34, 0xb1, 0x43, 0x00,
498 0x8b, 0x45, 0x10, 0x85, 0xc0, 0x74, 0x05, 0x8b, 0x4d, 0xec, 0x89, 0x08, 0x8a, 0x45, 0xf0, 0x84,
499 0xc0, 0x75, 0x78, 0xa1, 0xe0, 0x30, 0x44, 0x00, 0x8b, 0x7d, 0xe8, 0x8b, 0x75, 0x0c, 0x85, 0xc0,
500 0x75, 0x44, 0x8b, 0x1d, 0xd0, 0xb0, 0x43, 0x00, 0x85, 0xff, 0x76, 0x37, 0x81, 0xff, 0x00, 0x00,
501 0x04, 0x00, 0x6a, 0x00, 0x76, 0x43, 0x8b, 0x45, 0xf8, 0x8d, 0x55, 0xfc, 0x52, 0x68, 0x00, 0x00,
502 0x04, 0x00, 0x56, 0x50, 0xff, 0x15, 0x2c, 0xb1, 0x43, 0x00, 0x6a, 0x05, 0xff, 0xd3, 0xa1, 0xe0,
503 0x30, 0x44, 0x00, 0x81, 0xef, 0x00, 0x00, 0x04, 0x00, 0x81, 0xc6, 0x00, 0x00, 0x04, 0x00, 0x85,
504 0xc0, 0x74, 0xc5, 0x8b, 0x5d, 0xf8, 0x53, 0xe8, 0xf4, 0xfb, 0xff, 0xff, 0x8b, 0x45, 0x0c, 0x83,
505 0xc4, 0x04, 0x5f, 0x5e, 0x5b, 0x8b, 0xe5, 0x5d, 0xc3, 0x8b, 0x55, 0xf8, 0x8d, 0x4d, 0xfc, 0x51,
506 0x57, 0x56, 0x52, 0xff, 0x15, 0x2c, 0xb1, 0x43, 0x00, 0xeb, 0xd8, 0x8b, 0x45, 0xe8, 0x83, 0xc0,
507 0x20, 0x50, 0x6a, 0x00, 0xe8, 0x47, 0x28, 0x01, 0x00, 0x8b, 0x7d, 0xe8, 0x89, 0x45, 0xf4, 0x8b,
508 0xf0, 0xa1, 0xe0, 0x30, 0x44, 0x00, 0x83, 0xc4, 0x08, 0x85, 0xc0, 0x75, 0x56, 0x8b, 0x1d, 0xd0,
509 0xb0, 0x43, 0x00, 0x85, 0xff, 0x76, 0x49, 0x81, 0xff, 0x00, 0x00, 0x04, 0x00, 0x6a, 0x00, 0x76,
510];