Create a gist now

Instantly share code, notes, and snippets.

@Halogen002 /amethyst.rs Secret
Created Feb 12, 2017

use std::io::{Read,Write};
use std::fs::File;
extern crate byteorder;
use byteorder::{ByteOrder,LittleEndian};
extern crate tritium;
use tritium::map::{Game,Map};
fn main() {
let mut p : Vec<String> = std::env::args().collect();
if p.len() == 1 {
println!("amethyst <map1> [map2...]");
return;
}
let path = p.remove(0);
let resources = match File::open(std::path::Path::new(&path).parent().unwrap().join("resources.map").to_str().unwrap()) {
Ok(mut n) => {
let mut x = Vec::new();
match n.read_to_end(&mut x) {
Ok(_) => (),
Err(e) => {
writeln!(std::io::stderr(),"Error reading resources.map: {}", e).unwrap();
return;
}
}
match Map::from_cache_file(&x) {
Ok(e) => e,
Err(e) => {
writeln!(std::io::stderr(),"Error parsing resources.map: {}", e).unwrap();
return;
}
}
},
Err(e) => {
writeln!(std::io::stderr(),"Error opening resources.map: {}", e).unwrap();
return;
}
};
let diff = match File::open(std::path::Path::new(&path).parent().unwrap().join("diff.bin").to_str().unwrap()) {
Ok(mut n) => {
let mut x = Vec::new();
match n.read_to_end(&mut x) {
Ok(_) => (),
Err(e) => {
writeln!(std::io::stderr(),"Error reading diff.bin: {}", e).unwrap();
return;
}
}
let mut v = Vec::new();
for i in 0..x.len() / 8 {
v.push((LittleEndian::read_u32(&x[i * 4 * 2..]),LittleEndian::read_u32(&x[i * 4 * 2 + 4..])));
}
v
},
Err(e) => {
writeln!(std::io::stderr(),"Error opening diff.bin: {}", e).unwrap();
return;
}
};
let convert_offset = |offset : u32| {
for ref i in &diff {
if i.0 == offset {
return Some(i.1);
}
}
return None;
};
'maps: for m in p {
let mut new_map = match File::open(&m) {
Ok(mut n) => {
let mut x = Vec::new();
match n.read_to_end(&mut x) {
Ok(_) => (),
Err(e) => {
writeln!(std::io::stderr(),"Error reading {}: {}", m, e).unwrap();
continue;
}
}
match Map::from_cache_file(&x) {
Ok(e) => e,
Err(e) => {
writeln!(std::io::stderr(),"Error parsing {}: {}", m, e).unwrap();
continue;
}
}
},
Err(e) => {
writeln!(std::io::stderr(),"Error reading {}: {}", m, e).unwrap();
continue;
}
};
if new_map.kind.0 == Game::HaloCombatEvolved {
writeln!(std::io::stderr(),"{} is already a retail map. Skipping...", m).unwrap();
continue 'maps;
}
let tag_count = new_map.tag_array.tags().len();
for index in 0..tag_count {
let mut i = &mut new_map.tag_array.tags_mut()[index];
if !i.implicit {
let class = i.tag_class.0;
match class {
// bitm
0x6269746D => {
let tag_data = i.data.as_mut().unwrap().to_owned();
if tag_data.len() < 0x60 + 0xC {
writeln!(std::io::stderr(),"{} bitmap tag too small", m).unwrap();
continue 'maps;
}
let count = LittleEndian::read_u32(&tag_data[0x60..]) as usize;
if count == 0 {
continue;
}
let offset = match i.offset_from_memory_address(LittleEndian::read_u32(&tag_data[0x60 + 4..])) {
Some(n) => n,
None => {
writeln!(std::io::stderr(),"{} bitmap reflexive invalid", m).unwrap();
continue 'maps;
}
};
let mut bitmaps = &mut i.data.as_mut().unwrap()[offset .. offset + count * 0x30];
for i in 0..count {
let mut bitmap = &mut bitmaps[i * 0x30 .. (i+1)*0x30];
if bitmap[0xF] & 1 == 1 {
let data_offset = LittleEndian::read_u32(&bitmap[0x18..]);
match convert_offset(data_offset) {
Some(n) => LittleEndian::write_u32(&mut bitmap[0x18..],n),
None => ()
}
}
}
},
// snd!
0x736E6421 => {
let tag_data = i.data.as_ref().unwrap().to_owned();
let tag_data_len = tag_data.len();
if tag_data_len < 0x98 + 0xC {
writeln!(std::io::stderr(),"{} sound tag too small", m).unwrap();
continue 'maps;
}
let ranges_count = LittleEndian::read_u32(&tag_data[0x98..]) as usize;
if ranges_count == 0 {
continue;
}
let ranges_offset = match i.offset_from_memory_address(LittleEndian::read_u32(&tag_data[0x98 + 4..])) {
Some(n) => n,
None => {
writeln!(std::io::stderr(),"{} sound range too small", m).unwrap();
continue 'maps;
}
};
let ranges = tag_data[ranges_offset .. ranges_offset + ranges_count * 0x48].to_owned();
for r in 0..ranges_count as usize {
let range = &ranges[r * 0x48 .. (r+1)* 0x48];
let permutations_count = LittleEndian::read_u32(&range[0x3C..]) as usize;
if permutations_count == 0 {
continue;
}
let permutations_offset = match i.offset_from_memory_address(LittleEndian::read_u32(&range[0x3C + 4..])) {
Some(n) => n,
None => {
writeln!(std::io::stderr(),"{} permutation too small", m).unwrap();
continue 'maps;
}
};
let mut permutations = &mut i.data.as_mut().unwrap()[permutations_offset .. permutations_offset + permutations_count * 0x7C];
for p in 0..permutations_count {
let mut sound = &mut permutations[p * 0x7C .. (p+1) * 0x7C];
if sound[0x44] & 1 == 1 {
let data_offset = LittleEndian::read_u32(&sound[0x48..]);
match convert_offset(data_offset) {
Some(n) => LittleEndian::write_u32(&mut sound[0x48..],n),
None => ()
}
}
}
}
},
_ => ()
}
continue;
}
let class = i.tag_class.0;
let path = i.tag_path.clone();
match resources.tag_array.find_tag(&path,class) {
Some(n) => {
let tag_found = &resources.tag_array.tags()[n];
i.data = tag_found.data.clone();
i.memory_address = tag_found.memory_address;
for x in tag_found.references(&resources.tag_array) {
let mut p = x.clone();
p.tag_index = index;
i.set_reference(&p);
}
},
None => {
writeln!(std::io::stderr(),"Failed to convert {}: Failed to find a tag. Map may be protected!", m).unwrap();
continue 'maps;
}
}
i.resource_index = None;
i.implicit = false;
}
new_map.kind.0 = Game::HaloCombatEvolved;
let mut new_file = match File::create(&m) {
Ok(n) => n,
Err(e) => {
writeln!(std::io::stderr(),"Error saving {}: {}", m, e).unwrap();
continue;
}
};
let data = match new_map.as_cache_file() {
Ok(n) => n,
Err(e) => {
writeln!(std::io::stderr(),"Error saving {}: {}", m, e).unwrap();
continue;
}
};
match new_file.write_all(&data) {
Ok(_) => (),
Err(e) => {
writeln!(std::io::stderr(),"Error saving {}: {}", m, e).unwrap();
continue;
}
}
println!("Converted {} successfully.",m);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment