Skip to content

Commit 31e4a2d

Browse files
Add 1001 Card Cruncher packer strategy
Implement signature detection for 1001 CardCruncher ACM and v4 variants. Fix unpacker SYS address search fallback when PRG load address is below $0800. Add 1001 Card Cruncher test case and verify 100% equivalence against unp64.
1 parent 83a547a commit 31e4a2d

4 files changed

Lines changed: 87 additions & 1 deletion

File tree

crates/regenerator2000-core/src/bin/unpacker_compare_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ fn main() {
6161
"c64_soul_on_fire_unk.prg",
6262
"c64_gianna_sister_remix_badboy.tbc_multicompactor.prg",
6363
"c64_chiller.antiram_packer.prg",
64+
"c64_323_ice_psm.1001_card_cruncher.prg",
6465
];
6566

6667
println!(
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//! 1001 Card Cruncher strategy implementation.
2+
3+
use super::simple::SimplePacker;
4+
use super::{Packer, PackerInfo};
5+
6+
/// Detects 1001 Card Cruncher signature.
7+
#[must_use]
8+
pub fn detect(mem: &[u8], load_addr: u16) -> Option<Box<dyn Packer>> {
9+
if mem.len() > 0x840 && load_addr <= 0x0801 {
10+
// Variant 1: 1001 CardCruncher ACM
11+
// Code at $080D / $080F: SEI; LDX #$F0; STX $01; LDA $src,X; STA $dst,X; DEX; BNE; JMP $0100
12+
for q in [0x080D_usize, 0x080F_usize] {
13+
if mem.len() > q + 17
14+
&& mem[q] == 0x78
15+
&& mem[q + 1] == 0xA2
16+
&& mem[q + 3] == 0x86
17+
&& mem[q + 4] == 0x01
18+
&& mem[q + 5] == 0xBD
19+
&& mem[q + 8] == 0x9D
20+
&& mem[q + 11] == 0xCA
21+
&& mem[q + 12] == 0xD0
22+
&& mem[q + 14] == 0x4C
23+
&& mem[q + 15] == 0x00
24+
&& mem[q + 16] == 0x01
25+
{
26+
let end_val = u16::from_le_bytes([mem[0x081E], mem[0x081F]]);
27+
let entry_val = u16::from_le_bytes([mem[0x083F], mem[0x0840]]);
28+
let end_addr = if end_val > load_addr {
29+
Some(end_val.saturating_sub(1))
30+
} else {
31+
None
32+
};
33+
34+
return Some(Box::new(SimplePacker::new(PackerInfo {
35+
name: "1001 CardCruncher ACM",
36+
dep_addr: Some(0x0100),
37+
start_addr: Some(0x0801),
38+
end_addr,
39+
entry_point: Some(entry_val),
40+
end_addr_ptr: None,
41+
})));
42+
}
43+
}
44+
45+
// Variant 2: 1001 CardCruncher v4
46+
// Code at $0815: STX $01; LDA $082A,Y; STA $FA99...
47+
if mem.len() > 0x941
48+
&& mem[0x815] == 0x86
49+
&& mem[0x816] == 0x01
50+
&& mem[0x81A] == 0x2A
51+
&& mem[0x81B] == 0x08
52+
&& mem[0x81C] == 0x99
53+
&& mem[0x81D] == 0xFA
54+
{
55+
let end_val = u16::from_le_bytes([mem[0x82E], mem[0x82F]]);
56+
let entry_val = u16::from_le_bytes([mem[0x940], mem[0x941]]);
57+
58+
return Some(Box::new(SimplePacker::new(PackerInfo {
59+
name: "1001 CardCruncher v4",
60+
dep_addr: Some(0x0100),
61+
start_addr: Some(0x0801),
62+
end_addr: Some(end_val),
63+
entry_point: Some(entry_val),
64+
end_addr_ptr: None,
65+
})));
66+
}
67+
}
68+
None
69+
}

crates/regenerator2000-core/src/packers/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod action_replay;
44
pub mod alz64;
55
pub mod antiram;
66
pub mod byteboozer;
7+
pub mod card_cruncher;
78
pub mod ccs;
89
pub mod cruel_cruncher;
910
pub mod dali;
@@ -104,6 +105,9 @@ pub fn detect_packer(mem: &[u8], load_addr: u16, load_end: u16) -> Option<Box<dy
104105
if let Some(p) = time_cruncher::detect(mem) {
105106
return Some(p);
106107
}
108+
if let Some(p) = card_cruncher::detect(mem, load_addr) {
109+
return Some(p);
110+
}
107111
if let Some(p) = mc_cracken::detect(mem) {
108112
return Some(p);
109113
}

crates/regenerator2000-core/src/unpacker.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,9 @@ pub fn unpack(
10321032
let entry = if let Some(forced) = config.forced_entry {
10331033
forced
10341034
} else {
1035-
find_sys_address(&memory.mem, basic_start).ok_or(UnpackError::NoEntryPoint)?
1035+
find_sys_address(&memory.mem, basic_start)
1036+
.or_else(|| find_sys_address(&memory.mem, 0x0801))
1037+
.ok_or(UnpackError::NoEntryPoint)?
10361038
};
10371039

10381040
let ret_addr = config
@@ -1686,6 +1688,16 @@ mod tests {
16861688
exp_end: 0xF732,
16871689
exp_entry: 0xE000,
16881690
exp_dep: Some(0x005E),
1691+
exp_packer: None,
1692+
max_instructions: None,
1693+
},
1694+
KnownUnpackCase {
1695+
file: "c64_323_ice_psm.1001_card_cruncher.prg",
1696+
exp_start: 0x0801,
1697+
exp_end: 0x319C,
1698+
exp_entry: 0x3197,
1699+
exp_dep: Some(0x0100),
1700+
exp_packer: Some("1001 CardCruncher ACM"),
16891701
max_instructions: None,
16901702
},
16911703
];

0 commit comments

Comments
 (0)