Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 86 additions & 85 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "gcmod"
version = "0.1.0"
edition = "2021"
authors = ["Addison Bean <addisonbean@gmail.com>"]

[dependencies]
byteorder = "1"
clap = "2"
tempfile = "2.2.0"
lazy_static = "1.0"
regex = "1"
33 changes: 18 additions & 15 deletions src/game.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
use std::collections::BTreeMap;
use std::fs::{create_dir, File};
use std::io::{self, BufRead, Seek};
use std::path::Path;

use sections::apploader::{Apploader, APPLOADER_OFFSET};
use sections::dol::DOLHeader;
use sections::dol::segment::Segment;
use sections::fst::{
entry::DirectoryEntry,
FST,
use std::{
collections::BTreeMap,
fs::{create_dir, File},
io::{self, BufRead, Seek},
path::Path
};
use sections::header::{GAME_HEADER_SIZE, Header};
use ::{

use crate::{
format_u64,
NumberStyle,
paths::*,
sections::{
apploader::{Apploader, APPLOADER_OFFSET},
dol::{segment::Segment, DOLHeader},
fst::{
entry::DirectoryEntry,
FST,
},
header::{Header, GAME_HEADER_SIZE},
Section,
},
NumberStyle,
};

use sections::Section;

pub const ROM_SIZE: usize = 0x57058000;

Expand Down
35 changes: 15 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
extern crate byteorder;
#[macro_use]
extern crate lazy_static;
extern crate regex;

use std::borrow::Cow;
use std::cmp::min;
use std::fmt;
use std::io::{self, Read, Write};
use std::num::ParseIntError;
use std::{
borrow::Cow,
cmp::min,
fmt,
io::{self, Read, Write},
num::ParseIntError,
};

mod game;
pub use game::Game;
pub use game::ROM_SIZE;

mod rom_rebuilder;
pub mod sections;

mod rom_rebuilder;
pub use game::{Game, ROM_SIZE};
pub use rom_rebuilder::ROMRebuilder;

// 1048576 = 2^20 = 1MiB, there's no real good reason behind this choice
pub const WRITE_CHUNK_SIZE: usize = 1048576;
pub const WRITE_CHUNK_SIZE: usize = 1048576;

// 32KiB
pub const DEFAULT_ALIGNMENT: u64 = 32 * 1024;
pub const DEFAULT_ALIGNMENT: u64 = 32 * 1024;
pub const MIN_ALIGNMENT: u64 = 4;

pub mod paths {
pub const APPLOADER_PATH: &'static str = "&&systemdata/Apploader.ldr";
pub const DOL_PATH: &'static str = "&&systemdata/Start.dol";
pub const FST_PATH: &'static str = "&&systemdata/Game.toc";
pub const HEADER_PATH: &'static str = "&&systemdata/ISO.hdr";
pub const APPLOADER_PATH: &str = "&&systemdata/Apploader.ldr";
pub const DOL_PATH: &str = "&&systemdata/Start.dol";
pub const FST_PATH: &str = "&&systemdata/Game.toc";
pub const HEADER_PATH: &str = "&&systemdata/ISO.hdr";
}

pub fn extract_section(
Expand Down
21 changes: 9 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
#[macro_use]
extern crate clap;
extern crate gcmod;
extern crate tempfile;

use std::fs::{remove_file, File};
use std::io::BufReader;
use std::path::Path;
use std::{
fs::{remove_file, File},
io::BufReader,
path::Path,
};

use clap::AppSettings;
use clap::{clap_app, AppSettings};

use gcmod::{
AppError,
Expand All @@ -20,6 +17,7 @@ use gcmod::{
NumberStyle,
parse_as_u64,
ROM_SIZE,
ROMRebuilder,
sections::{
apploader::Apploader,
dol::DOLHeader,
Expand All @@ -28,7 +26,6 @@ use gcmod::{
Section,
},
};
use gcmod::ROMRebuilder;

fn main() -> AppResult {
let app = clap_app!(app =>
Expand Down Expand Up @@ -72,13 +69,13 @@ fn main() -> AppResult {
).setting(AppSettings::SubcommandRequired);

match app.get_matches().subcommand() {
("extract", Some(cmd)) =>
("extract", Some(cmd)) =>
extract_iso(
cmd.value_of("rom_path").unwrap(),
cmd.value_of("output").unwrap(),
cmd.value_of("rom_section"),
),
("info", Some(cmd)) =>
("info", Some(cmd)) =>
get_info(
cmd.value_of("rom_path").unwrap(),
cmd.value_of("type"),
Expand Down
67 changes: 36 additions & 31 deletions src/rom_rebuilder.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
use std::cmp;
use std::fs::{File, read_dir};
use std::io::{self, BufReader, Write};
use std::path::{self, Path, PathBuf};
use std::sync::Mutex;

use sections::apploader::APPLOADER_OFFSET;
use sections::fst::{
FST,
entry::{DirectoryEntry, Entry, EntryInfo, FileEntry},
use std::{
cmp,
fs::{read_dir, File},
io::{self, BufReader, Write},
iter,
path::{self, Path, PathBuf},
sync::OnceLock,
};
use sections::header::Header;
use ::{

use crate::{
align,
DEFAULT_ALIGNMENT,
extract_section,
paths::*,
sections::{
apploader::APPLOADER_OFFSET,
fst::{
entry::{DirectoryEntry, Entry, EntryInfo, FileEntry},
FST
},
header::Header,
},
DEFAULT_ALIGNMENT,
WRITE_CHUNK_SIZE,
};

Expand Down Expand Up @@ -129,7 +134,7 @@ impl<'a> FSTRebuilder<'a> {
size,
};
let fst_path = self.config.root_path.join(FST_PATH);
fst.write(File::create(&fst_path)?)?;
fst.write(File::create(fst_path)?)?;

self.config.space_used = Some(max_eof);

Expand Down Expand Up @@ -178,11 +183,11 @@ impl<'a> FSTRebuilder<'a> {
let filename = e.file_name();
let filename = filename.to_string_lossy();

if FSTRebuilder::is_file_ignored(&*filename) {
if FSTRebuilder::is_file_ignored(&filename) {
continue
}

let index = rb_info.entries.len() as usize;
let index = rb_info.entries.len();
let info = EntryInfo {
index,
name: filename.clone().into_owned(),
Expand Down Expand Up @@ -234,8 +239,8 @@ impl<'a> HeaderRebuilder<'a> {
let header_buf = BufReader::new(File::open(&header_path)?);
let mut header = Header::new(header_buf, 0)?;

header.dol_offset = self.dol_offset as u64;
header.fst_offset = self.fst.offset as u64;
header.dol_offset = self.dol_offset;
header.fst_offset = self.fst.offset;
header.fst_size = self.fst.size;

// TODO: Is this okay to assume?
Expand Down Expand Up @@ -351,15 +356,15 @@ impl ROMRebuilder {
let total_files = self.files.len();

for (i, &(offset, ref filename)) in self.files.iter().enumerate() {
let mut file = File::open(filename)?;
let file = File::open(filename)?;
let size = file.metadata()?.len();

if size == 0 { continue }

write_zeros((offset - bytes_written) as usize, &mut output)?;
bytes_written = offset;

extract_section(&mut file, size as usize, &mut output)?;
extract_section(&file, size as usize, &mut output)?;
bytes_written += size;

if bytes_written as usize > ROM_SIZE {
Expand All @@ -386,17 +391,17 @@ impl ROMRebuilder {
}
}

fn write_zeros(count: usize, mut output: impl Write) -> io::Result<()> {
lazy_static! {
static ref ZEROS: Mutex<Vec<u8>> = Mutex::new(vec![]);
}
let mut zeros = ZEROS.lock().unwrap();
let block_size = cmp::min(count, WRITE_CHUNK_SIZE);
zeros.resize(block_size, 0);
for i in 0..(count / WRITE_CHUNK_SIZE + 1) {
(&mut output).write_all(
&zeros[..cmp::min(WRITE_CHUNK_SIZE, count - WRITE_CHUNK_SIZE * i)]
)?;
fn write_zeros(mut remaining: usize, mut output: impl Write) -> io::Result<()> {
static ZEROS: OnceLock<Box<[u8]>> = OnceLock::new();
let zeros = ZEROS.get_or_init(||
iter::repeat(0).take(WRITE_CHUNK_SIZE).collect()
);

while remaining > 0 {
let count = cmp::min(WRITE_CHUNK_SIZE, remaining);
output.write_all(&zeros[..count])?;
remaining -= count;
}

Ok(())
}
7 changes: 3 additions & 4 deletions src/sections/apploader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ use std::io::{self, Read, Seek, SeekFrom, Write};

use byteorder::{BigEndian, ReadBytesExt};

use ::{
use crate::{
align,
extract_section,
format_u64,
format_usize,
NumberStyle,
sections::Section,
};

use sections::Section;

pub const APPLOADER_OFFSET: u64 = 0x2440;
const APPLOADER_DATE_SIZE: usize = 0x0A;
const APPLOADER_ENTRY_POINT_ADDR: u64 = 0x2450;
Expand All @@ -35,7 +34,7 @@ impl Apploader {
reader.seek(SeekFrom::Start(offset))?;
let mut date = String::new();
reader.take(APPLOADER_DATE_SIZE as u64).read_to_string(&mut date)?;

reader.seek(SeekFrom::Current(6))?; // it's just fluff

let entry_point = reader.read_u32::<BigEndian>()? as u64;
Expand Down
17 changes: 8 additions & 9 deletions src/sections/dol/mod.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
pub mod segment;

use std::cmp::max;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::iter::Iterator;
use std::{
cmp::max,
io::{self, Read, Seek, SeekFrom, Write},
};

use byteorder::{BigEndian, ReadBytesExt};

use ::{
use crate::{
extract_section,
format_u64,
format_usize,
sections::Section,
NumberStyle,
};

use self::segment::{Segment, SegmentType};

use sections::Section;
pub mod segment;
use segment::{Segment, SegmentType};

const TEXT_SEG_COUNT: usize = 7;
const DATA_SEG_COUNT: usize = 11;
Expand Down
34 changes: 15 additions & 19 deletions src/sections/dol/segment.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use std::io::{Write, Seek, SeekFrom, Read, self};

use regex::Regex;

use ::{format_u64, format_usize, NumberStyle, parse_as_u64, extract_section};
use sections::Section;
use crate::{
extract_section,
format_u64,
format_usize,
parse_as_u64,
sections::Section,
NumberStyle,
};

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum SegmentType {
Expand Down Expand Up @@ -60,21 +64,13 @@ impl Segment {
}

pub fn parse_segment_name(name: &str) -> Option<(SegmentType, u64)> {
use self::SegmentType::*;
lazy_static! {
static ref SEG_NAME_REGEX: Regex =
Regex::new(r"^\.?(text|data)(\d+)$").unwrap();
}
SEG_NAME_REGEX.captures(name).and_then(|c| {
parse_as_u64(c.get(2).unwrap().as_str()).map(|n| {
let t = match c.get(1).unwrap().as_str() {
"text" => Text,
"data" => Data,
_ => unreachable!(),
};
(t, n)
}).ok()
})
let (kind, suffix) =
if let Some(suffix) = name.strip_prefix(".text") { (SegmentType::Text, suffix) }
else if let Some(suffix) = name.strip_prefix(".data") { (SegmentType::Data, suffix) }
else { return None; };

let n = parse_as_u64(suffix).ok()?;
Some((kind, n))
}

// TODO: put in a trait
Expand Down
Loading