mirror of
https://github.com/redstrate/Physis.git
synced 2025-04-26 14:17:45 +00:00
Handle patching error handling better
* Move PlatformId to common module. * Platform id is no longer hardcoded to win32. * Parse apply options correctly. * Parse target info better, use more native structures. * All errors are now handled properly, and propagated up. * Added a new Region enum. * Added todo!() to all unhandled patching branches. * Paths are now built using PathBuf instead of format!(). * process_patch function is no longer accessible, now you have to use the function found in GameData and BootData.
This commit is contained in:
parent
03e3d3ca21
commit
18322a13d2
5 changed files with 180 additions and 90 deletions
|
@ -1,9 +1,13 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use crate::gamedata::MemoryBuffer;
|
||||||
|
use crate::patch::apply_patch;
|
||||||
|
|
||||||
/// Boot data for FFXIV.
|
/// Boot data for FFXIV.
|
||||||
pub struct BootData {
|
pub struct BootData {
|
||||||
version: String,
|
path : String,
|
||||||
|
|
||||||
|
pub version : String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_valid(path: &str) -> bool {
|
fn is_valid(path: &str) -> bool {
|
||||||
|
@ -32,6 +36,7 @@ impl BootData {
|
||||||
pub fn from_existing(directory: &str) -> Option<BootData> {
|
pub fn from_existing(directory: &str) -> Option<BootData> {
|
||||||
match is_valid(directory) {
|
match is_valid(directory) {
|
||||||
true => Some(BootData {
|
true => Some(BootData {
|
||||||
|
path: directory.parse().unwrap(),
|
||||||
version: String::new()
|
version: String::new()
|
||||||
}),
|
}),
|
||||||
false => {
|
false => {
|
||||||
|
@ -40,4 +45,8 @@ impl BootData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn apply_patch(&self, patch_path : &str) {
|
||||||
|
apply_patch(&self.path, patch_path);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use binrw::binread;
|
use binrw::binrw;
|
||||||
|
|
||||||
#[binread]
|
#[binrw]
|
||||||
#[br(repr(u8))]
|
#[brw(repr(u8))]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
/// The language the game data is written for.
|
/// The language the game data is written for.
|
||||||
pub enum Language {
|
pub enum Language {
|
||||||
|
@ -35,4 +35,29 @@ pub fn get_language_code(lang: &Language) -> &'static str {
|
||||||
Language::ChineseTraditional => "cht",
|
Language::ChineseTraditional => "cht",
|
||||||
Language::Korean => "ko"
|
Language::Korean => "ko"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[brw(repr = u16)]
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum PlatformId {
|
||||||
|
Windows,
|
||||||
|
PS3,
|
||||||
|
PS4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_platform_string(id: &PlatformId) -> &'static str {
|
||||||
|
match &id {
|
||||||
|
PlatformId::Windows => "win32",
|
||||||
|
PlatformId::PS3 => "ps3", // TODO: lol are these even correct? i have no idea
|
||||||
|
PlatformId::PS4 => "ps4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[brw(repr = i16)]
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Region {
|
||||||
|
Global = -1
|
||||||
|
// TODO: find patch codes for other regions :-)
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::exd::EXD;
|
||||||
use crate::exh::EXH;
|
use crate::exh::EXH;
|
||||||
use crate::exl::EXL;
|
use crate::exl::EXL;
|
||||||
use crate::index::IndexFile;
|
use crate::index::IndexFile;
|
||||||
|
use crate::patch::{apply_patch, PatchError};
|
||||||
use crate::repository::{Category, Repository, string_to_category};
|
use crate::repository::{Category, Repository, string_to_category};
|
||||||
use crate::sqpack::calculate_hash;
|
use crate::sqpack::calculate_hash;
|
||||||
|
|
||||||
|
@ -211,6 +212,10 @@ impl GameData {
|
||||||
|
|
||||||
EXD::from_existing(&exh, &exd_file)
|
EXD::from_existing(&exh, &exd_file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn apply_patch(&self, patch_path : &str) -> Result<(), PatchError> {
|
||||||
|
apply_patch(&self.game_directory, patch_path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
10
src/index.rs
10
src/index.rs
|
@ -2,15 +2,7 @@ use std::io::SeekFrom;
|
||||||
use binrw::binrw;
|
use binrw::binrw;
|
||||||
use binrw::BinRead;
|
use binrw::BinRead;
|
||||||
use bitfield_struct::bitfield;
|
use bitfield_struct::bitfield;
|
||||||
|
use crate::common::PlatformId;
|
||||||
#[binrw]
|
|
||||||
#[brw(repr = u8)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum PlatformId {
|
|
||||||
Win32,
|
|
||||||
PS3,
|
|
||||||
PS4,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[br(magic = b"SqPack")]
|
#[br(magic = b"SqPack")]
|
||||||
|
|
211
src/patch.rs
211
src/patch.rs
|
@ -5,7 +5,19 @@ use binrw::BinRead;
|
||||||
use binrw::binread;
|
use binrw::binread;
|
||||||
use crate::sqpack::read_data_block_patch;
|
use crate::sqpack::read_data_block_patch;
|
||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
use crate::patch::TargetHeaderKind::Version;
|
use std::path::PathBuf;
|
||||||
|
use crate::common::{get_platform_string, PlatformId, Region};
|
||||||
|
|
||||||
|
#[binread]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PatchHeader {
|
||||||
|
#[br(temp)]
|
||||||
|
#[br(count = 7)]
|
||||||
|
#[br(pad_before = 1)]
|
||||||
|
#[br(pad_after = 4)]
|
||||||
|
#[br(assert(magic == b"ZIPATCH"))]
|
||||||
|
magic : Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(BinRead, Debug)]
|
#[derive(BinRead, Debug)]
|
||||||
struct PatchChunk {
|
struct PatchChunk {
|
||||||
|
@ -66,16 +78,25 @@ struct FileHeaderChunk3 {
|
||||||
sqpk_delete_commands: u32,
|
sqpk_delete_commands: u32,
|
||||||
sqpk_expand_commands: u32,
|
sqpk_expand_commands: u32,
|
||||||
sqpk_header_commands: u32,
|
sqpk_header_commands: u32,
|
||||||
|
|
||||||
#[br(pad_after = 0xB8)]
|
#[br(pad_after = 0xB8)]
|
||||||
sqpk_file_commands: u32
|
sqpk_file_commands: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[binread]
|
||||||
|
#[br(repr = u32)]
|
||||||
|
#[br(big)]
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum ApplyOption {
|
||||||
|
IgnoreMissing = 1,
|
||||||
|
IgnoreOldMismatch = 2,
|
||||||
|
}
|
||||||
|
|
||||||
#[binrw::binread]
|
#[binrw::binread]
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
struct ApplyOptionChunk {
|
struct ApplyOptionChunk {
|
||||||
#[br(pad_after = 4)]
|
#[br(pad_after = 4)]
|
||||||
option: u32,
|
option: ApplyOption,
|
||||||
|
#[br(big)]
|
||||||
value: u32,
|
value: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,9 +252,10 @@ struct SqpkFileOperationData {
|
||||||
#[br(big)]
|
#[br(big)]
|
||||||
struct SqpkTargetInfo {
|
struct SqpkTargetInfo {
|
||||||
#[br(pad_before = 3)]
|
#[br(pad_before = 3)]
|
||||||
platform : u16,
|
platform : PlatformId,
|
||||||
region : i16,
|
region : Region,
|
||||||
is_debug : i16,
|
#[br(map = | x : i16 | x == 1)]
|
||||||
|
is_debug : bool,
|
||||||
version : u16,
|
version : u16,
|
||||||
#[br(little)]
|
#[br(little)]
|
||||||
deleted_data_size : u64,
|
deleted_data_size : u64,
|
||||||
|
@ -251,39 +273,43 @@ struct SqpkChunk {
|
||||||
|
|
||||||
const WIPE_BUFFER: [u8; 1 << 16] = [0; 1 << 16];
|
const WIPE_BUFFER: [u8; 1 << 16] = [0; 1 << 16];
|
||||||
|
|
||||||
fn wipe(mut file : &File, length : i32) {
|
fn wipe(mut file : &File, length : i32) -> Result<(), PatchError> {
|
||||||
let mut length = length;
|
let mut length = length;
|
||||||
while length > 0 {
|
while length > 0 {
|
||||||
let num_bytes = min(WIPE_BUFFER.len() as i32, length);
|
let num_bytes = min(WIPE_BUFFER.len() as i32, length);
|
||||||
file.write(&WIPE_BUFFER[0..num_bytes as usize]);
|
file.write(&WIPE_BUFFER[0..num_bytes as usize])?;
|
||||||
length -= num_bytes;
|
length -= num_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wipe_from_offset(mut file : &File, length : i32, offset : i32) {
|
fn wipe_from_offset(mut file : &File, length : i32, offset : i32) -> Result<(), PatchError> {
|
||||||
file.seek(SeekFrom::Start(offset as u64));
|
file.seek(SeekFrom::Start(offset as u64))?;
|
||||||
wipe(file, length);
|
wipe(file, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_empty_file_block_at(mut file : &File, offset : i32, block_number : i32) {
|
fn write_empty_file_block_at(mut file : &File, offset : i32, block_number : i32) -> Result<(), PatchError> {
|
||||||
wipe_from_offset(file, block_number << 7, offset);
|
wipe_from_offset(file, block_number << 7, offset)?;
|
||||||
|
|
||||||
file.seek(SeekFrom::Start(offset as u64));
|
file.seek(SeekFrom::Start(offset as u64))?;
|
||||||
|
|
||||||
let block_size : i32 = 1 << 7;
|
let block_size : i32 = 1 << 7;
|
||||||
file.write(block_size.to_le_bytes().as_slice());
|
file.write(block_size.to_le_bytes().as_slice())?;
|
||||||
|
|
||||||
let unknown : i32 = 0;
|
let unknown : i32 = 0;
|
||||||
file.write(unknown.to_le_bytes().as_slice());
|
file.write(unknown.to_le_bytes().as_slice())?;
|
||||||
|
|
||||||
let file_size : i32 = 0;
|
let file_size : i32 = 0;
|
||||||
file.write(file_size.to_le_bytes().as_slice());
|
file.write(file_size.to_le_bytes().as_slice())?;
|
||||||
|
|
||||||
let num_blocks : i32 = block_number - 1;
|
let num_blocks : i32 = block_number - 1;
|
||||||
file.write(num_blocks.to_le_bytes().as_slice());
|
file.write(num_blocks.to_le_bytes().as_slice())?;
|
||||||
|
|
||||||
let used_blocks : i32 = 0;
|
let used_blocks : i32 = 0;
|
||||||
file.write(used_blocks.to_le_bytes().as_slice());
|
file.write(used_blocks.to_le_bytes().as_slice())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_expansion_folder(sub_id : u16) -> String {
|
fn get_expansion_folder(sub_id : u16) -> String {
|
||||||
|
@ -291,102 +317,122 @@ fn get_expansion_folder(sub_id : u16) -> String {
|
||||||
|
|
||||||
match expansion_id {
|
match expansion_id {
|
||||||
0 => "ffxiv".to_string(),
|
0 => "ffxiv".to_string(),
|
||||||
x => format!("ex{}", x)
|
n => format!("ex{}", n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dat_filename(data_dir : &str, main_id : u16, sub_id : u16, file_id : u32) -> String {
|
#[derive(Debug)]
|
||||||
format!("{}/sqpack/{}/{:02x}{:04x}.win32.dat{}", data_dir, get_expansion_folder(sub_id), main_id, sub_id, file_id)
|
pub enum PatchError {
|
||||||
|
InvalidPatchFile,
|
||||||
|
ParseError
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_index_filename(data_dir : &str, main_id : u16, sub_id : u16, file_id : u32) -> String {
|
impl From<std::io::Error> for PatchError {
|
||||||
// FIXME: don't hardcode win32 here. target/patchinfo contains the correct platform :-)
|
fn from(_: std::io::Error) -> Self {
|
||||||
let mut path = format!("{}/sqpack/{}/{:02x}{:04x}.win32.index", data_dir, get_expansion_folder(sub_id), main_id, sub_id);
|
PatchError::InvalidPatchFile
|
||||||
|
|
||||||
// index files have no special ending if it's file_id == 0
|
|
||||||
if file_id != 0 {
|
|
||||||
path += &*format!("{}", file_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_patch(data_dir : &str, path : &str) {
|
impl From<binrw::Error> for PatchError {
|
||||||
let mut file = File::open(path).unwrap();
|
fn from(_: binrw::Error) -> Self {
|
||||||
|
PatchError::ParseError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
file.seek(SeekFrom::Start(12));
|
/// Applies a boot or a game patch to the specified _data_dir_.
|
||||||
|
pub(crate) fn apply_patch(data_dir : &str, patch_path : &str) -> Result<(), PatchError> {
|
||||||
|
let mut file = File::open(patch_path)?;
|
||||||
|
|
||||||
|
PatchHeader::read(&mut file)?;
|
||||||
|
|
||||||
|
let mut target_info : Option<SqpkTargetInfo> = None;
|
||||||
|
|
||||||
|
let get_dat_path = |target_info : &SqpkTargetInfo, main_id : u16, sub_id : u16, file_id : u32| -> String {
|
||||||
|
let filename = format!("{:02x}{:04x}.{}.dat{}", main_id, sub_id, get_platform_string(&target_info.platform), file_id);
|
||||||
|
let path : PathBuf = [data_dir, "sqpack", &get_expansion_folder(sub_id), &filename].iter().collect();
|
||||||
|
|
||||||
|
path.to_str().unwrap().to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let get_index_path = |target_info : &SqpkTargetInfo, main_id : u16, sub_id : u16, file_id : u32| -> String {
|
||||||
|
let mut filename = format!("{:02x}{:04x}.{}.index", main_id, sub_id, get_platform_string(&target_info.platform));
|
||||||
|
|
||||||
|
// index files have no special ending if it's file_id == 0
|
||||||
|
if file_id != 0 {
|
||||||
|
filename += &*format!("{}", file_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let path : PathBuf = [data_dir, "sqpack", &get_expansion_folder(sub_id), &filename].iter().collect();
|
||||||
|
|
||||||
|
path.to_str().unwrap().to_string()
|
||||||
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let chunk = PatchChunk::read(&mut file).unwrap();
|
let chunk = PatchChunk::read(&mut file)?;
|
||||||
|
|
||||||
match chunk.chunk_type {
|
match chunk.chunk_type {
|
||||||
ChunkType::Sqpk(pchunk) => {
|
ChunkType::Sqpk(pchunk) => {
|
||||||
match pchunk.operation {
|
match pchunk.operation {
|
||||||
SqpkOperation::AddData(add) => {
|
SqpkOperation::AddData(add) => {
|
||||||
let filename = get_dat_filename(data_dir, add.main_id, add.sub_id, add.file_id);
|
let filename = get_dat_path(&target_info.as_ref().unwrap(), add.main_id, add.sub_id, add.file_id);
|
||||||
|
|
||||||
let (left, _) = filename.rsplit_once('/').unwrap();
|
let (left, _) = filename.rsplit_once('/').unwrap();
|
||||||
fs::create_dir_all(left);
|
fs::create_dir_all(left)?;
|
||||||
|
|
||||||
let mut new_file = OpenOptions::new()
|
let mut new_file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(filename).unwrap();
|
.open(filename)?;
|
||||||
|
|
||||||
new_file.seek(SeekFrom::Start(add.block_offset as u64));
|
new_file.seek(SeekFrom::Start(add.block_offset as u64))?;
|
||||||
|
|
||||||
new_file.write(&*add.block_data);
|
new_file.write(&*add.block_data)?;
|
||||||
|
|
||||||
wipe(&mut new_file, add.block_delete_number);
|
wipe(&mut new_file, add.block_delete_number)?;
|
||||||
}
|
}
|
||||||
SqpkOperation::DeleteData(delete) => {
|
SqpkOperation::DeleteData(delete) => {
|
||||||
let filename = get_dat_filename(data_dir, delete.main_id, delete.sub_id, delete.file_id);
|
let filename = get_dat_path(&target_info.as_ref().unwrap(), delete.main_id, delete.sub_id, delete.file_id);
|
||||||
|
|
||||||
let mut new_file = OpenOptions::new()
|
let mut new_file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(filename).unwrap();
|
.open(filename)?;
|
||||||
|
|
||||||
write_empty_file_block_at(&mut new_file, delete.block_offset, delete.block_number);
|
write_empty_file_block_at(&mut new_file, delete.block_offset, delete.block_number)?;
|
||||||
}
|
}
|
||||||
SqpkOperation::ExpandData(expand) => {
|
SqpkOperation::ExpandData(expand) => {
|
||||||
let filename = get_dat_filename(data_dir, expand.main_id, expand.sub_id, expand.file_id);
|
let filename = get_dat_path(&target_info.as_ref().unwrap(), expand.main_id, expand.sub_id, expand.file_id);
|
||||||
|
|
||||||
let (left, _) = filename.rsplit_once('/').unwrap();
|
let (left, _) = filename.rsplit_once('/').unwrap();
|
||||||
fs::create_dir_all(left);
|
fs::create_dir_all(left)?;
|
||||||
|
|
||||||
let mut new_file = OpenOptions::new()
|
let mut new_file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(filename).unwrap();
|
.open(filename)?;
|
||||||
|
|
||||||
write_empty_file_block_at(&mut new_file, expand.block_offset, expand.block_number);
|
write_empty_file_block_at(&mut new_file, expand.block_offset, expand.block_number)?;
|
||||||
}
|
}
|
||||||
SqpkOperation::HeaderUpdate(header) => {
|
SqpkOperation::HeaderUpdate(header) => {
|
||||||
let file_path : String;
|
let file_path = match header.file_kind {
|
||||||
|
TargetFileKind::Dat => get_dat_path(&target_info.as_ref().unwrap(), header.main_id, header.sub_id, header.file_id),
|
||||||
|
TargetFileKind::Index => get_index_path(&target_info.as_ref().unwrap(), header.main_id, header.sub_id, header.file_id)
|
||||||
|
};
|
||||||
|
|
||||||
match header.file_kind {
|
let (left, _) = file_path.rsplit_once('/')
|
||||||
TargetFileKind::Dat => {
|
.ok_or(PatchError::ParseError)?;
|
||||||
file_path = get_dat_filename(data_dir, header.main_id, header.sub_id, header.file_id)
|
fs::create_dir_all(left)?;
|
||||||
}
|
|
||||||
TargetFileKind::Index => {
|
|
||||||
file_path = get_index_filename(data_dir, header.main_id, header.sub_id, header.file_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (left, _) = file_path.rsplit_once('/').unwrap();
|
|
||||||
fs::create_dir_all(left);
|
|
||||||
|
|
||||||
let mut new_file = OpenOptions::new()
|
let mut new_file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(file_path.as_str()).unwrap();
|
.open(file_path)?;
|
||||||
|
|
||||||
if header.header_kind != Version {
|
if header.header_kind != TargetHeaderKind::Version {
|
||||||
new_file.seek(SeekFrom::Start(1024));
|
new_file.seek(SeekFrom::Start(1024))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_file.write(&*header.header_data);
|
new_file.write(&*header.header_data)?;
|
||||||
}
|
}
|
||||||
SqpkOperation::FileOperation(fop) => {
|
SqpkOperation::FileOperation(fop) => {
|
||||||
match fop.operation {
|
match fop.operation {
|
||||||
|
@ -395,10 +441,10 @@ pub fn process_patch(data_dir : &str, path : &str) {
|
||||||
|
|
||||||
let (left, _) = new_path.rsplit_once('/').unwrap();
|
let (left, _) = new_path.rsplit_once('/').unwrap();
|
||||||
|
|
||||||
fs::create_dir_all(left);
|
fs::create_dir_all(left)?;
|
||||||
|
|
||||||
// reverse reading crc32
|
// reverse reading crc32
|
||||||
file.seek(SeekFrom::Current(-4));
|
file.seek(SeekFrom::Current(-4))?;
|
||||||
|
|
||||||
let mut data: Vec<u8> = Vec::with_capacity(fop.file_size as usize);
|
let mut data: Vec<u8> = Vec::with_capacity(fop.file_size as usize);
|
||||||
|
|
||||||
|
@ -407,34 +453,47 @@ pub fn process_patch(data_dir : &str, path : &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// re-apply crc32
|
// re-apply crc32
|
||||||
file.seek(SeekFrom::Current(4));
|
file.seek(SeekFrom::Current(4))?;
|
||||||
|
|
||||||
// now apply the file!
|
// now apply the file!
|
||||||
let mut new_file = OpenOptions::new()
|
let mut new_file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(new_path).unwrap();
|
.open(new_path)?;
|
||||||
new_file.seek(SeekFrom::Start(fop.offset as u64));
|
|
||||||
|
|
||||||
new_file.write(&mut data);
|
new_file.seek(SeekFrom::Start(fop.offset as u64))?;
|
||||||
|
new_file.write(&mut data)?;
|
||||||
}
|
}
|
||||||
SqpkFileOperation::DeleteFile => {
|
SqpkFileOperation::DeleteFile => {
|
||||||
let new_path = data_dir.to_owned() + "/" + &fop.path;
|
let new_path = data_dir.to_owned() + "/" + &fop.path;
|
||||||
|
|
||||||
fs::remove_file(new_path.as_str());
|
fs::remove_file(new_path.as_str())?;
|
||||||
}
|
}
|
||||||
SqpkFileOperation::RemoveAll => {
|
SqpkFileOperation::RemoveAll => {
|
||||||
println!("STUB: SqpkFileOperation::RemoveAll");
|
println!("have to remove all files in {}...", fop.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
SqpkOperation::IndexAddDelete(_) => todo!(),
|
||||||
|
SqpkOperation::PatchInfo(patch_info) => {
|
||||||
|
println!("Got patch info: {:#?}", patch_info);
|
||||||
|
},
|
||||||
|
SqpkOperation::TargetInfo(new_target_info) => {
|
||||||
|
target_info = Some(new_target_info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
ChunkType::FileHeader(header) => {
|
||||||
|
println!("Got file header: {:#?}", header);
|
||||||
|
},
|
||||||
|
ChunkType::ApplyOption(option) => {
|
||||||
|
println!("apply option: {:#?}", option);
|
||||||
|
},
|
||||||
|
ChunkType::AddDirectory(_) => todo!(),
|
||||||
|
ChunkType::DeleteDirectory(_) => todo!(),
|
||||||
ChunkType::EndOfFile => {
|
ChunkType::EndOfFile => {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue