Created
December 12, 2024 08:07
-
-
Save PARTYMANX/68112d487259c027251f19223ec74897 to your computer and use it in GitHub Desktop.
THPS trg gap parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::env; | |
use std::str; | |
use std::fs; | |
use std::fs::File; | |
use std::io::{ self, BufRead, BufReader }; | |
use std::io::Seek; | |
use std::io::Read; | |
use std::io::Write; | |
use std::path::Path; | |
use std::io::SeekFrom; | |
use std::convert::TryInto; | |
struct Gap { | |
gap_type: u16, | |
score: u16, | |
name: String, | |
} | |
fn read_u8(file: &mut File) -> u8 { | |
let mut buf = [0; 1]; | |
file.read(&mut buf[..]); | |
return buf[0]; | |
} | |
fn read_u16(file: &mut File) -> u16 { | |
let mut buf = [0; 2]; | |
file.read(&mut buf[..]); // TODO: should probably make sure 2 bytes were read... | |
return (buf[0] as u16) | ((buf[1] as u16) << 8); | |
} | |
fn read_u32(file: &mut File) -> u32 { | |
let mut buf = [0; 4]; | |
let count = file.read(&mut buf[..]); // TODO: should probably make sure 4 bytes were read... | |
assert_eq!(matches!(count, Ok(4)), true); | |
//println!("{} {} {} {}", buf[0], buf[1], buf[2], buf[3]); | |
return (buf[0] as u32) | ((buf[1] as u32) << 8) | ((buf[2] as u32) << 16) | ((buf[3] as u32) << 24); | |
} | |
fn read_string(file: &mut File, max_len: u32) -> String { | |
let mut result = String::from(""); | |
let mut i = 0; | |
let mut is_end = false; | |
while !is_end { | |
i += 1; | |
let c = read_u8(file); | |
if c == 0 || i >= max_len { | |
is_end = true; | |
} else { | |
result.push(char::from(c)); | |
} | |
} | |
return result; | |
} | |
fn read_gaplist(filename: &String) -> Vec<Gap> { | |
println!("Reading gap list from file {}", filename); | |
let path = Path::new(&filename); | |
let mut file = File::open(path).unwrap(); | |
file.seek(SeekFrom::Start(0x99a38 as u64)); | |
let mut result = Vec::new(); | |
let mut is_end = false; | |
while !is_end { | |
let gap_type = read_u16(&mut file); | |
let score = read_u16(&mut file); | |
let name_addr = read_u32(&mut file); | |
let cur_offset = file.seek(SeekFrom::Current(0)).unwrap(); | |
let name = if name_addr != 0 { | |
file.seek(SeekFrom::Start((name_addr - 0x8000f800) as u64)); | |
let result = read_string(&mut file, 128); | |
file.seek(SeekFrom::Start(cur_offset)); | |
result | |
} else { | |
String::from("") | |
}; | |
let gap = Gap { | |
gap_type, | |
score, | |
name, | |
}; | |
if gap.gap_type == 0xffff { | |
is_end = true; | |
} else { | |
result.push(gap); | |
} | |
} | |
return result; | |
} | |
fn gaplist_dump(filename: &String) { | |
let gaplist = read_gaplist(filename); | |
for gap in gaplist { | |
println!("{}: type: {} score: {}", gap.name, gap.gap_type, gap.score); | |
} | |
} | |
fn get_checklist(gaplist: &Vec<Gap>, gap_checklist: &mut Vec<bool>, trg_name: &String) { | |
let path = Path::new(trg_name); | |
let mut file = File::open(path).unwrap(); | |
let magic_num = read_u32(&mut file); | |
let ver = read_u32(&mut file); | |
if magic_num != 0x4752545f || ver != 2 { | |
println!("TRG FILE INVALID"); | |
return; | |
} | |
let num_offsets = read_u32(&mut file); | |
let mut offsets = Vec::with_capacity(num_offsets as usize); | |
for _ in 0..num_offsets { | |
offsets.push(read_u32(&mut file)); | |
} | |
for offset in offsets { | |
file.seek(SeekFrom::Start(offset as u64)); | |
let node_type = read_u16(&mut file); | |
if node_type == 6 || node_type == 2 || node_type == 9 { | |
// command point. dump the node | |
let mut checksum_offset = offset + 1 + (read_u16(&mut file) as u32 * 2) + 1; | |
if (checksum_offset & 2) != 0 { | |
checksum_offset += 2; | |
} | |
file.seek(SeekFrom::Start(checksum_offset as u64)); | |
let checksum = read_u32(&mut file); | |
let mut is_end = false; | |
while !is_end { | |
let cmd = read_u16(&mut file); | |
if cmd == 0x00c9 { | |
let id_offset = file.seek(SeekFrom::Current(4)).unwrap(); | |
if (id_offset & 2) != 0 { | |
file.seek(SeekFrom::Current(2)); | |
} | |
let gap_id = read_u16(&mut file); | |
let search_result = { | |
let mut result = None; | |
for i in 0..gaplist.len() { | |
if gaplist[i].gap_type == gap_id { | |
result = Some(i); | |
} | |
} | |
result | |
}; | |
if let Some(gap_idx) = search_result { | |
gap_checklist[gap_idx] = true; | |
} else { | |
println!("Unknown gap??? {} ({})", gap_id, checksum_offset); | |
} | |
} else if cmd == 0xffff { | |
is_end = true; | |
} | |
} | |
} | |
} | |
} | |
fn gap_dump(exe_name: &String, trg_names: &Vec<String>) { | |
let gaplist = read_gaplist(&exe_name); | |
let mut gap_checklist = vec![false; gaplist.len()]; | |
for trg_name in trg_names { | |
get_checklist(&gaplist, &mut gap_checklist, &trg_name); | |
} | |
if trg_names.len() == 1 { | |
for i in 0..gaplist.len() { | |
if gap_checklist[i] { | |
println!("{}: score: {}", gaplist[i].name, gaplist[i].score); | |
} | |
} | |
} else { | |
for i in 0..gaplist.len() { | |
if !gap_checklist[i] { | |
println!("{}: score: {} id: {}", gaplist[i].name, gaplist[i].score, gaplist[i].gap_type); | |
} | |
} | |
} | |
} | |
fn main() { | |
let mut args: Vec<String> = env::args().collect(); | |
args.remove(0); // ignore first arg | |
if args.len() > 1 { | |
gap_dump(&args.remove(0), &args); | |
} else if args.len() == 1 { | |
gaplist_dump(&args[0]); | |
} else { | |
println!("Usage: gapdump.exe [executable] [trg file]") | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
WAREHOUSE: | |
[TRANSFER]: score: 200 | |
[TAXI GAP]: score: 600 | |
[KICKER GAP]: score: 100 | |
[OVER THE PIPE]: score: 300 | |
[SECRET ROOM]: score: 300 | |
[FACEPLANT]: score: 400 | |
[CHANNEL GAP]: score: 250 | |
[KICKER 2 LEDGE]: score: 200 | |
[BIG RAIL]: score: 200 | |
[DECK 2 RAIL]: score: 300 | |
[TAXI 2 LEDGE]: score: 500 | |
[TAXI 2 RAIL]: score: 1000 | |
[MONSTER GRIND]: score: 500 | |
[HIGH RAIL]: score: 200 | |
[HOLY SHI...]: score: 3000 | |
[TRANSITION GRIND]: score: 400 | |
SCHOOL: | |
[ROOF 2 ROOF GAP]: score: 500 | |
[SWIM TEAM GAP]: score: 1000 | |
[GARBAGE OLLIE]: score: 50 | |
[ROOF TO AWNING GAP]: score: 750 | |
[DITCH SLAP]: score: 250 | |
[OVER THE AIR CONDITIONER]: score: 750 | |
[OVER A FOOTBRIDGE]: score: 1000 | |
[PARK GAP]: score: 500 | |
[MINI GAP]: score: 250 | |
[PLANTER GAP]: score: 100 | |
[KICKER GAP]: score: 100 | |
[HALL PASS GAP]: score: 1000 | |
[DUMPSTER RAIL GAP]: score: 250 | |
[PLAYGROUND RAIL]: score: 500 | |
[RAIL TO RAIL TRANSFER]: score: 750 | |
[HUGE RAIL]: score: 1000 | |
[LONG ASS RAIL]: score: 2500 | |
[FUNBOX TO RAIL TRANSFER]: score: 250 | |
[FUNBOX TO TABLE TRANSFER]: score: 500 | |
[GIMME GAP]: score: 50 | |
[HANDICAP RAMP RAIL]: score: 500 | |
MALL: | |
[OVER A 16 STAIR SET]: score: 250 | |
[OVER A HUGE 32 STAIR GAP]: score: 2000 | |
[SKATER ESCALATOR GAP]: score: 500 | |
[32 STEPS OFF A MEZZANINE]: score: 2500 | |
[THE FLYING LEAP]: score: 100 | |
[PLANTER GAP]: score: 100 | |
[GOING UP GAP]: score: 250 | |
[GOING DOWN GAP]: score: 250 | |
[FOUNTAIN GAP]: score: 250 | |
[COFFEE GRIND]: score: 1000 | |
[FOR THE WHOLE ATRIUM]: score: 500 | |
[RAIL COMBO]: score: 500 | |
SKATEPARK: | |
[TRANSFER]: score: 200 | |
[ACID DROP]: score: 1000 | |
[WHOOP GAP]: score: 1000 | |
[WALL GAP]: score: 100 | |
[OVER THE BOX]: score: 100 | |
[OVER THE RAFTERS]: score: 2000 | |
[OVER THE PIPE]: score: 700 | |
[POOL HIP]: score: 500 | |
[POOL 2 WALKWAY]: score: 700 | |
[HP TRANSFER]: score: 250 | |
[RAFTER RAIL]: score: 1000 | |
[PIPE 2 BOX GRIND]: score: 1000 | |
[LIGHT GRIND]: score: 500 | |
[WALKWAY RAIL TRANS]: score: 700 | |
[POOL RAIL TRANS]: score: 1000 | |
DOWNTOWN: | |
[PHAT GAP]: score: 1000 | |
[TRANSFER]: score: 200 | |
[KICKER GAP]: score: 100 | |
[KICKER 2 STREET]: score: 100 | |
[BS GAP]: score: 500 | |
[T 2 T GAP]: score: 500 | |
[SECRET TUNNEL ENTRANCE]: score: 500 | |
[TUNNEL GAP]: score: 1000 | |
[OVER THE TUNNEL]: score: 2000 | |
[CAR OLLIE]: score: 100 | |
[CHEESY DECK GAP]: score: 50 | |
[DECK GAP]: score: 250 | |
[BURLY DECK GAP]: score: 2500 | |
[TRUCK GAP]: score: 250 | |
[ROOF 2 ROOF]: score: 2000 | |
[SUCKY ROOM GAP]: score: 1500 | |
[BIG ASS]: score: 1500 | |
[GLASS GAP]: score: 750 | |
[KICKER 2 EDGE]: score: 100 | |
[BS GRIND]: score: 200 | |
[RAIL 2 RAIL TRANSFER]: score: 750 | |
[BILLBOARD GRIND]: score: 500 | |
[DIRTY RAIL]: score: 3000 | |
[DEATH GRIND]: score: 2000 | |
DHJ: | |
[HUGE WATER HAZARD GAP]: score: 1000 | |
[50 FEET]: score: 50 | |
[100 FEET]: score: 100 | |
[150 FEET]: score: 150 | |
[200 FEET]: score: 200 | |
[250 FEET]: score: 250 | |
[SMALL WATER HAZARD GAP]: score: 250 | |
[25 FEET]: score: 25 | |
[75 FEET]: score: 75 | |
[125 FEET]: score: 125 | |
[175 FEET]: score: 175 | |
[225 FEET]: score: 225 | |
[NEVERSOFT ELEC CO GAP]: score: 1500 | |
BURNSIDE: | |
[TRANSFER]: score: 200 | |
[BRIDGE GAP]: score: 1000 | |
[VERT WALL GAP]: score: 700 | |
[TWINKIE TRANSFER]: score: 700 | |
[OVER DA POOL]: score: 800 | |
[TRIPLE RAIL]: score: 1000 | |
[BRIDGE GRIND]: score: 800 | |
STREETS: | |
[PORCH GAP]: score: 250 | |
[LOMBARD GAP]: score: 5000 | |
[THE GONZ GAP]: score: 500 | |
[PAGODA GAP]: score: 1000 | |
[OVER THE SEVEN]: score: 100 | |
[HUBBA GAP]: score: 750 | |
[STREET GAP]: score: 500 | |
[HANDI GAP]: score: 1000 | |
[C BLOCK GAP]: score: 500 | |
[PLANTER GAP]: score: 500 | |
[FOUNTAIN GAP]: score: 750 | |
[SPINE GAP]: score: 1000 | |
[RAMP 2 RAMP]: score: 500 | |
[RAMP 2 RAMP]: score: 750 | |
[OVERSIZED 8 SET]: score: 500 | |
[ACID DROP-IN]: score: 1000 | |
[KICKER GAP]: score: 500 | |
[DOWN THE SPIRAL]: score: 2000 | |
[LOMBARD LEDGE]: score: 250 | |
[HUBBA LEDGE]: score: 500 | |
[HOOK RAIL]: score: 750 | |
[RAIL 2 RAIL]: score: 500 | |
[BACKWOODS LEDGE]: score: 250 | |
[BENDY'S LIP]: score: 500 | |
ROSWELL: | |
[LOW DECK GAP]: score: 500 | |
[HIGH DECK GAP]: score: 1000 | |
[DECK GAP]: score: 1500 | |
[ROLL IN CHANNEL GAP]: score: 1000 | |
[CHANNEL GAP]: score: 500 | |
[ET GRIND]: score: 1000 | |
[BHOUSE RAIL]: score: 1000 | |
[POOL GRIND]: score: 2000 | |
[DECK GRIND]: score: 800 | |
[MB EMERSON GRIND]: score: 2000 | |
UNUSED: | |
[GAP]: score: 500 | |
[AWNING GAP]: score: 125 | |
[]: score: 500 | |
[RAMP GAP]: score: 250 | |
[RAGING GORGE GAP]: score: 250 | |
[BIG WATER HAZARD GAP]: score: 500 | |
[RAMP 2 RAMP]: score: 250 | |
[TUNNEL GAP]: score: 500 | |
[PORCH GAP]: score: 500 | |
[STREET GAP]: score: 250 | |
[FOUNTAIN GAP: score: 250 | |
[PORCH GAP]: score: 750 | |
[PAGODA HOP]: score: 500 | |
[PAGODA HOP]: score: 750 | |
[GAP]: score: 1000 | |
[FREAKIN POOL GRIND]: score: 100 | |
[GAP]: score: 100 | |
[GAP]: score: 100 | |
[GAP]: score: 1000 | |
[GAP]: score: 1000 | |
[GAP]: score: 1000 | |
[GAP]: score: 1000 | |
[GAP]: score: 1000 | |
[KICKER 2 LEDGE]: score: 200 | |
[HAWK BRIDGE GRIND]: score: 1000 | |
[ARE YOU KIDDING?]: score: 3000 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment