Skip to content

Commit cb52414

Browse files
committed
🌟: first commit
0 parents  commit cb52414

File tree

6 files changed

+433
-0
lines changed

6 files changed

+433
-0
lines changed

Diff for: .gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/target
2+
/.idea
3+
/output
4+
/Image
5+
/package-file
6+
/RESERVED
7+
/BOOT
8+
/embedded-update.img
9+
/update.img
10+
/outputpackage-file
11+
/out

Diff for: Cargo.lock

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "afptool-rs"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
anyhow = "1.0.71"

Diff for: src/lib.rs

+297
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
use std::{mem, str};
2+
use std::fs::{create_dir_all, File};
3+
use std::io::{Read, Seek, Write};
4+
use std::path::Path;
5+
use std::ffi::CStr;
6+
use anyhow::{anyhow, Result};
7+
8+
pub const RKAFP_MAGIC: &str = "RKAF";
9+
pub const PARM_MAGIC: &str = "PARM";
10+
pub const MAX_PARTS: usize = 16;
11+
pub const MAX_NAME_LEN: usize = 32;
12+
const MAX_FULL_PATH_LEN: usize = 60;
13+
const MAX_MODEL_LEN: usize = 34;
14+
const MAX_ID_LEN: usize = 30;
15+
const MAX_MANUFACTURER_LEN: usize = 56;
16+
pub const RKAF_SIGNATURE: &[u8] = b"RKAF";
17+
pub const RKFW_SIGNATURE: &[u8] = b"RKFW";
18+
pub const RKFP_SIGNATURE: &[u8] = b"RKFP";
19+
20+
#[derive(Copy, Clone, Debug)]
21+
#[repr(C, packed)]
22+
pub struct UpdatePart {
23+
name: [u8; MAX_NAME_LEN],
24+
pub full_path: [u8; MAX_FULL_PATH_LEN],
25+
flash_size: u32,
26+
pub part_offset: u32,
27+
flash_offset: u32,
28+
padded_size: u32,
29+
pub part_byte_count: u32,
30+
}
31+
32+
#[derive(Copy, Clone, Debug)]
33+
#[repr(C, packed)]
34+
pub struct UpdateHeader {
35+
pub magic: [u8; 4],
36+
pub length: u32,
37+
pub model: [u8; MAX_MODEL_LEN],
38+
id: [u8; MAX_ID_LEN],
39+
pub manufacturer: [u8; MAX_MANUFACTURER_LEN],
40+
unknown1: u32,
41+
version: u32,
42+
pub num_parts: u32,
43+
pub parts: [UpdatePart; MAX_PARTS],
44+
reserved: [u8; 116],
45+
}
46+
47+
#[derive(Copy, Clone, Debug)]
48+
#[repr(C, packed)]
49+
pub struct ParamHeader {
50+
magic: [u8; 4],
51+
length: u32,
52+
}
53+
54+
55+
impl UpdateHeader {
56+
pub fn default() -> Self {
57+
Self {
58+
magic: [0u8; 4],
59+
length: 0,
60+
model: [0u8; MAX_MODEL_LEN],
61+
id: [0u8; MAX_ID_LEN],
62+
manufacturer: [0u8; MAX_MANUFACTURER_LEN],
63+
unknown1: 0,
64+
version: 0,
65+
num_parts: 0,
66+
parts: [UpdatePart::default(); MAX_PARTS],
67+
reserved: [0u8; 116],
68+
69+
}
70+
}
71+
pub fn from_bytes(bytes: &[u8]) -> &UpdateHeader {
72+
unsafe { mem::transmute(bytes.as_ptr()) }
73+
}
74+
75+
pub fn to_bytes(&self) -> &[u8] {
76+
unsafe { std::slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<UpdatePart>()) }
77+
}
78+
}
79+
80+
impl UpdatePart {
81+
pub fn default() -> Self {
82+
Self {
83+
name: [0u8; MAX_NAME_LEN],
84+
full_path: [0u8; MAX_FULL_PATH_LEN],
85+
flash_size: 0,
86+
part_offset: 0,
87+
flash_offset: 0,
88+
padded_size: 0,
89+
part_byte_count: 0,
90+
}
91+
}
92+
}
93+
94+
pub fn unpack_file(file_path: &str, dst_path: &str) -> Result<()> {
95+
let mut file = File::open(file_path)?;
96+
let mut buffer = Vec::new();
97+
file.read_to_end(&mut buffer)?;
98+
99+
let signature = &buffer[0..4];
100+
match signature {
101+
RKAF_SIGNATURE => unpack_rkafp(file_path, dst_path)?,
102+
RKFW_SIGNATURE => unpack_rkfw(&buffer, dst_path)?,
103+
_ => {
104+
return Err(anyhow!("Unknown signature: {:?}", signature));
105+
}
106+
}
107+
Ok(())
108+
}
109+
110+
fn unpack_rkfw(buf: &[u8], dst_path: &str) -> Result<()> {
111+
let mut chip: Option<&str> = None;
112+
113+
println!("RKFW signature detected");
114+
println!(
115+
"version: {}.{}.{}",
116+
buf[9],
117+
buf[8],
118+
(buf[7] as u16) << 8 + buf[6] as u16
119+
);
120+
println!(
121+
"date: {}-{:02}-{:02} {:02}:{:02}:{:02}",
122+
(buf[0x0f] as u16) << 8 + buf[0x0e] as u16,
123+
buf[0x10],
124+
buf[0x11],
125+
buf[0x12],
126+
buf[0x13],
127+
buf[0x14]
128+
);
129+
130+
match buf[0x15] {
131+
0x50 => chip = Some("rk29xx"),
132+
0x60 => chip = Some("rk30xx"),
133+
0x70 => chip = Some("rk31xx"),
134+
0x80 => chip = Some("rk32xx"),
135+
0x41 => chip = Some("rk3368"),
136+
0x36 => chip = Some("RK3326"),
137+
0x38 => chip = Some("RK3566"),
138+
0x30 => chip = Some("PX30"),
139+
_ => println!(
140+
"You got a brand new chip ({:#x}), congratulations!!!",
141+
buf[0x15]
142+
),
143+
}
144+
145+
println!("family: {}", chip.unwrap_or("unknown"));
146+
147+
let ioff = get_u32_le(&buf[0x19..]);
148+
let isize: u32 = get_u32_le(&buf[0x1d..]);
149+
150+
if &buf[ioff as usize..ioff as usize + 4] != b"BOOT" {
151+
panic!("cannot find BOOT signature");
152+
}
153+
154+
println!(
155+
"{:08x}-{:08x} {:26} (size: {})",
156+
ioff,
157+
ioff + isize - 1,
158+
"BOOT",
159+
isize
160+
);
161+
create_dir_all(dst_path)?;
162+
write_file(
163+
&Path::new(format!("{}/BOOT", dst_path).as_mut()),
164+
&buf[ioff as usize..ioff as usize + (isize as usize)],
165+
)?;
166+
167+
let ioff = get_u32_le(&buf[0x21..]);
168+
let isize = get_u32_le(&buf[0x25..]);
169+
170+
if &buf[ioff as usize..ioff as usize + 4] != b"RKAF" {
171+
panic!("cannot find embedded RKAF update.img");
172+
}
173+
174+
println!(
175+
"{:08x}-{:08x} {:26} (size: {})",
176+
ioff,
177+
ioff + isize - 1,
178+
"embedded-update.img",
179+
isize
180+
);
181+
write_file(
182+
&Path::new(format!("{}/embedded-update.img", dst_path).as_mut()),
183+
&buf[ioff as usize..ioff as usize + isize as usize],
184+
)?;
185+
Ok(())
186+
}
187+
188+
pub unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
189+
core::slice::from_raw_parts(
190+
(p as *const T) as *const u8,
191+
mem::size_of::<T>(),
192+
)
193+
}
194+
195+
pub fn info_and_fatal(is_fatal: bool, message: String) {
196+
if is_fatal {
197+
eprint!("rkunpack: fatal: ");
198+
} else {
199+
eprint!("rkunpack: info: ");
200+
}
201+
eprintln!("{}", message);
202+
if is_fatal {
203+
std::process::exit(1);
204+
}
205+
}
206+
#[macro_export]
207+
macro_rules! info {
208+
($message:expr) => {
209+
info_and_fatal(false, $message);
210+
};
211+
}
212+
213+
#[macro_export]
214+
macro_rules! fatal {
215+
($message:expr) => {
216+
info_and_fatal(true, $message);
217+
};
218+
}
219+
fn extract_file(fp: &mut File, offset: u64, len: u64, full_path: &str) -> Result<()> {
220+
println!("{:08x}-{:08x} {}", offset, len, full_path);
221+
let mut buffer = vec![0u8; 16 * 1024];
222+
let mut fp_out = File::create(full_path)?;
223+
224+
fp.seek(std::io::SeekFrom::Start(offset))?;
225+
226+
let mut remaining = len;
227+
228+
while remaining > 0 {
229+
let read_len = std::cmp::min(remaining as usize, buffer.len());
230+
let read_bytes = fp.read(&mut buffer[..read_len])?;
231+
232+
if read_bytes != read_len {
233+
return Err(anyhow!("Insufficient length in container image file"));
234+
}
235+
236+
fp_out.write_all(&buffer[..read_len])?;
237+
238+
remaining -= read_len as u64;
239+
}
240+
241+
Ok(())
242+
}
243+
244+
fn unpack_rkafp(file_path: &str, dst_path: &str) -> Result<()> {
245+
let mut fp = File::open(file_path)?;
246+
let mut buf = vec![0u8; mem::size_of::<UpdateHeader>()];
247+
fp.read_exact(&mut buf)?;
248+
let header = UpdateHeader::from_bytes(buf.as_mut());
249+
let magic_str = str::from_utf8(&header.magic)?;
250+
if magic_str != RKAFP_MAGIC {
251+
return Err(anyhow!("Invalid header magic id"));
252+
}
253+
254+
let filesize = fp.metadata()?.len();
255+
println!("Filesize: {}", filesize);
256+
if filesize - 4 != header.length as u64 {
257+
eprintln!("update_header.length cannot be correct, cannot check CRC");
258+
}
259+
create_dir_all(format!("{}/Image", dst_path))?;
260+
unsafe {
261+
println!("manufacturer: {}", CStr::from_ptr(header.manufacturer.as_ptr() as *const i8).to_str()?);
262+
println!("model: {}", CStr::from_ptr(header.model.as_ptr() as *const i8).to_str()?);
263+
}
264+
for i in 0..header.num_parts {
265+
let part = &header.parts[i as usize];
266+
unsafe {
267+
let cstr = CStr::from_ptr(part.full_path.as_ptr() as *const i8);
268+
let part_full_path = cstr.to_str()?;
269+
if part_full_path == "SELF" || part_full_path == "RESERVED" {
270+
continue;
271+
}
272+
let part_full_path = format!("{}/{}", dst_path, part_full_path);
273+
extract_file(
274+
&mut fp,
275+
part.part_offset as u64,
276+
part.part_byte_count as u64,
277+
&part_full_path,
278+
)?;
279+
}
280+
}
281+
282+
Ok(())
283+
}
284+
285+
286+
fn get_u32_le(slice: &[u8]) -> u32 {
287+
((slice[3] as u32) << 24)
288+
+ ((slice[2] as u32) << 16)
289+
+ ((slice[1] as u32) << 8)
290+
+ slice[0] as u32
291+
}
292+
293+
fn write_file(path: &Path, buffer: &[u8]) -> Result<()> {
294+
let mut file = File::create(path)?;
295+
file.write_all(buffer)?;
296+
Ok(())
297+
}

Diff for: src/main.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use std::env;
2+
use afptool_rs::unpack_file;
3+
use anyhow::Result;
4+
5+
fn main() -> Result<()> {
6+
let args: Vec<String> = env::args().collect();
7+
if args.len() != 3 {
8+
eprintln!("Usage: afptool-rs <src_dir> <out_dir>");
9+
std::process::exit(1);
10+
}
11+
12+
let file_path = &args[1];
13+
let dst_path = &args[2];
14+
unpack_file(file_path, dst_path)?;
15+
Ok(())
16+
}
17+
18+
19+

0 commit comments

Comments
 (0)