1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
// Copyright 2021 Carrie J Vrtis // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! A Rust library for working with Final Fantasy XIV .DAT files. //! These files store client-side game config including macros, hotkeys, and ui settings. //! //! Libxivdat provides low-level file i/o via [`DATFile`](crate::dat_file::DATFile), //! a [`std::fs::File`]-like interface that automatically manages the header, footer, and content //! masking of DAT files. //! //! Each DAT file contains unique data structures. Higher-level support for specific file types is //! implemented on a type-by-type basis as optional features. See the chart below //! for more information and feature names. //! //! # DAT Data Structures //! //! Internally, some DAT file content blocks use a variable-length data structure referred to as a [`section`](crate::section) //! in this library. A section consists of a single UTF-8 char type tag, u16le size, and a null-terminated //! UTF-8 string. A single resource (ie, a macro) is then comprised of a repeating pattern of sections. //! Other DAT files use fixed-size resource blocks, with each resource immediately following the last. //! These are referred to as "Block DATs" below. //! //! Some DAT files contain unique binary data that does not follow the "standard" DAT format. Others contain //! UTF-8 plaintext and are not binary files at all. Support for these files is not currently planned. //! //! ## DAT Support Table //! //! | Symbol | Description | //! |--------|-----------------| //! | ✅ | Full support | //! | 🌀 | Partial support | //! | ❌ | No support | //! //! | File | Contains | Type | DATFile Read/Write | High Level Module | //! |--------------------|----------------------------------|------------|--------------------|-------------------| //! | ACQ.DAT | Recent /tell history | Section | ✅ | 🌀 - `section` | //! | ADDON.DAT | ? | Unique | ❌ | ❌ | //! | COMMON.DAT | Character configuration | Plaintext | ❌ | ❌ | //! | CONTROL0.DAT | Gamepad control config | Plaintext | ❌ | ❌ | //! | CONTROL1.DAT | Keyboard/mouse control config | Plaintext | ❌ | ❌ | //! | FFXIV_CHARA_XX.DAT | Character appearance presets | Unique | ❌ | ❌ | //! | GEARSET.DAT | Gearsets | Block | ✅ | ❌ | //! | GS.DAT | Gold Saucer config (Triad decks) | Block | ✅ | ❌ | //! | HOTBAR.DAT | Hotbar layouts | Block | ✅ | ❌ | //! | ITEMFDR.DAT | "Search for item" indexing? | Block | ✅ | ❌ | //! | ITEMODR.DAT | Item order in bags | Block | ✅ | ❌ | //! | KEYBIND.DAT | Keybinds | Section | ✅ | 🌀 - `section` | //! | LOGFLTR.DAT | Chat log filters? | Block | ✅ | ❌ | //! | MACRO.DAT | Character-specific macros | Section | ✅ | ✅ - `macro` | //! | MACROSYS.DAT | System-wide macros | Section | ✅ | ✅ - `macro` | //! | UISAVE.DAT | UI config | Block | ✅ | ❌ | //! //! # Examples: //! //! ## Reading a file: //! //! ```rust //! use libxivdat::dat_file::read_content; //! # let path_to_dat_file = "./resources/TEST.DAT"; //! let data_vec = read_content(&path_to_dat_file).unwrap(); //! ``` //! //! ## Writing to an existing file: //! DAT files contain metadata in the header that pertains to how data should be written. //! Because of this, creating a new DAT file and writing contents are separate steps. //! //! ```rust //! use libxivdat::dat_file::write_content; //! # use libxivdat::dat_file::DATFile; //! # use libxivdat::dat_type::DATType; //! # extern crate tempfile; //! # use tempfile::tempdir; //! # let temp_dir = tempdir().unwrap(); //! # let path_to_dat_file = temp_dir.path().join("TEST.DAT"); //! # DATFile::create(&path_to_dat_file, DATType::Macro).unwrap(); //! let data_vec = write_content(&path_to_dat_file, b"This is some data.").unwrap(); //! ``` //! //! ## Creating a new file: //! //! ```rust //! use libxivdat::dat_file::DATFile; //! use libxivdat::dat_type::DATType; //! # extern crate tempfile; //! # use tempfile::tempdir; //! # let temp_dir = tempdir().unwrap(); //! # let path_to_dat_file = temp_dir.path().join("TEST.DAT"); //! DATFile::create_with_content(&path_to_dat_file, DATType::Macro, b"This is some data.").unwrap(); //! ``` //! //! ## File-like access: //! //! ```rust //! use libxivdat::dat_file::read_content; //! use libxivdat::dat_file::DATFile; //! use libxivdat::dat_type::DATType; //! use std::io::{Read,Seek,SeekFrom,Write}; //! # extern crate tempfile; //! # use tempfile::tempdir; //! # let temp_dir = tempdir().unwrap(); //! # let path_to_dat_file = temp_dir.path().join("TEST.DAT"); //! # DATFile::create(&path_to_dat_file, DATType::Macro).unwrap(); //! //! let mut dat_file = DATFile::open(&path_to_dat_file).unwrap(); //! //! let mut first_256_bytes = [0u8; 256]; //! dat_file.read(&mut first_256_bytes).unwrap(); //! ``` /// Contains the [`DATError`](crate::dat_error::DATError) wrapper error. This error type is used /// for all functions that do not implement a `std::io` trait. pub mod dat_error; /// Contains a generic, low-level tool set for working with any standard binary DAT files. /// This provides the convenience functions [`read_content()`](crate::dat_file::read_content) /// and [`write_content()`](crate::dat_file::write_content) as well as the [`std::fs::File`]-like /// [`DATFile`](crate::dat_file::DATFile) interface. pub mod dat_file; /// Contains the enum of all supported file types, [`DATType`](crate::dat_type::DATType) and /// functions for accessing default header and mask values specific to each type. pub mod dat_type; /// Contains general-purpose traits and functions applicable to all high-level, file-type-specific /// modules such as [`xiv_macro`]. /// /// Enabled by feature `high-level`, which is implied by any file type feature. #[cfg(feature = "high-level")] pub mod high_level; /// Contains a generic tool set for working with any section-based binary DAT files. /// This module contains two equivalent implementations: [`Section`](crate::section::Section), /// [`read_section()`](crate::section::read_section), and [`read_section_content()`](crate::section::read_section_content) /// for working with files on disk and [`SectionData`](`crate::section::SectionData), /// [`as_section()`](crate::section::as_section`), and [`as_section_vec()`](crate::section::as_section_vec) /// for working with pre-allocated byte arrays. /// /// Because sections are variable-length data structures, no functions for writing sections in-place are /// provided. The recommended approach to writing section-based files is to read the entire file, then /// write an entirely new content block with [`write_content()`](crate::dat_file::write_content). pub mod section; /// Contains the high-level toolkit for working with macro files, `MACRO.DAT` and `MACROSYS.DAT`. /// This module contains two equivalent implementations: [`Macro`](crate::xiv_macro::Macro), /// [`read_macro()`](crate::xiv_macro::read_macro), and [`read_macro_content()`](crate::xiv_macro::read_macro_content) /// for working with files on disk and [`MacroData`](`crate::xiv_macro::MacroData), /// [`as_macro()`](crate::xiv_macro::as_macro`), and [`as_macro_vec()`](crate::xiv_macro::as_macro_vec) /// for working with pre-allocated byte arrays and [`SectionData](crate::section::SectionData). /// /// Enabled by feature `macro`. /// /// # Examples /// /// ## Reading a macro file /// ```rust /// use libxivdat::xiv_macro::read_macro_content; /// use libxivdat::xiv_macro::icon::MacroIcon; /// /// let macro_contents = read_macro_content("./resources/TEST_MACRO.DAT").unwrap(); /// /// assert_eq!(macro_contents[0].title, "0"); /// assert_eq!(macro_contents[0].lines[0], "DefaultIcon"); /// assert_eq!(macro_contents[0].get_icon().unwrap(), MacroIcon::DefaultIcon); /// /// assert_eq!(macro_contents[1].title, "1"); /// assert_eq!(macro_contents[1].lines[0], "DPS1"); /// assert_eq!(macro_contents[1].get_icon().unwrap(), MacroIcon::DPS1); /// ``` /// /// ## Writing a macro file /// Macro files use variable-length [`Sections`](crate::section::Section) to store data on disk, /// so it is not possible to easily overwrite a single macro in-place. The recommended approach to modifying /// macros is to read and write the entire file as a block. /// ```rust /// use libxivdat::dat_file::write_content; /// use libxivdat::xiv_macro::{read_macro_content, to_writeable_bytes, Macro}; /// use libxivdat::xiv_macro::icon::MacroIcon; /// # use libxivdat::dat_file::DATFile; /// # use libxivdat::dat_type::DATType; /// /// # extern crate tempfile; /// # use tempfile::tempdir; /// # let temp_dir = tempdir().unwrap(); /// # let out_path = temp_dir.path().join("TEST.DAT"); /// # DATFile::create(&out_path, DATType::Macro).unwrap(); /// /// let mut macro_vec = read_macro_content("./resources/TEST_MACRO.DAT").unwrap(); /// // Replace macro #0 with a new macro. Macro::new will enforce the game's specs for macros. /// macro_vec[0] = Macro::new( /// String::from("libxivdat was here"), /// vec![String::from("/sh I <3 libxivdat!!!")], /// MacroIcon::SymbolExclamation /// ).unwrap(); /// /// // Write it back out to a file. to_writeable_bytes will validate every macro in the vector. /// let out_bytes = to_writeable_bytes(¯o_vec).unwrap(); /// write_content(&out_path, &out_bytes); /// ``` #[cfg(feature = "macro")] pub mod xiv_macro { pub use crate::high_level_modules::r#macro::*; } /// High-level, file-type-specific submodules container. mod high_level_modules;