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]]
|
[[package]]
|
||||||
name = "icf-reader"
|
name = "icf-reader"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "icf-reader"
|
name = "icf-reader"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -35,12 +35,14 @@ interface IcfInnerData {
|
|||||||
version: Version,
|
version: Version,
|
||||||
required_system_version: Version,
|
required_system_version: Version,
|
||||||
datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
||||||
|
is_prerelease: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IcfOptionData {
|
interface IcfOptionData {
|
||||||
type: "Option",
|
type: "Option",
|
||||||
option_id: string, // Must be 4 characters
|
option_id: string, // Must be 4 characters
|
||||||
datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
||||||
|
is_prerelease: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IcfPatchData {
|
interface IcfPatchData {
|
||||||
@ -55,6 +57,8 @@ interface IcfPatchData {
|
|||||||
target_version: Version,
|
target_version: Version,
|
||||||
target_datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
target_datetime: string, // ISO8601 string yyyy-MM-dd'T'HH:mm:ss
|
||||||
target_required_system_version: Version,
|
target_required_system_version: Version,
|
||||||
|
|
||||||
|
is_prerelease: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
type IcfData = IcfInnerData | IcfOptionData | IcfPatchData;
|
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);
|
let mut entries: Vec<IcfData> = Vec::with_capacity(entry_count);
|
||||||
for _ in 0..entry_count {
|
for _ in 0..entry_count {
|
||||||
let sig = rd.read_bytes(4)?;
|
let sig = rd.read_u16()?;
|
||||||
if sig[0] != 2 || sig[1] != 1 {
|
if sig != 0x0102 && sig != 0x0201 {
|
||||||
return Err(anyhow!(
|
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
|
rd.pos
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let _ = rd.read_bytes(2)?;
|
||||||
|
|
||||||
|
let is_prerelease = sig == 0x0201;
|
||||||
|
|
||||||
let container_type = rd.read_u32()?;
|
let container_type = rd.read_u32()?;
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
@ -162,12 +165,14 @@ pub fn parse_icf(data: impl AsRef<[u8]>) -> Result<Vec<IcfData>> {
|
|||||||
version,
|
version,
|
||||||
datetime,
|
datetime,
|
||||||
required_system_version,
|
required_system_version,
|
||||||
|
is_prerelease,
|
||||||
}),
|
}),
|
||||||
0x0001 => IcfData::App(IcfInnerData {
|
0x0001 => IcfData::App(IcfInnerData {
|
||||||
id: app_id.clone(),
|
id: app_id.clone(),
|
||||||
version,
|
version,
|
||||||
datetime,
|
datetime,
|
||||||
required_system_version,
|
required_system_version,
|
||||||
|
is_prerelease,
|
||||||
}),
|
}),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -188,6 +193,7 @@ pub fn parse_icf(data: impl AsRef<[u8]>) -> Result<Vec<IcfData>> {
|
|||||||
option_id,
|
option_id,
|
||||||
datetime,
|
datetime,
|
||||||
required_system_version,
|
required_system_version,
|
||||||
|
is_prerelease,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -219,6 +225,7 @@ pub fn parse_icf(data: impl AsRef<[u8]>) -> Result<Vec<IcfData>> {
|
|||||||
target_version,
|
target_version,
|
||||||
target_datetime,
|
target_datetime,
|
||||||
target_required_system_version,
|
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;
|
let mut app_id: Option<String> = None;
|
||||||
|
|
||||||
for container in data {
|
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 {
|
match container {
|
||||||
IcfData::System(s) => {
|
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")),
|
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 {
|
let app_id = match app_id {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => return Err(anyhow!("Missing entry of type App in provided ICF data")),
|
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;
|
let mut containers_checksum: u32 = 0;
|
||||||
for container in icf.chunks(0x40).skip(1) {
|
for container in icf.chunks(0x40).skip(1) {
|
||||||
if container[0] == 2 && container[1] == 1 {
|
if container[0] == 2 && container[1] == 1 {
|
||||||
|
@ -22,6 +22,9 @@ pub struct IcfInnerData {
|
|||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub required_system_version: Version,
|
pub required_system_version: Version,
|
||||||
pub datetime: NaiveDateTime,
|
pub datetime: NaiveDateTime,
|
||||||
|
|
||||||
|
#[serde(default = "default_is_prerelease")]
|
||||||
|
pub is_prerelease: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@ -35,6 +38,9 @@ pub struct IcfOptionData {
|
|||||||
pub required_system_version: Version,
|
pub required_system_version: Version,
|
||||||
|
|
||||||
pub datetime: NaiveDateTime,
|
pub datetime: NaiveDateTime,
|
||||||
|
|
||||||
|
#[serde(default = "default_is_prerelease")]
|
||||||
|
pub is_prerelease: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@ -51,6 +57,9 @@ pub struct IcfPatchData {
|
|||||||
pub target_version: Version,
|
pub target_version: Version,
|
||||||
pub target_datetime: NaiveDateTime,
|
pub target_datetime: NaiveDateTime,
|
||||||
pub target_required_system_version: Version,
|
pub target_required_system_version: Version,
|
||||||
|
|
||||||
|
#[serde(default = "default_is_prerelease")]
|
||||||
|
pub is_prerelease: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@ -63,6 +72,15 @@ pub enum IcfData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn filename(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
IcfData::System(data) => format!(
|
IcfData::System(data) => format!(
|
||||||
@ -104,3 +122,7 @@ fn empty_string() -> String {
|
|||||||
fn empty_version() -> Version {
|
fn empty_version() -> Version {
|
||||||
Version { major: 0, minor: 0, build: 0 }
|
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)?;
|
let icf = parse_icf(&icf_buf)?;
|
||||||
|
|
||||||
for entry in icf {
|
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)?;
|
let encrypted_icf = encrypt_icf(&icf_buf, ICF_KEY, ICF_IV)?;
|
||||||
@ -72,7 +78,13 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for entry in icf {
|
for entry in icf {
|
||||||
println!("{}", entry.filename())
|
print!("{}", entry.filename());
|
||||||
|
|
||||||
|
if entry.is_prerelease() {
|
||||||
|
print!(" (PRERELEASE)");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Commands::Encode { json_input, output } => {
|
Commands::Encode { json_input, output } => {
|
||||||
|
Loading…
Reference in New Issue
Block a user