use super::ApplyContext;
use crate::{Platform, Result, ZiPatchError};
use std::path::PathBuf;
pub(crate) fn expansion_folder_id(id: u16) -> String {
if id == 0 {
"ffxiv".to_string()
} else {
format!("ex{id}")
}
}
fn expansion_folder(sub_id: u16) -> String {
expansion_folder_id(sub_id >> 8)
}
fn platform_str(platform: Platform) -> Result<&'static str> {
match platform {
Platform::Win32 => Ok("win32"),
Platform::Ps3 => Ok("ps3"),
Platform::Ps4 => Ok("ps4"),
Platform::Unknown(id) => Err(ZiPatchError::UnsupportedPlatform(id)),
}
}
pub(crate) fn dat_path(
ctx: &ApplyContext,
main_id: u16,
sub_id: u16,
file_id: u32,
) -> Result<PathBuf> {
let platform = platform_str(ctx.platform)?;
Ok(ctx
.game_path
.join("sqpack")
.join(expansion_folder(sub_id))
.join(format!("{main_id:02x}{sub_id:04x}.{platform}.dat{file_id}")))
}
pub(crate) fn index_path(
ctx: &ApplyContext,
main_id: u16,
sub_id: u16,
file_id: u32,
) -> Result<PathBuf> {
let platform = platform_str(ctx.platform)?;
let suffix = if file_id == 0 {
String::new()
} else {
file_id.to_string()
};
Ok(ctx
.game_path
.join("sqpack")
.join(expansion_folder(sub_id))
.join(format!(
"{main_id:02x}{sub_id:04x}.{platform}.index{suffix}"
)))
}
#[must_use]
pub(crate) fn generic_path(ctx: &ApplyContext, relative_path: &str) -> PathBuf {
ctx.game_path.join(relative_path)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::apply::ApplyContext;
fn ctx(game: &str) -> ApplyContext {
ApplyContext::new(game)
}
#[test]
fn dat_expansion_0() {
let result = dat_path(&ctx("/game"), 0x00, 0x0000, 0).unwrap();
assert_eq!(
result,
PathBuf::from("/game/sqpack/ffxiv/000000.win32.dat0")
);
}
#[test]
fn dat_expansion_1() {
let result = dat_path(&ctx("/game"), 0x04, 0x0100, 2).unwrap();
assert_eq!(result, PathBuf::from("/game/sqpack/ex1/040100.win32.dat2"));
}
#[test]
fn index_expansion_0_file_id_0() {
let result = index_path(&ctx("/game"), 0x00, 0x0000, 0).unwrap();
assert_eq!(
result,
PathBuf::from("/game/sqpack/ffxiv/000000.win32.index")
);
}
#[test]
fn index_expansion_0_file_id_1() {
let result = index_path(&ctx("/game"), 0x00, 0x0000, 1).unwrap();
assert_eq!(
result,
PathBuf::from("/game/sqpack/ffxiv/000000.win32.index1")
);
}
#[test]
fn index_expansion_1_file_id_0() {
let result = index_path(&ctx("/game"), 0x04, 0x0100, 0).unwrap();
assert_eq!(result, PathBuf::from("/game/sqpack/ex1/040100.win32.index"));
}
#[test]
fn index_expansion_1_file_id_1() {
let result = index_path(&ctx("/game"), 0x04, 0x0100, 1).unwrap();
assert_eq!(
result,
PathBuf::from("/game/sqpack/ex1/040100.win32.index1")
);
}
#[test]
fn dat_ps3_platform() {
let mut ctx = ApplyContext::new("/game");
ctx.platform = Platform::Ps3;
assert_eq!(
dat_path(&ctx, 0x00, 0x0000, 0).unwrap(),
PathBuf::from("/game/sqpack/ffxiv/000000.ps3.dat0")
);
}
#[test]
fn dat_ps4_platform() {
let mut ctx = ApplyContext::new("/game");
ctx.platform = Platform::Ps4;
assert_eq!(
dat_path(&ctx, 0x00, 0x0000, 0).unwrap(),
PathBuf::from("/game/sqpack/ffxiv/000000.ps4.dat0")
);
}
#[test]
fn dat_expansion_2() {
let result = dat_path(&ctx("/game"), 0x08, 0x0200, 0).unwrap();
assert_eq!(result, PathBuf::from("/game/sqpack/ex2/080200.win32.dat0"));
}
#[test]
fn dat_path_returns_unsupported_platform_for_unknown() {
let mut c = ApplyContext::new("/game");
c.platform = Platform::Unknown(99);
let err = dat_path(&c, 0x00, 0x0000, 0)
.expect_err("unknown platform must abort dat_path resolution");
match err {
ZiPatchError::UnsupportedPlatform(id) => assert_eq!(
id, 99,
"error must carry the raw platform_id for diagnostics"
),
other => panic!("expected UnsupportedPlatform(99), got {other:?}"),
}
}
#[test]
fn index_path_returns_unsupported_platform_for_unknown() {
let mut c = ApplyContext::new("/game");
c.platform = Platform::Unknown(7);
let err = index_path(&c, 0x00, 0x0000, 1)
.expect_err("unknown platform must abort index_path resolution");
match err {
ZiPatchError::UnsupportedPlatform(id) => assert_eq!(id, 7),
other => panic!("expected UnsupportedPlatform(7), got {other:?}"),
}
}
}