jmmt: add texture formats
This commit also adds a skeleton for a texture tool.
This commit is contained in:
parent
094c14a799
commit
69e82236c6
|
@ -1,7 +1,8 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
#resolver = "2"
|
#resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"crates/jmmt",
|
"crates/jmmt",
|
||||||
"crates/jmrenamer"
|
"crates/jmrenamer",
|
||||||
# "crates/paktool"
|
"crates/textool"
|
||||||
|
# "crates/paktool"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,10 +1,37 @@
|
||||||
//! Low-level structure definitions
|
//! Low-level file format definitions. These are suitable for usage with the [binext] crate,
|
||||||
|
//! which just so happens to be a dependency of this crate! Funny how things work.
|
||||||
|
|
||||||
pub mod package;
|
pub mod package;
|
||||||
pub mod package_toc;
|
pub mod package_toc;
|
||||||
|
pub mod ps2_texture;
|
||||||
|
pub mod ps2_palette;
|
||||||
|
|
||||||
/// A trait validatable format objects should implement.
|
/// A trait validatable format objects should implement.
|
||||||
|
/// TODO: integrate this with some FourCC crate, or re-invent the wheel.
|
||||||
pub trait Validatable {
|
pub trait Validatable {
|
||||||
/// Returns true if the object is valid, false otherwise.
|
/// Returns true if the object is valid, false otherwise.
|
||||||
fn valid(&self) -> bool;
|
fn valid(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make a Rust [String] from a byte slice that came from a C string/structure.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
///
|
||||||
|
/// The byte slice has to be a valid UTF-8 string.
|
||||||
|
/// (Note that in most cases, ASCII strings are valid UTF-8, so this isn't something you'll particularly
|
||||||
|
/// have to worry about).
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function does not directly make use of any unsafe Rust code.
|
||||||
|
pub fn make_c_string(bytes: &[u8]) -> Option<String> {
|
||||||
|
let bytes_without_null = match bytes.iter().position(|&b| b == 0) {
|
||||||
|
Some(ix) => &bytes[..ix],
|
||||||
|
None => bytes,
|
||||||
|
};
|
||||||
|
|
||||||
|
match std::str::from_utf8(bytes_without_null).ok() {
|
||||||
|
Some(string) => Some(String::from(string)),
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub struct PackageEofHeader {
|
||||||
|
|
||||||
/// A Package Group. I have no idea what this is yet
|
/// A Package Group. I have no idea what this is yet
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
pub struct PackageGroup {
|
pub struct PackageGroup {
|
||||||
pub fourcc: u32,
|
pub fourcc: u32,
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ impl Validatable for PackageGroup {
|
||||||
|
|
||||||
/// A package file chunk.
|
/// A package file chunk.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
pub struct PackageFileChunk {
|
pub struct PackageFileChunk {
|
||||||
pub fourcc: u32,
|
pub fourcc: u32,
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Package.toc structures
|
//! Package.toc structures
|
||||||
|
|
||||||
|
use super::make_c_string;
|
||||||
|
|
||||||
/// An entry inside the `package.toc` file
|
/// An entry inside the `package.toc` file
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -15,7 +17,7 @@ pub struct PackageTocEntry {
|
||||||
|
|
||||||
impl PackageTocEntry {
|
impl PackageTocEntry {
|
||||||
fn file_name(&self) -> Option<String> {
|
fn file_name(&self) -> Option<String> {
|
||||||
String::from_utf8(self.file_name.to_vec()).ok()
|
make_c_string(&self.file_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_name_hash(&self) -> u32 {
|
fn file_name_hash(&self) -> u32 {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
//! .ps2_palette structures
|
||||||
|
|
||||||
|
use super::Validatable;
|
||||||
|
|
||||||
|
/// .ps2_palette header
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Ps2PaletteHeader {
|
||||||
|
pub fourcc: u32,
|
||||||
|
pub unk: u32,
|
||||||
|
pub unk2: u16,
|
||||||
|
|
||||||
|
pub color_count: u16,
|
||||||
|
pub palette_bpp: u16,
|
||||||
|
pub unk3: u16,
|
||||||
|
|
||||||
|
pub data_start: u32,
|
||||||
|
pub header_size: u32,
|
||||||
|
|
||||||
|
pub pad: [u32; 6] // reserved for game code, like .ps2_texture?
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ps2PaletteHeader {
|
||||||
|
/// 'PAL1'
|
||||||
|
pub const VALID_FOURCC : u32 = 0x314c4150;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Validatable for Ps2PaletteHeader {
|
||||||
|
fn valid(&self) -> bool {
|
||||||
|
self.fourcc == Self::VALID_FOURCC && self.header_size == 0x18
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
//! .ps2_texture structures
|
||||||
|
|
||||||
|
use super::Validatable;
|
||||||
|
|
||||||
|
/// .ps2_texture header.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Ps2TextureHeader {
|
||||||
|
pub magic: u32,
|
||||||
|
pub unk: u32,
|
||||||
|
pub unk2: u16,
|
||||||
|
pub width: u16,
|
||||||
|
pub height: u16,
|
||||||
|
|
||||||
|
/// bits-per-pixel of the texture data. Anything above 8
|
||||||
|
/// will not have an associated .ps2_palette file,
|
||||||
|
/// since the texture data will not be indirect color.
|
||||||
|
pub bpp: u16,
|
||||||
|
|
||||||
|
/// Data start offset.
|
||||||
|
pub data_start_offset: u32,
|
||||||
|
|
||||||
|
/// Possibly the size of this header.
|
||||||
|
pub header_end_offset: u32,
|
||||||
|
|
||||||
|
pub unk6: [u32; 8], // mostly unrelated values, this is probably padding space for the game code to put stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ps2TextureHeader {
|
||||||
|
/// 'TEX1'
|
||||||
|
pub const VALID_FOURCC: u32 = 0x31584554;
|
||||||
|
|
||||||
|
fn has_palette(&self) -> bool {
|
||||||
|
// if the BPP is less than or equal to 8 (I've only seen 8bpp and 16bpp),
|
||||||
|
// then the texture will be palettized.
|
||||||
|
self.bpp >= 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Validatable for Ps2TextureHeader {
|
||||||
|
fn valid(&self) -> bool {
|
||||||
|
self.magic == Self::VALID_FOURCC && self.header_end_offset == 0x38
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,3 +10,6 @@ pub mod lzss;
|
||||||
// higher level I/O?
|
// higher level I/O?
|
||||||
// pub mod read;
|
// pub mod read;
|
||||||
// pub mod write;
|
// pub mod write;
|
||||||
|
|
||||||
|
// Maybe, using package.toc?
|
||||||
|
// pub mod pakfs;
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::mem::size_of;
|
||||||
use crate::format::Validatable;
|
use crate::format::Validatable;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
pub struct LzssHeader {
|
pub struct LzssHeader {
|
||||||
pub next: u32, // ps2 ptr. usually 0 cause theres no next header
|
pub next: u32, // ps2 ptr. usually 0 cause theres no next header
|
||||||
pub byte_id: u8,
|
pub byte_id: u8,
|
||||||
|
@ -25,4 +26,4 @@ impl Validatable for LzssHeader {
|
||||||
fn valid(&self) -> bool {
|
fn valid(&self) -> bool {
|
||||||
self.byte_id == 0x91 && self.header_size as usize == size_of::<LzssHeader>()
|
self.byte_id == 0x91 && self.header_size as usize == size_of::<LzssHeader>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "textool"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.3.8", features = ["derive"] }
|
||||||
|
image = "0.24.6"
|
||||||
|
jmmt = { path = "../jmmt" }
|
||||||
|
|
||||||
|
# Temporary, until the reader code is thrown into jmmt crate
|
||||||
|
binext = "1.0.0"
|
|
@ -0,0 +1,65 @@
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
use std::default;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::fs::{
|
||||||
|
File
|
||||||
|
};
|
||||||
|
|
||||||
|
use jmmt::format::{
|
||||||
|
ps2_palette::*,
|
||||||
|
ps2_texture::*
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
#[command(propagate_version = true)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Commands {
|
||||||
|
/// Exports the texture to a .png file
|
||||||
|
Export { path: String },
|
||||||
|
|
||||||
|
//Import
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImageReader {
|
||||||
|
file: File,
|
||||||
|
header: Ps2TextureHeader,
|
||||||
|
pal_header: Ps2PaletteHeader,
|
||||||
|
|
||||||
|
image_data: Vec<u8>,
|
||||||
|
palette_data: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageReader {
|
||||||
|
fn new(file: &mut File) -> Self {
|
||||||
|
ImageReader {
|
||||||
|
file: file.try_clone().unwrap(),
|
||||||
|
header: Ps2TextureHeader::default(),
|
||||||
|
pal_header: Ps2PaletteHeader::default(),
|
||||||
|
image_data: Vec::default(),
|
||||||
|
palette_data: Vec::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self) -> std::io::Result<()> {
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
match &cli.command {
|
||||||
|
Commands::Export { path } => {
|
||||||
|
println!("exporting {}", path);
|
||||||
|
let path = Path::new(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue