use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use bmpf2fnt::{build_font_atlas, generate_bmfont, BmpfFont};
use crate::registry::register_ron;
use crate::script::{format_script_ref, write_script_lua};
use crate::util::{relative_path, write_ron};
pub fn convert_inf_to_ron(
inf_path: &Path,
input_root: &Path,
stage_root: &Path,
entries: &mut [inf2ron::InfEntry],
registry: &mut HashMap<String, Vec<String>>,
debug: bool,
) -> Result<()> {
let ron_rel = relative_path(inf_path, input_root).with_extension("ron");
let ron_path = stage_root.join(&ron_rel);
if let Some(parent) = ron_path.parent() {
fs::create_dir_all(parent)?;
}
let mut seq = 0usize;
for entry in entries.iter_mut() {
if let Some(script_blocks) = entry.blocks.get_mut("script") {
for block in script_blocks.iter_mut() {
if let inf2ron::BlockContent::Text(script_s2s) = &mut block.content {
seq += 1;
let s2s = std::mem::take(script_s2s);
write_script_lua(inf_path, input_root, stage_root, seq, &s2s, debug)?;
block.content = inf2ron::BlockContent::Text(format_script_ref(&ron_rel, seq));
}
}
}
}
let entries_slice: &[inf2ron::InfEntry] = &*entries;
write_ron(&ron_path, entries_slice)?;
register_ron(&ron_rel, registry);
if debug {
eprintln!(" {:?} → {:?} ({} entries, {} scripts)", inf_path, ron_path, entries.len(), seq);
}
Ok(())
}
pub fn convert_b3d_to_glb(
b3d_path: &Path,
glb_path: &Path,
game_root: &Path,
_stem: &str,
) -> Result<()> {
let data = fs::read(b3d_path).with_context(|| format!("reading {:?}", b3d_path))?;
let b3d = b3d2glb::b3d_parser::B3D::read(&data)
.map_err(|e| anyhow::anyhow!("B3D parse error in {:?}: {}", b3d_path, e))?;
let mesh = b3d2glb::b3d::collect_mesh(&b3d);
let mut joints = Vec::new();
let mut vertex_joint = vec![None; mesh.positions.len()];
b3d2glb::b3d::collect_joints(&b3d.node, None, &mut joints, &mut vertex_joint, mesh.positions.len(), true);
let clips = b3d2glb::b3d::collect_anims(&b3d.node);
let tex_cache = glb_path.parent().unwrap_or(Path::new(".")).join(".tex_cache");
fs::create_dir_all(&tex_cache)?;
b3d2glb::writer::write_glb(
&mesh,
&joints,
&clips,
&b3d.textures,
&b3d.brushes,
_stem,
game_root,
&tex_cache,
glb_path,
None, None, )
.map_err(|e| anyhow::anyhow!("GLB conversion error in {:?}: {}", b3d_path, e))?;
Ok(())
}
pub fn convert_bmp_to_png(bmp_path: &Path, png_path: &Path) -> Result<()> {
let img = image::ImageReader::open(bmp_path)
.with_context(|| format!("opening {:?}", bmp_path))?
.decode()
.with_context(|| format!("decoding {:?}", bmp_path))?;
let mut rgba = img.to_rgba8();
let tolerance = 10u8;
for pixel in rgba.pixels_mut() {
let dr = pixel[0].abs_diff(255);
let dg = pixel[1].abs_diff(0);
let db = pixel[2].abs_diff(255);
if dr <= tolerance && dg <= tolerance && db <= tolerance {
pixel[3] = 0; }
}
if let Some(parent) = png_path.parent() {
fs::create_dir_all(parent)?;
}
rgba.save(png_path)
.with_context(|| format!("saving {:?}", png_path))?;
Ok(())
}
pub fn convert_bmpf_to_fnt(
bmpf_path: &Path,
bmp_path: &Path,
fnt_path: &Path,
png_path: &Path,
stem: &str,
_input_root: &Path,
) -> Result<()> {
let bmpf_data = fs::read(bmpf_path)
.with_context(|| format!("reading {:?}", bmpf_path))?;
let bmpf = BmpfFont::parse(&bmpf_data)
.map_err(|e| anyhow::anyhow!("invalid .bmpf {:?}: {e}", bmpf_path))?;
let img = image::ImageReader::open(bmp_path)
.with_context(|| format!("opening {:?}", bmp_path))?
.decode()
.with_context(|| format!("decoding {:?}", bmp_path))?;
let rgba = img.to_rgba8();
let (img_w, img_h) = rgba.dimensions();
let pixels = rgba.into_raw();
let atlas = build_font_atlas(&pixels, img_w, img_h, &bmpf)?;
convert_bmp_to_png(bmp_path, png_path)?;
let png_rel = if let Some(fnt_parent) = fnt_path.parent() {
pathdiff::diff_paths(png_path, fnt_parent)
.unwrap_or_else(|| PathBuf::from(stem).with_extension("png"))
} else {
PathBuf::from(stem).with_extension("png")
};
let fnt_content = generate_bmfont(&atlas, stem, png_rel.to_string_lossy().as_ref());
fs::write(fnt_path, &fnt_content)
.with_context(|| format!("writing {:?}", fnt_path))?;
Ok(())
}
pub fn convert_s2_to_osmap(
s2_path: &Path,
osmap_path: &Path,
input_root: &Path,
) -> Result<String> {
let source_name = s2_path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("map");
let data = fs::read(s2_path)
.with_context(|| format!("reading {:?}", s2_path))?;
let s2 = openstranded_map_tool::parse_s2(&data)
.map_err(|e| anyhow::anyhow!("failed to parse {:?}: {}", s2_path, e))?;
let source_filename = source_name.to_string() + ".s2";
let osmap = openstranded_map_tool::s2_to_osmap(s2, &source_filename);
if let Some(parent) = osmap_path.parent() {
fs::create_dir_all(parent)?;
}
openstranded_map_tool::save_osmap_ron(&osmap, osmap_path)?;
let orig_rel = super::util::relative_path(s2_path, input_root)
.to_string_lossy()
.to_string();
Ok(orig_rel)
}