Create a gist now

Instantly share code, notes, and snippets.

Demonstration of Tritium: A BSP Swapper for Halo Combat Evolved maps
use std::io::{Read,Write};
use std::fs::File;
extern crate tritium;
use tritium::map::Map;
extern crate byteorder;
use byteorder::{ByteOrder,LittleEndian};
fn main() {
let mut args : Vec<String> = std::env::args().collect();
args.remove(0);
let mut last_arg = None;
let mut from = None;
let mut target = None;
let mut output = None;
let mut copy_scenery = false;
let mut from_bsp = None;
for a in args {
let k = a.to_lowercase();
match last_arg {
Some(n) => {
match &n as &str {
"--from_sbsp" => {
from_bsp = Some(a.parse::<usize>().unwrap());
},
"--output" => {
output = Some(a);
},
"--from" => {
from = Some(a);
},
"--target" => {
target = Some(a);
},
_ => ()
}
last_arg = None;
},
None => {
match &k as &str {
"--help" => {
println!("BSP Swapper by 002 v1.0a");
println!("Usage: bsp_swapper <arguments>");
println!("Arguments:");
println!(" --help - Hi.");
println!(" --from <map> - Specify the path to the origin map. This is required.");
println!(" --target <map> - Specify the path to the target map. This is required.");
println!(" --output <map> - Specify the path to the output map. This is required.");
println!(" --copy_scenery - Replace the scenery with the scenery from the origin map.");
println!(" --from_sbsp <#> - Copy a particular BSP (0 = first BSP, etc.) - required if");
println!(" there are multiple BSPs on the origin map.");
return;
},
"--copy_scenery" => {
copy_scenery = true;
continue;
},
"--from" => (),
"--from_sbsp" => (),
"--target" => (),
"--output" => (),
_ => {
println!("Unexpected argument {}", a);
return;
}
}
last_arg = Some(k);
}
}
}
if target.is_none() {
println!("Error: No target map! Use --help for usage."); return;
}
if from.is_none() {
println!("Error: No origin map! Use --help for usage."); return;
}
if output.is_none() {
println!("Error: No output map! Use --help for usage."); return;
}
let map_from_path = |path : &str| -> Map {
let mut v = Vec::new();
File::open(path).unwrap().read_to_end(&mut v).unwrap();
let map = match Map::from_cache_file(&v) {
Ok(n) => n,
Err(e) => {
panic!("An error occurred while parsing {}. The error was {}", path, e);
}
};
match map.tag_array.principal_tag() {
Some(n) => {
if map.tag_array.tags()[n].tag_class.0 != 0x73636E72 {
panic!("Map {}'s principal tag isn't a scenario tag or is protected.")
}
},
None => panic!("Map {} has no principal tag!", path)
}
map
};
println!("Opening maps...");
let mut map_to = map_from_path(&target.unwrap());
let map_from = map_from_path(&from.unwrap());
println!("Getting origin SBSP index...");
let new_magic;
let origin_sbsp_index = {
let origin_scenario_tag = &map_from.tag_array.tags()[map_from.tag_array.principal_tag().unwrap()];
let origin_scenario_tag_data = origin_scenario_tag.data.as_ref().unwrap();
let origin_sbsp_count = LittleEndian::read_u32(&origin_scenario_tag_data[0x5A4..]) as usize;
if origin_sbsp_count == 0 {
panic!("Origin map has no SBSP tags.");
}
if origin_sbsp_count != 1 && from_bsp.is_none() {
panic!("Origin map has multiple SBSP tags. No SBSP was specified.")
}
let origin_sbsp = match from_bsp {
Some(n) => if n >= origin_sbsp_count {
panic!("Invalid SBSP specified!");
} else { n },
None => 0
};
let origin_sbsp_offset = origin_scenario_tag.offset_from_memory_address(LittleEndian::read_u32(&origin_scenario_tag_data[0x5A4+4..])).unwrap();
let origin_sbsp_id = LittleEndian::read_u32(&origin_scenario_tag_data[origin_sbsp_offset + origin_sbsp * 32 + 0x10 + 0xC..]) as usize;
new_magic = LittleEndian::read_u32(&origin_scenario_tag_data[origin_sbsp_offset + origin_sbsp * 32 + 0x8..]);
if origin_sbsp_id == 0xFFFFFFFF {
panic!("Origin SBSP is nulled out.");
}
origin_sbsp_id & 0xFFFF
};
println!("Importing SBSP into new map");
let new_id = map_to.tag_array.insert_recursive(&map_from.tag_array, origin_sbsp_index);
let new_size = map_to.tag_array.tags()[new_id].data.as_ref().unwrap().len();
println!("Replacing older SBSP tag...");
let principal_tag = *map_to.tag_array.principal_tag().as_ref().unwrap();
let old_id;
let target_sbsp_offset = {
let target_scenario_tag = &map_to.tag_array.tags()[principal_tag];
let target_scenario_tag_data = target_scenario_tag.data.as_ref().unwrap();
let target_sbsp_count = LittleEndian::read_u32(&target_scenario_tag_data[0x5A4..]) as usize;
if target_sbsp_count == 0 {
panic!("Target map has no SBSP tags.");
}
if target_sbsp_count != 1 {
panic!("Target map has multiple SBSP tags.")
}
let target_sbsp_offset = target_scenario_tag.offset_from_memory_address(LittleEndian::read_u32(&target_scenario_tag_data[0x5A4+4..])).unwrap();
let id_offset = target_sbsp_offset + 0x10 + 0xC;
let target_sbsp_id = LittleEndian::read_u32(&target_scenario_tag_data[id_offset..]) as usize;
if target_sbsp_id == 0xFFFFFFFF {
panic!("Target SBSP is nulled out.");
}
old_id = target_sbsp_id & 0xFFFF;
target_sbsp_offset
};
{
let target_scenario_tag = &mut map_to.tag_array.tags_mut()[principal_tag];
let target_scenario_tag_data = target_scenario_tag.data.as_mut().unwrap();
LittleEndian::write_u16(&mut target_scenario_tag_data[target_sbsp_offset + 0x10 + 0xC..], new_id as u16);
LittleEndian::write_u32(&mut target_scenario_tag_data[target_sbsp_offset + 4..], new_size as u32);
LittleEndian::write_u32(&mut target_scenario_tag_data[target_sbsp_offset + 8..], new_magic);
}
let origin_scenario_tag = &map_from.tag_array.tags()[map_from.tag_array.principal_tag().unwrap()];
let origin_scenario_tag_data = origin_scenario_tag.data.as_ref().unwrap();
macro_rules! clone_data {
($reflexive_offset : expr, $reflexive_size : expr, $for_each : expr) => {
{
let origin_count = LittleEndian::read_u32(&origin_scenario_tag_data[$reflexive_offset..]) as usize;
let origin_offset = origin_scenario_tag.offset_from_memory_address(LittleEndian::read_u32(&origin_scenario_tag_data[$reflexive_offset+4..])).unwrap();
let mut origin_data = origin_scenario_tag_data[origin_offset..origin_offset + origin_count*$reflexive_size].to_owned();
for o in 0..origin_count {
$for_each(&mut origin_data[o * $reflexive_size..(o+1) * $reflexive_size]);
}
let mut target_scenario_tag = &mut map_to.tag_array.tags_mut()[principal_tag];
let mut remove_offset;
let remove_length;
{
let target_scenario_tag_data = target_scenario_tag.data.as_ref().unwrap();
remove_length = LittleEndian::read_u32(&target_scenario_tag_data[$reflexive_offset..]) as usize * $reflexive_size;
if remove_length == 0 {
remove_offset = 0
}
else {
remove_offset = target_scenario_tag.offset_from_memory_address(LittleEndian::read_u32(&target_scenario_tag_data[$reflexive_offset+4..])).unwrap();
}
}
if remove_offset != 0 {
target_scenario_tag.delete_data(remove_offset,remove_length);
}
else {
remove_offset = target_scenario_tag.data.as_ref().unwrap().len();
}
let address = target_scenario_tag.memory_address_from_offset(remove_offset).unwrap();
target_scenario_tag.insert_data(remove_offset,&origin_data);
let mut target_scenario_tag_data = target_scenario_tag.data.as_mut().unwrap();
LittleEndian::write_u32(&mut target_scenario_tag_data[$reflexive_offset..], origin_count as u32);
LittleEndian::write_u32(&mut target_scenario_tag_data[$reflexive_offset + 4..], address);
}
}
}
println!("Cloning decals...");
clone_data!(0x3A8,16,|_:&[u8]| {});
println!("Cloning decal palette and importing tags...");
clone_data!(0x3B4,16,|x : &mut [u8]| {
let decal_type = LittleEndian::read_u32(&x[0xC..]) as usize;
if decal_type == 0xFFFFFFFF {
return;
}
let decal_type_id = decal_type & 0xFFFF;
let new_id = {
let decal = &map_from.tag_array.tags()[decal_type_id];
match map_to.tag_array.find_tag(&decal.tag_path,decal.tag_class.0) {
Some(n) => n,
None => {
map_to.tag_array.insert_recursive(&map_from.tag_array,decal_type_id)
}
}
} as u16;
LittleEndian::write_u16(&mut x[0xC..],new_id);
});
println!("Cloning detail objects and importing tags...");
clone_data!(0x3C0,48,|x : &mut [u8]| {
let dobc_type = LittleEndian::read_u32(&x[0xC..]) as usize;
if dobc_type == 0xFFFFFFFF {
return;
}
let dobc_type_id = dobc_type & 0xFFFF;
let new_id = {
let dobc = &map_from.tag_array.tags()[dobc_type_id];
match map_to.tag_array.find_tag(&dobc.tag_path,dobc.tag_class.0) {
Some(n) => n,
None => {
map_to.tag_array.insert_recursive(&map_from.tag_array,dobc_type_id)
}
}
} as u16;
LittleEndian::write_u16(&mut x[0xC..],new_id);
});
println!("Removing older SBSP tag...");
map_to.tag_array.remove(old_id);
if copy_scenery {
println!("Cloning scenery...");
clone_data!(0x210,72,|x : &mut [u8]| { LittleEndian::write_u16(&mut x[2..],0xFFFF); });
println!("Cloning scenery palette and importing tags...");
clone_data!(0x21C,48,|x : &mut [u8]| {
let scenery_type = LittleEndian::read_u32(&x[0xC..]) as usize;
if scenery_type == 0xFFFFFFFF {
return;
}
let scenery_type_id = scenery_type & 0xFFFF;
let new_id = {
let scenery = &map_from.tag_array.tags()[scenery_type_id];
match map_to.tag_array.find_tag(&scenery.tag_path,scenery.tag_class.0) {
Some(n) => n,
None => {
map_to.tag_array.insert_recursive(&map_from.tag_array,scenery_type_id)
}
}
} as u16;
LittleEndian::write_u16(&mut x[0xC..],new_id);
});
}
File::create(&output.unwrap()).unwrap().write_all(&map_to.as_cache_file().unwrap()).unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment