mirror of
https://gitea.tendokyu.moe/beerpsi/icf-reader.git
synced 2024-11-27 17:00:48 +01:00
feat: Add support for prerelease entries
This commit is contained in:
parent
be3b29047c
commit
2c3d165a70
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -294,7 +294,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icf-reader"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "icf-reader"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -35,12 +35,14 @@ interface IcfInnerData {
|
||||
version: Version,
|
||||
required_system_version: Version,
|
||||
datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
||||
is_prerelease: boolean,
|
||||
}
|
||||
|
||||
interface IcfOptionData {
|
||||
type: "Option",
|
||||
option_id: string, // Must be 4 characters
|
||||
datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
||||
is_prerelease: boolean,
|
||||
}
|
||||
|
||||
interface IcfPatchData {
|
||||
@ -55,6 +57,8 @@ interface IcfPatchData {
|
||||
target_version: Version,
|
||||
target_datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
||||
target_required_system_version: Version,
|
||||
|
||||
is_prerelease: boolean,
|
||||
}
|
||||
|
||||
type IcfData = IcfInnerData | IcfOptionData | IcfPatchData;
|
||||
|
@ -129,13 +129,16 @@ pub fn parse_icf(data: impl AsRef<[u8]>) -> Result<Vec<IcfData>> {
|
||||
|
||||
let mut entries: Vec<IcfData> = Vec::with_capacity(entry_count);
|
||||
for _ in 0..entry_count {
|
||||
let sig = rd.read_bytes(4)?;
|
||||
if sig[0] != 2 || sig[1] != 1 {
|
||||
let sig = rd.read_u16()?;
|
||||
if sig != 0x0102 && sig != 0x0201 {
|
||||
return Err(anyhow!(
|
||||
"Container does not start with signature (0x0102), byte {:#06x}",
|
||||
"Container does not start with signature (0x0102 or 0x0201), byte {:#06x}",
|
||||
rd.pos
|
||||
));
|
||||
}
|
||||
let _ = rd.read_bytes(2)?;
|
||||
|
||||
let is_prerelease = sig == 0x0201;
|
||||
|
||||
let container_type = rd.read_u32()?;
|
||||
for _ in 0..3 {
|
||||
@ -162,12 +165,14 @@ pub fn parse_icf(data: impl AsRef<[u8]>) -> Result<Vec<IcfData>> {
|
||||
version,
|
||||
datetime,
|
||||
required_system_version,
|
||||
is_prerelease,
|
||||
}),
|
||||
0x0001 => IcfData::App(IcfInnerData {
|
||||
id: app_id.clone(),
|
||||
version,
|
||||
datetime,
|
||||
required_system_version,
|
||||
is_prerelease,
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -188,6 +193,7 @@ pub fn parse_icf(data: impl AsRef<[u8]>) -> Result<Vec<IcfData>> {
|
||||
option_id,
|
||||
datetime,
|
||||
required_system_version,
|
||||
is_prerelease,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
@ -219,6 +225,7 @@ pub fn parse_icf(data: impl AsRef<[u8]>) -> Result<Vec<IcfData>> {
|
||||
target_version,
|
||||
target_datetime,
|
||||
target_required_system_version,
|
||||
is_prerelease,
|
||||
})
|
||||
}
|
||||
};
|
||||
@ -263,7 +270,20 @@ pub fn serialize_icf(data: &[IcfData]) -> Result<Vec<u8>> {
|
||||
let mut app_id: Option<String> = None;
|
||||
|
||||
for container in data {
|
||||
icf.extend([0x02, 0x01, 0x00, 0x00]);
|
||||
// I don't think there's a really good way to do this?
|
||||
// At least, not without breaking backwards compatibility.
|
||||
let is_prerelease = match container {
|
||||
IcfData::System(s) => s.is_prerelease,
|
||||
IcfData::App(a) => a.is_prerelease,
|
||||
IcfData::Option(o) => o.is_prerelease,
|
||||
IcfData::Patch(p) => p.is_prerelease,
|
||||
};
|
||||
|
||||
if is_prerelease {
|
||||
icf.extend([0x01, 0x02, 0x00, 0x00]);
|
||||
} else {
|
||||
icf.extend([0x02, 0x01, 0x00, 0x00]);
|
||||
}
|
||||
|
||||
match container {
|
||||
IcfData::System(s) => {
|
||||
@ -316,11 +336,19 @@ pub fn serialize_icf(data: &[IcfData]) -> Result<Vec<u8>> {
|
||||
None => return Err(anyhow!("Missing entry of type System in provided ICF data")),
|
||||
};
|
||||
|
||||
if platform_id.len() != 3 {
|
||||
return Err(anyhow!("Incorrect platform ID length: expected 3, got {}", platform_id.len()));
|
||||
}
|
||||
|
||||
let app_id = match app_id {
|
||||
Some(s) => s,
|
||||
None => return Err(anyhow!("Missing entry of type App in provided ICF data")),
|
||||
};
|
||||
|
||||
if app_id.len() != 4 {
|
||||
return Err(anyhow!("Incorrect app ID length: expected 4, got {}", app_id.len()));
|
||||
}
|
||||
|
||||
let mut containers_checksum: u32 = 0;
|
||||
for container in icf.chunks(0x40).skip(1) {
|
||||
if container[0] == 2 && container[1] == 1 {
|
||||
|
@ -22,6 +22,9 @@ pub struct IcfInnerData {
|
||||
pub version: Version,
|
||||
pub required_system_version: Version,
|
||||
pub datetime: NaiveDateTime,
|
||||
|
||||
#[serde(default = "default_is_prerelease")]
|
||||
pub is_prerelease: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@ -35,6 +38,9 @@ pub struct IcfOptionData {
|
||||
pub required_system_version: Version,
|
||||
|
||||
pub datetime: NaiveDateTime,
|
||||
|
||||
#[serde(default = "default_is_prerelease")]
|
||||
pub is_prerelease: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@ -51,6 +57,9 @@ pub struct IcfPatchData {
|
||||
pub target_version: Version,
|
||||
pub target_datetime: NaiveDateTime,
|
||||
pub target_required_system_version: Version,
|
||||
|
||||
#[serde(default = "default_is_prerelease")]
|
||||
pub is_prerelease: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@ -63,6 +72,15 @@ pub enum IcfData {
|
||||
}
|
||||
|
||||
impl IcfData {
|
||||
pub fn is_prerelease(&self) -> bool {
|
||||
match self {
|
||||
IcfData::System(s) => s.is_prerelease,
|
||||
IcfData::App(a) => a.is_prerelease,
|
||||
IcfData::Option(o) => o.is_prerelease,
|
||||
IcfData::Patch(p) => p.is_prerelease,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filename(&self) -> String {
|
||||
match self {
|
||||
IcfData::System(data) => format!(
|
||||
@ -103,4 +121,8 @@ fn empty_string() -> String {
|
||||
|
||||
fn empty_version() -> Version {
|
||||
Version { major: 0, minor: 0, build: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
fn default_is_prerelease() -> bool {
|
||||
false
|
||||
}
|
||||
|
16
src/main.rs
16
src/main.rs
@ -49,7 +49,13 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
let icf = parse_icf(&icf_buf)?;
|
||||
|
||||
for entry in icf {
|
||||
println!("{}", entry.filename());
|
||||
print!("{}", entry.filename());
|
||||
|
||||
if entry.is_prerelease() {
|
||||
print!(" (PRERELEASE)");
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
let encrypted_icf = encrypt_icf(&icf_buf, ICF_KEY, ICF_IV)?;
|
||||
@ -72,7 +78,13 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
}
|
||||
|
||||
for entry in icf {
|
||||
println!("{}", entry.filename())
|
||||
print!("{}", entry.filename());
|
||||
|
||||
if entry.is_prerelease() {
|
||||
print!(" (PRERELEASE)");
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
}
|
||||
Commands::Encode { json_input, output } => {
|
||||
|
Loading…
Reference in New Issue
Block a user