Skip to content

Commit

Permalink
Move to library and examples
Browse files Browse the repository at this point in the history
  • Loading branch information
dbierek committed Jun 11, 2024
1 parent 352e1c3 commit a1c23d9
Show file tree
Hide file tree
Showing 8 changed files with 728 additions and 220 deletions.
428 changes: 426 additions & 2 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ byteorder = "1.5.0"
rayon = { version = "1.10.0", optional = true}
serde = { version = "1.0.199", optional = true, features = ["derive"] }
serde-hex = { version = "0.1.0", optional = true }
tungstenite = { version = "0.16.0", features = ["native-tls"]}
url = "2.2.2"

[features]
default = ["path-list", "serde"]
Expand Down
27 changes: 27 additions & 0 deletions examples/connect_to_game.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use std::env;

use tungstenite::{connect, Message};
use url::Url;

pub fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("Usage: cargo run -- example connect_to_game <port>");
return;
}
println!("Connecting to game on port {}", args[1]);

let (mut socket, _response) = connect(
Url::parse(("ws://localhost:".to_string() + &args[1] + "/socket").as_str()).unwrap()
).expect("Can't connect");

let _ = socket.write_message(Message::Text(r#"{"type":"hello","identifier":"glacier2obj"}"#.into()));
// let desk = "feeda4e80bc4b859";
// let _ = socket.write_message(Message::Text(r#"{"type":"selectEntity","entity":{"id": "feede50ae0479660","tblu":"003FC5B5BE3EA0CE"}}"#.into()));
let _ = socket.write_message(Message::Text(r#"{"type":"listEntities"}"#.into()));

loop {
let msg = socket.read_message().expect("Error reading message");
println!("Received: {}", msg);
}
}
16 changes: 16 additions & 0 deletions examples/scan_scenario.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::env;

use glacier2obj::scanner::scenario_scan::ScenarioScan;

// Based on mount_game_files example from rpkg-rs
pub fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 6 {
eprintln!("Usage: cargo run -- example scan_scenario <path to a Retail directory> <game version (H2016 | HM2 | HM3)> <ioi string or hash> <path to a hashlist> <path to output file>");
return;
}
let mut scan: ScenarioScan = ScenarioScan::new(args[1].clone(), args[2].clone(), args[3].clone(), args[4].clone());
scan.scan_scenario();
scan.output_to_file(args[5].clone());
return;
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod scanner;
220 changes: 2 additions & 218 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,223 +1,7 @@
use itertools::Itertools;
use rpkg_rs::misc::hash_path_list::PathList;
use rpkg_rs::misc::ini_file_system::IniFileSystem;
use rpkg_rs::misc::resource_id::ResourceID;
use rpkg_rs::resource::partition_manager::{PartitionManager, PartitionState};
use rpkg_rs::resource::pdefs::PackageDefinitionSource;
use rpkg_rs::resource::resource_info::ResourceInfo;
use rpkg_rs::resource::resource_partition::PatchId;
use rpkg_rs::resource::runtime_resource_id::RuntimeResourceID;
use std::collections::{HashSet, VecDeque};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{env, io};
use std::env;

// Based on mount_game_files example from rpkg-rs
pub fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 5 {
eprintln!("Usage: cargo run <path to a Retail directory> <game version (H2016 | HM2 | HM3)> <ioi string or hash> <path to a hashlist>");
return;
}

let retail_path = PathBuf::from(&args[1]);
let thumbs_path = retail_path.join("thumbs.dat");

let thumbs = IniFileSystem::from(&thumbs_path.as_path()).unwrap_or_else(|err| {
eprintln!("Error reading thumbs file: {:?}", err);
std::process::exit(1);
});

let app_options = &thumbs.root()["application"];

let hash_list_path = Path::new(&args[4]);

let mut path_list = PathList::new();
path_list.parse_into(hash_list_path).unwrap();


if let (Some(proj_path), Some(relative_runtime_path)) = (
app_options.options().get("PROJECT_PATH"),
app_options.options().get("RUNTIME_PATH"),
) {
let runtime_path = PathBuf::from(format!(
"{}\\{proj_path}\\{relative_runtime_path}",
retail_path.display()
));
std::println!("start reading package definitions {:?}", runtime_path);

let mut package_manager = PartitionManager::new(runtime_path.clone());

//read the packagedefs here
let mut last_index = 0;
let mut progress = 0.0;
let progress_callback = |current, state: &PartitionState| {
if current != last_index {
last_index = current;
print!("Mounting partition {} ", current);
}
let install_progress = (state.install_progress * 10.0).ceil() / 10.0;

let chars_to_add = (install_progress * 10.0 - progress * 10.0) as usize * 2;
let chars_to_add = std::cmp::min(chars_to_add, 20);
print!("{}", "█".repeat(chars_to_add));
io::stdout().flush().unwrap();

progress = install_progress;

if progress == 1.0 {
progress = 0.0;
println!(" done :)");
}
};

let package_defs_bytes =
std::fs::read(runtime_path.join("packagedefinition.txt").as_path()).unwrap();

let mut package_defs = match args[2].as_str() {
"HM2016" => PackageDefinitionSource::HM2016(package_defs_bytes).read(),
"HM2" => PackageDefinitionSource::HM2(package_defs_bytes).read(),
"HM3" => PackageDefinitionSource::HM3(package_defs_bytes).read(),
e => {
eprintln!("invalid game version: {}", e);
std::process::exit(0);
}
}
.unwrap_or_else(|e| {
println!("Failed to parse package definitions {}", e);
std::process::exit(0);
});

//ignore modded patches
for partition in package_defs.iter_mut() {
partition.set_max_patch_level(301);
}

package_manager
.mount_partitions(
PackageDefinitionSource::Custom(package_defs),
progress_callback,
)
.unwrap_or_else(|e| {
eprintln!("failed to init package manager: {}", e);
std::process::exit(0);
});

let ioi_string_or_hash = args[3].as_str();
let mut hash;
let hash_resource_id = RuntimeResourceID::from_hex_string(ioi_string_or_hash);
if hash_resource_id.is_err() {
let ioi_string_resource_id = ResourceID::from_str(ioi_string_or_hash);
if !ioi_string_resource_id.is_err() {
hash = RuntimeResourceID::from_resource_id(&ioi_string_resource_id.unwrap()).to_hex_string();
} else {
println!("Invalid RuntimeResourceId");
std::process::exit(0);
}
} else {
hash = ioi_string_or_hash.to_string();
}
let mut hashes: VecDeque<String> = VecDeque::from([String::from_str(&hash).unwrap()]);
let mut found_hashes = HashSet::new();
println!("Getting ALOCs for: {}", hash);
loop {
if hashes.len() == 0 {
break;
}
hash = hashes.pop_front().unwrap();
let rrid = RuntimeResourceID::from_hex_string(&hash).unwrap_or_else(|_| {
println!("Invalid RuntimeResourceId");
std::process::exit(0);
});
if found_hashes.contains(&rrid) {
continue;
}
found_hashes.insert(rrid);
let resource_package_opt = get_resource_info(&package_manager, &rrid);
if resource_package_opt.is_none() {
continue;
}
let ioi_string = if path_list.get(&rrid).is_some() {
path_list.get(&rrid).unwrap().resource_path()
} else {
"".to_string()
};

let resource_package = resource_package_opt.unwrap();
let references = resource_package.0.references();
// println!("{} {} Type: {} Partition: {}", rrid, ioi_string, resource_package.0.data_type(), resource_package.1);

for reference in references.iter() {
let dep_rrid = reference.0;

if found_hashes.contains(&dep_rrid) {
continue;
}
let depend_resource_opt = get_resource_info(&package_manager, &dep_rrid);
if depend_resource_opt.is_none() {
continue;
}
let dep_ioi_string = if path_list.get(&dep_rrid).is_some() {
path_list.get(&dep_rrid).unwrap().resource_path()
} else {
"".to_string()
};

let depend_resource = depend_resource_opt.unwrap();
if Vec::from(["TEMP", "ALOC", "PRIM"]).contains(&depend_resource.0.data_type().as_str()) {
let is_aloc = depend_resource.0.data_type().as_str() == "ALOC";
// println!("|-> {} {} Type: {} Partition: {}", dep_rrid, dep_ioi_string, depend_resource.0.data_type(), depend_resource.1);

if is_aloc {
println!("{} {} Type: {} Partition: {}", rrid, ioi_string, resource_package.0.data_type(), resource_package.1);

println!("|-> {} {} Type: {} Partition: {}", dep_rrid, dep_ioi_string, depend_resource.0.data_type(), depend_resource.1);
}
hashes.push_back(dep_rrid.to_hex_string());
}
}
}
} else {
eprintln!(
"Missing required properties inside thumbs.dat:\n\
PROJECT_PATH: {}\n\
RUNTIME_PATH: {}",
app_options.has_option("PROJECT_PATH"),
app_options.has_option("RUNTIME_PATH")
);
return;
}
}

fn get_resource_info(package_manager: &PartitionManager, rrid: &RuntimeResourceID) -> Option<(ResourceInfo, String)> {
let mut last_occurrence: Option<&ResourceInfo> = None;
let mut last_partition: Option<String> = None;
for partition in package_manager.partitions() {
let changes = partition.resource_patch_indices(rrid);
let deletions = partition.resource_removal_indices(rrid);
let occurrences = changes
.clone()
.into_iter()
.chain(deletions.clone().into_iter())
.collect::<Vec<PatchId>>();
for occurrence in occurrences.iter().sorted() {
if deletions.contains(occurrence) {
last_occurrence = None;
}
if changes.contains(occurrence) {
if let Ok(info) = partition.resource_info_from(rrid, *occurrence) {
last_occurrence = Some(info);
last_partition = Some(partition.partition_info().filename(*occurrence));
}
}
}
if !last_occurrence.is_none(){
break;
}
}
if last_occurrence.is_none() || last_partition.is_none() {
return None
}
return Some((last_occurrence.unwrap().clone(), last_partition.unwrap()));
}

1 change: 1 addition & 0 deletions src/scanner/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod scenario_scan;
Loading

0 comments on commit a1c23d9

Please sign in to comment.