1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-04-20 11:47:46 +00:00
physis/docs/architecture.md
2022-10-26 17:25:32 -04:00

2.3 KiB

Architecture

This is a document going over my various design decisions I made with physis, to onboard future contributors and also point a light at possible mistakes and what we can do to solve them!

Goals

I think it's important to go over what problems physis is set up to solve. It's meant to:

  • Make patching easier for custom launchers
  • Users who want to write one-off tools that interact with the game data (like scrapers)
  • Modding tools that want to read and write game data in a safe way

Physis is all about game data, reading, writing and modifying it. However, it's keen to keep it safe and prevent invalid data from being written, and if there is invalid data being read - to make it obvious to the developer. So there is a high-level representation of a file format, which is built on top of our custom parsers:

#[derive(Debug)]
pub struct IndexEntry {
    pub hash: u64,
    pub data_file_id: u8,
    pub offset: u32,
}

#[binrw]
#[br(little)]
pub struct IndexFile {
    sqpack_header: SqPackHeader,

    #[br(seek_before = SeekFrom::Start(sqpack_header.size.into()))]
    index_header: SqPackIndexHeader,

    #[br(seek_before = SeekFrom::Start(index_header.index_data_offset.into()))]
    #[br(count = index_header.index_data_size / 16)]
    pub entries: Vec<IndexHashTableEntry>,
}

Here's a section of src/index.rs that showcases an example of this methodology, headers and other auto-generated data is hidden by the developer (as there is very little to worry about there anyway) and they only need access to modifying and reading entries. There is still work to be done to improve upon this, but this is the general idea when writing for game format parsing.

Top-level Architecture

There is a bunch of methods to crack open your dat files, but the best way is to GameData and BootData. These two structures help parse and ensure boot and game data is valid.

There is helper methods in GameData such as extract(path) and exists(path) which are wrappers around other public APIs, but make it easier for developers to get started.

When parsing game data, you'll notice many formats only accept a memory buffer, which is just a Vec<u8>. These are passed as references, and are purely non-owning. The purpose of this is because some files are not backed by a file on disk, but instead are extracted and processed entirely in memory.