feat: Add support for prerelease entries

This commit is contained in:
beerpsi 2024-01-03 00:34:18 +07:00
parent be3b29047c
commit 2c3d165a70
6 changed files with 75 additions and 9 deletions

2
Cargo.lock generated
View File

@ -294,7 +294,7 @@ dependencies = [
[[package]]
name = "icf-reader"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"aes",
"anyhow",

View File

@ -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

View File

@ -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;

View File

@ -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 {
// 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 {

View File

@ -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!(
@ -104,3 +122,7 @@ fn empty_string() -> String {
fn empty_version() -> Version {
Version { major: 0, minor: 0, build: 0 }
}
fn default_is_prerelease() -> bool {
false
}

View File

@ -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 } => {