├── .github └── workflows │ ├── main.yaml │ └── release.yaml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Developers.md ├── ReadMe.md ├── bindings ├── CMakeLists.txt ├── main.cpp └── test.py ├── core ├── CMakeLists.txt ├── crypto │ ├── aes │ │ ├── aes.c │ │ ├── aes.h │ │ ├── aes_xts.c │ │ └── aes_xts.h │ ├── aes_cbc_strategy.cpp │ ├── aes_cbc_strategy.hpp │ ├── aes_cbc_swapped_strategy.cpp │ ├── aes_cbc_swapped_strategy.hpp │ ├── aes_xts_strategy.cpp │ ├── aes_xts_strategy.hpp │ ├── aes_xts_swapped_strategy.cpp │ ├── aes_xts_swapped_strategy.hpp │ ├── crypto_strategy.hpp │ ├── multi_layer_strategy.cpp │ └── multi_layer_strategy.hpp ├── disk │ ├── disk.cpp │ ├── disk.hpp │ ├── disk_config.cpp │ ├── disk_config.hpp │ ├── partition.cpp │ └── partition.hpp ├── formats │ ├── cell_decrypted_format.cpp │ ├── cell_decrypted_format.hpp │ ├── cell_format.cpp │ ├── cell_format.hpp │ ├── cell_phat_format.cpp │ ├── cell_phat_format.hpp │ ├── cell_slim_format.cpp │ ├── cell_slim_format.hpp │ ├── disk_format.hpp │ ├── disk_format_factory.cpp │ ├── disk_format_factory.hpp │ ├── orbis_format.cpp │ ├── orbis_format.hpp │ └── partition │ │ ├── gpt.h │ │ └── ps3.h ├── io │ ├── data │ │ ├── bounded_data_provider.cpp │ │ ├── bounded_data_provider.hpp │ │ ├── data_provider.cpp │ │ └── data_provider.hpp │ └── stream │ │ ├── disk_stream.hpp │ │ ├── disk_stream_factory.cpp │ │ ├── disk_stream_factory.hpp │ │ ├── file_disk_stream.cpp │ │ ├── file_disk_stream.hpp │ │ ├── physical_disk_stream.cpp │ │ └── physical_disk_stream.hpp ├── logging │ ├── logger.cpp │ ├── logger.hpp │ ├── stdout_log_handler.cpp │ └── stdout_log_handler.hpp ├── utilities │ └── endian.hpp └── vfs │ ├── adapters │ ├── adapter.cpp │ ├── adapter.hpp │ ├── fat │ │ └── fat_.h │ ├── fat_adapter.cpp │ ├── fat_adapter.hpp │ ├── fat_boot_sector.cpp │ ├── fat_boot_sector.hpp │ ├── fat_file_allocation_table.cpp │ ├── fat_file_allocation_table.hpp │ ├── fatx_adapter.cpp │ ├── fatx_adapter.hpp │ ├── ufs │ │ ├── ffs │ │ │ └── fs.h │ │ ├── types.h │ │ └── ufs │ │ │ ├── dinode.h │ │ │ └── dir.h │ ├── ufs2_adapter.cpp │ └── ufs2_adapter.hpp │ ├── date_time.cpp │ ├── date_time.hpp │ ├── directory.cpp │ ├── directory.hpp │ ├── file.cpp │ ├── file.hpp │ ├── file_system.cpp │ ├── file_system.hpp │ ├── node.cpp │ └── node.hpp ├── decrypt-tool ├── CMakeLists.txt └── main.cpp ├── file-explorer ├── CMakeLists.txt ├── QFileDiskStream.cpp ├── QFileDiskStream.hpp ├── main.cpp ├── mainwindow.cpp └── mainwindow.hpp ├── hex-viewer ├── CMakeLists.txt ├── QDiskBuffer.cpp ├── QDiskBuffer.hpp ├── QDiskDevice.cpp ├── QDiskDevice.hpp ├── QFileDiskStream.cpp ├── QFileDiskStream.hpp ├── hexview.cpp ├── hexview.hpp ├── main.cpp ├── mainwindow.cpp ├── mainwindow.hpp ├── searchdialog.cpp ├── searchdialog.hpp ├── searchthread.cpp ├── searchthread.hpp ├── selectdialog.cpp └── selectdialog.hpp └── mount-tool ├── CMakeLists.txt └── main.cpp /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | build-windows: 14 | runs-on: windows-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | submodules: true 20 | 21 | - name: Install Qt 22 | uses: jurplel/install-qt-action@v3 23 | 24 | - name: Create Build Environment 25 | run: cmake -S . -B build 26 | 27 | - name: Build 28 | shell: bash 29 | run: | 30 | cmake --build ./build --config $BUILD_TYPE 31 | windeployqt --release --verbose 2 bin/Release/file-explorer.exe 32 | windeployqt --release --verbose 2 bin/Release/hex-viewer.exe 33 | 34 | - name: Upload Artifact 35 | uses: actions/upload-artifact@v2 36 | with: 37 | name: Release-${{ runner.os }} 38 | path: bin/Release 39 | 40 | build-linux: 41 | runs-on: ubuntu-latest 42 | 43 | steps: 44 | - uses: actions/checkout@v2 45 | with: 46 | submodules: true 47 | 48 | - name: Install Qt 49 | uses: jurplel/install-qt-action@v3 50 | 51 | - name: Create Build Environment 52 | run: cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE 53 | 54 | - name: Build 55 | shell: bash 56 | run: | 57 | cmake --build ./build --config $BUILD_TYPE 58 | pwd 59 | ls 60 | 61 | - name: Upload Artifact 62 | uses: actions/upload-artifact@v2 63 | with: 64 | name: Release-${{ runner.os }} 65 | path: bin/Release 66 | 67 | build-macos: 68 | runs-on: macos-latest 69 | 70 | steps: 71 | - uses: actions/checkout@v2 72 | with: 73 | submodules: true 74 | 75 | - name: Install Qt 76 | uses: jurplel/install-qt-action@v3 77 | 78 | - name: Create Build Environment 79 | run: cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE 80 | 81 | - name: Build 82 | shell: bash 83 | run: | 84 | cmake --build ./build --config $BUILD_TYPE 85 | pwd 86 | ls 87 | 88 | - name: Upload Artifact 89 | uses: actions/upload-artifact@v2 90 | with: 91 | name: Release-${{ runner.os }} 92 | path: bin/Release 93 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: [workflow_dispatch] 4 | 5 | jobs: 6 | release-windows: 7 | runs-on: windows-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | submodules: true 13 | 14 | - name: Install Qt 15 | uses: jurplel/install-qt-action@v3 16 | 17 | - name: Create Build Environment 18 | run: cmake -S . -B build 19 | 20 | - name: Build 21 | shell: bash 22 | run: | 23 | cmake --build ./build --config $BUILD_TYPE 24 | windeployqt --release --verbose 2 bin/Release/file-explorer.exe 25 | windeployqt --release --verbose 2 bin/Release/hex-viewer.exe 26 | 27 | - name: Upload Artifact 28 | uses: actions/upload-artifact@v2 29 | with: 30 | name: Release-${{ runner.os }} 31 | path: bin/Release 32 | 33 | release-linux: 34 | runs-on: ubuntu-latest 35 | 36 | steps: 37 | - uses: actions/checkout@v2 38 | with: 39 | submodules: true 40 | 41 | - name: Install Qt 42 | uses: jurplel/install-qt-action@v3 43 | 44 | - name: Create Build Environment 45 | run: cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE 46 | 47 | - name: Build 48 | shell: bash 49 | run: | 50 | cmake --build ./build --config $BUILD_TYPE 51 | pwd 52 | ls 53 | 54 | - name: Upload Artifact 55 | uses: actions/upload-artifact@v2 56 | with: 57 | name: Release-${{ runner.os }} 58 | path: bin/Release 59 | 60 | release-macos: 61 | runs-on: macos-latest 62 | 63 | steps: 64 | - uses: actions/checkout@v2 65 | with: 66 | submodules: true 67 | 68 | - name: Install Qt 69 | uses: jurplel/install-qt-action@v3 70 | 71 | - name: Create Build Environment 72 | run: cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE 73 | 74 | - name: Build 75 | shell: bash 76 | run: | 77 | cmake --build ./build --config $BUILD_TYPE 78 | pwd 79 | ls 80 | 81 | - name: Upload Artifact 82 | uses: actions/upload-artifact@v2 83 | with: 84 | name: Release-${{ runner.os }} 85 | path: bin/Release -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .vscode/ 3 | build/ 4 | bin/ 5 | QHexView/ 6 | CMakeSettings.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/pybind11"] 2 | path = third_party/pybind11 3 | url = https://github.com/pybind/pybind11 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(PSHDDTools) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | add_subdirectory(core) 7 | add_subdirectory(decrypt-tool) 8 | add_subdirectory(hex-viewer) 9 | add_subdirectory(mount-tool) 10 | add_subdirectory(file-explorer) 11 | add_subdirectory(bindings) 12 | -------------------------------------------------------------------------------- /Developers.md: -------------------------------------------------------------------------------- 1 | # Developer's Guide 2 | This guide serves to help developers make their own tools using the core library. 3 | - [Developer's Guide](#developers-guide) 4 | - [Building the Project](#building-the-project) 5 | - [Dependencies](#dependencies) 6 | - [Generating Projects With CMake](#generating-projects-with-cmake) 7 | - [Building](#building) 8 | - [Using the Core Library](#using-the-core-library) 9 | - [Accessing Partitions](#accessing-partitions) 10 | - [Accessing a Partition’s File System](#accessing-a-partitions-file-system) 11 | - [VfsDirectory](#vfsdirectory) 12 | - [VfsFile](#vfsfile) 13 | - [VfsNode](#vfsnode) 14 | - [Accessing Raw Data](#accessing-raw-data) 15 | 16 | ## Building the Project 17 | ### Dependencies 18 | These are the project's dependencies and the versions that I used. 19 | - CMake 20 | - [cmake version 3.21.2](https://github.com/Kitware/CMake/releases/tag/v3.21.2) 21 | - Qt5 22 | - [Qt 5.14.2](https://download.qt.io/archive/qt/5.14/5.14.2/) 23 | 24 | ### Generating Projects With CMake 25 | Navigate to the directory of project in a terminal, then download all submodules using Git: 26 | 27 | `git submodule update --init` 28 | 29 | To generate the project files using CMake, run the following command: 30 | 31 | `cmake -S . -B build/` 32 | 33 | This will create a `build` directory with your generated project files. 34 | 35 | ### Building 36 | Optionally, you can also build using CMake with the following command: 37 | 38 | `cmake --build ./build` 39 | 40 | ## Using the Core Library 41 | First we need to set up a `DiskConfig`. We create a `FileDiskStream` which, internally, just wraps an `std::ifstream` to provide a stream that the library will read raw, unprocesssed data from a file. Then we load our keys file to memory and pass that to the `DiskConfig` as well. 42 | ```cpp 43 | FileDiskStream* stream = new FileDiskStream("c:/path/to/hdd/image.img"); 44 | /* You need to implement readFile, or get keys in whatever way you want to. */ 45 | std::vector keys = readFile("c:/path/to/hdd/keys.bin"); 46 | 47 | DiskConfig config; 48 | config.setStream(stream); 49 | config.setKeys(keys.data(), keys.size()); 50 | ``` 51 | 52 | Then, we can use our `DiskConfig` to pass to `DiskFormatFactory::detect()`. This function will automatically check if it is a recognized format and if it is, it will load the partition table, set up crypto, and returns a `Disk` object which will be our entry point for accessing data. 53 | ```cpp 54 | Disk* disk = DiskFormatFactory::getInstance()->detect(&config); 55 | ``` 56 | 57 | ## Accessing Partitions 58 | The `Disk` has a list of partitions that you can get by name: 59 | ```cpp 60 | Partition* partition = disk->getPartitionByName("dev_hdd0"); 61 | ``` 62 | 63 | Or you can request all partitions using `disk->getPartitions()`. 64 | 65 | ## Accessing a Partition’s File System 66 | You need to mount the file system before you are able to access it. To do so, request the `Vfs` associated with a partition, then call it's `mount()` method. After you mount it, be sure to check if it succeeded by calling `isMounted()`. After successfully mounting, you can access the root of the file system by using `getRoot()`, which returns a `VfsDirectory` that contains files at the root of the file system. 67 | 68 | **Note:** Mounting the file system involves indexing all files and all data that each file references. Plan for the possibility that it may take some extra time to mount. 69 | ```cpp 70 | /* Vfs provides access to the file system */ 71 | Vfs* vfs = partition->getVfs(); 72 | vfs->mount(); 73 | if (!vfs->isMounted()) 74 | throw "Failed to mount the file system!"; 75 | VfsDirectory* root = vfs->getRoot(); 76 | ``` 77 | 78 | `VfsDirectory` represents a directory, and you can access it's children by calling it's `getChildren()` method. Note that it returns a vector of `VfsNode`s. These nodes can be either a `VfsDirectory` or a `VfsFile` which represent a directory or a file respectively. You can see which type it is by calling the node's `getType()` method which returns either `VfsNodeType::DIRECTORY` or `VfsNodeType::FILE`. 79 | 80 | **Note:** In python, you can also just use `isinstance(node, VfsDirectory)` or `isinstance(node, VfsFile)` instead of `getType()`. 81 | ```cpp 82 | std::vector children = root->getChildren(); 83 | for (auto child in children) { 84 | if (child->getType() == VfsNodeType::DIRECTORY) 85 | cout << child->getName() << " is a directory" << endl; 86 | else (child->getType() == VfsNodeType::FILE) 87 | cout << child->getName() << " is a file" << endl; 88 | } 89 | ``` 90 | 91 | 92 | `VfsDirectory` and `VfsFile` have their own special accessor methods. 93 | ### VfsDirectory 94 | |Method|Description| 95 | |------|-----------| 96 | |getChildren|Get all node's in this directory| 97 | |getChildCount|Get number of node's in this directory| 98 | 99 | ### VfsFile 100 | |Method|Description| 101 | |------|-----------| 102 | |getFileSize|Get the size of this file's contents| 103 | 104 | They share these methods: 105 | ### VfsNode 106 | |Method|Description| 107 | |------|-----------| 108 | |getType|Get the node's type (Directory or File)| 109 | |getName|Get the name of this node| 110 | |getLastAccessTime|Get the time this node was last accessed| 111 | |getLastModifiedTime|Get the time this node was last modified| 112 | |getCreationTime|Get the time this node was created| 113 | |getOffsets|Get data offsets referenced to by this node| 114 | 115 | ## Accessing Raw Data 116 | You can access raw data by using `DataProvider`s. Both the `Disk` and `Partition` classes have their own `DataProvider`s. The `Disk` class has access to the entire disk, while the `Partition` class only has access to the range it exists in. The `DataProvider` will take care of decrypting sectors (even multiple layers) for you. 117 | ```cpp 118 | DataProvider* reader; 119 | if (weWantDiskReader) 120 | reader = disk->getDataProvider(); 121 | else 122 | reader = partition->getDataProvider(); 123 | reader->seek(0, /*whence=*/0); 124 | const auto kSize = 0x1000; 125 | vector buffer(kSize); 126 | auto amountRead = reader->read(buffer.data(), kSize); 127 | if (amountRead != kSize) 128 | throw "Failed to read requested amount of data!"; 129 | // Do something with buffer... 130 | ``` 131 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # PS-HDD-Tools 2 | ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/aerosoul94/PS-HDD-Tools/main.yaml?branch=master) 3 | 4 | :warning: These tools have not been thoroughly tested. 5 | 6 | This repository includes a collection of tools for inspecting encrypted PS3 or PS4 hard drives. 7 | 8 | Supports NAND or NOR PS3 hdd's and any PS4 hdd's. 9 | 10 | Requires either the EID root key (PS3) or EAP hdd key (PS4). 11 | 12 | # Contents 13 | - [PS-HDD-Tools](#ps-hdd-tools) 14 | - [Contents](#contents) 15 | - [Thanks to](#thanks-to) 16 | - [Contributors](#contributors) 17 | - [Tools](#tools) 18 | - [Decrypt Tool](#decrypt-tool) 19 | - [Usage](#usage) 20 | - [Hex Viewer](#hex-viewer) 21 | - [Mount Tool](#mount-tool) 22 | - [Usage](#usage-1) 23 | - [File Explorer](#file-explorer) 24 | - [Todo](#todo) 25 | 26 | # Thanks to 27 | * flat_z 28 | * glevand 29 | * naehrwert 30 | * 3141card 31 | 32 | # Contributors 33 | * Tdijital 34 | * jason098 35 | 36 | # Tools 37 | ### Decrypt Tool 38 | The decryption tool will take an hdd image and a key file as input and output a decrypted partition. 39 | #### Usage 40 | ``` 41 | Usage: decrypt-tool [command] [arguments...] 42 | 43 | Commands: 44 | list List available partitions. 45 | decrypt Decrypt a partition to a new file. 46 | ``` 47 | ### Hex Viewer 48 | Hex viewer that allows you to view decrypted contents of an HDD image. 49 | 50 | You can go to any offset by pressing `CTRL + G` and entering either a decimal value or a hexadecimal value beginning with "0x". 51 | 52 | ### Mount Tool 53 | This tool demonstrates usage of the virtual file system and simply prints out the contents of each file system. Eventually more functionality will be added. 54 | #### Usage 55 | ``` 56 | Usage: mount-tool [command] [arguments...] 57 | 58 | Commands: 59 | list List available partitions. 60 | display Display file system contents. 61 | ``` 62 | 63 | ### File Explorer 64 | A GUI for exploring the drive's file systems. 65 | 66 | # Todo 67 | - Add support for arcade hdd's 68 | - Data recovery tools -------------------------------------------------------------------------------- /bindings/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/pybind11 build) 2 | pybind11_add_module(disklib main.cpp) 3 | target_link_libraries(disklib PRIVATE core) 4 | include_directories(${PROJECT_SOURCE_DIR}/core) 5 | 6 | set_target_properties(disklib PROPERTIES 7 | LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin/Debug" 8 | LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin/Release" 9 | LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin/RelWithDebInfo" 10 | LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${PROJECT_SOURCE_DIR}/bin/MinSizeRel" 11 | ) 12 | -------------------------------------------------------------------------------- /bindings/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace py = pybind11; 16 | 17 | PYBIND11_MODULE(disklib, m) { 18 | py::class_(m, "DiskStream"); 19 | 20 | py::class_(m, "FileDiskStream") 21 | .def(pybind11::init()); 22 | 23 | py::class_(m, "DataProvider") 24 | .def("getLength", &io::data::DataProvider::getLength) 25 | .def("tell", &io::data::DataProvider::tell) 26 | .def("seek", [](io::data::DataProvider& provider, int64_t offset, uint32_t whence) 27 | { 28 | return provider.seek(offset, whence); 29 | }, py::arg("offset"), py::arg("whence") = 0) 30 | .def("read", [](io::data::DataProvider& provider, uint32_t length) 31 | { 32 | std::vector data(length); 33 | provider.read(data.data(), length); 34 | return py::bytes(data.data(), data.size()); 35 | }); 36 | 37 | py::class_(m, "BoundedDataProvider"); 38 | 39 | py::class_(m, "DiskConfig") 40 | .def(pybind11::init<>()) 41 | .def("setStream", &disk::DiskConfig::setStream) 42 | .def("getStream", &disk::DiskConfig::getStream, py::return_value_policy::reference) 43 | .def("setKeys", [](disk::DiskConfig& config, std::string& s) { config.setKeys(s.data(), s.length()); }) 44 | .def("getKeys", &disk::DiskConfig::getKeys); 45 | 46 | py::class_(m, "Disk") 47 | .def("getDataProvider", &disk::Disk::getDataProvider, py::return_value_policy::reference) 48 | .def("getPartitions", &disk::Disk::getPartitions, py::return_value_policy::reference) 49 | .def("getPartitionByName", &disk::Disk::getPartitionByName, py::return_value_policy::reference) 50 | // .def("addPartition", &disk::Disk::addPartition) 51 | .def("getSectorSize", &disk::Disk::getSectorSize); 52 | 53 | py::class_(m, "Partition") 54 | .def("mount", &disk::Partition::mount) 55 | .def("getName", &disk::Partition::getName) 56 | .def("setName", &disk::Partition::setName) 57 | .def("getVfs", &disk::Partition::getVfs, py::return_value_policy::reference) 58 | .def("getDataProvider", &disk::Partition::getDataProvider, py::return_value_policy::reference) 59 | .def("getLength", &disk::Partition::getLength) 60 | .def("getStart", &disk::Partition::getStart) 61 | .def("getEnd", &disk::Partition::getEnd); 62 | 63 | py::class_(m, "DiskFormatFactory") 64 | .def("detect", [](disk::DiskConfig* config) 65 | { return formats::DiskFormatFactory::getInstance()->detectFormat(config); }); 66 | 67 | py::class_(m, "Vfs") 68 | .def("mount", &vfs::Vfs::mount) 69 | .def("getRoot", &vfs::Vfs::getRoot, py::return_value_policy::reference) 70 | //.def("setAdapter", &vfs::Vfs::setAdapter) 71 | .def("isMounted", &vfs::Vfs::isMounted); 72 | 73 | py::enum_(m, "VfsNodeType") 74 | .value("Directory", vfs::VfsNodeType::DIRECTORY) 75 | .value("File", vfs::VfsNodeType::FILE) 76 | .export_values(); 77 | 78 | py::class_(m, "VfsNode") 79 | .def(pybind11::init()) 80 | .def("getType", &vfs::VfsNode::getType) 81 | .def("getName", &vfs::VfsNode::getName) 82 | // .def("setName", &vfs::VfsNode::setName) 83 | // .def("setParent", &vfs::VfsNode::setParent) 84 | .def("getLastAccessTime", &vfs::VfsNode::getLastAccessTime) 85 | .def("getlastModifiedTime", &vfs::VfsNode::getLastModifiedTime) 86 | .def("getCreationTime", &vfs::VfsNode::getCreationTime) 87 | // .def("setLastAccessTime", &vfs::VfsNode::setLastAccessTime) 88 | // .def("setlastModifiedTime", &vfs::VfsNode::setLastModifiedTime) 89 | // .def("setCreationTime", &vfs::VfsNode::setCreationTime) 90 | .def("getOffsets", &vfs::VfsNode::getOffsets) 91 | .def("__str__", [](const vfs::VfsNode& node) { 92 | return node.getName(); 93 | }); 94 | 95 | py::class_(m, "VfsDirectory") 96 | // .def("addChild", &vfs::VfsDirectory::addChild) 97 | .def("getChildren", &vfs::VfsDirectory::getChildren, py::return_value_policy::reference) 98 | .def("getChildCount", &vfs::VfsDirectory::getChildCount); 99 | 100 | py::class_(m, "VfsFile") 101 | // .def("setFileSize", &vfs::VfsFile::setFileSize) 102 | .def("getFileSize", &vfs::VfsFile::getFileSize); 103 | } 104 | -------------------------------------------------------------------------------- /bindings/test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import disklib 4 | 5 | # TODO: Write actual tests 6 | def main(imagePath, keyPath): 7 | stream = disklib.FileDiskStream(imagePath) 8 | keys = open(keyPath,'rb').read() 9 | config = disklib.DiskConfig() 10 | config.setStream(stream) 11 | config.setKeys(keys) 12 | disk = disklib.DiskFormatFactory.detect(config) 13 | partition = disk.getPartitionByName('dev_hdd0') 14 | print(partition.getStart()) 15 | print(partition.getEnd()) 16 | print(partition.getLength()) 17 | vfs = partition.getVfs() 18 | vfs.mount() 19 | root = vfs.getRoot() 20 | # Print root directory 21 | for node in root.getChildren(): 22 | print(node.getName()) 23 | 24 | if __name__ == "__main__": 25 | main(sys.argv[1], sys.argv[2]) 26 | -------------------------------------------------------------------------------- /core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #add_definitions("-Wall" "-g") 2 | 3 | file(GLOB SRC_FILES 4 | ${PROJECT_SOURCE_DIR}/core/disk/*.cpp 5 | ${PROJECT_SOURCE_DIR}/core/crypto/*.cpp 6 | ${PROJECT_SOURCE_DIR}/core/crypto/aes/*.c 7 | ${PROJECT_SOURCE_DIR}/core/formats/*.cpp 8 | ${PROJECT_SOURCE_DIR}/core/io/data/*.cpp 9 | ${PROJECT_SOURCE_DIR}/core/io/stream/*.cpp 10 | ${PROJECT_SOURCE_DIR}/core/logging/*.cpp 11 | ${PROJECT_SOURCE_DIR}/core/vfs/*.cpp 12 | ${PROJECT_SOURCE_DIR}/core/vfs/adapters/*.cpp 13 | ) 14 | 15 | include_directories(${PROJECT_SOURCE_DIR}/core) 16 | add_library(core ${SRC_FILES}) 17 | 18 | set_property(TARGET core PROPERTY POSITION_INDEPENDENT_CODE ON) 19 | -------------------------------------------------------------------------------- /core/crypto/aes/aes.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file aes.h 3 | * 4 | * \brief AES block cipher 5 | * 6 | * Copyright (C) 2006-2010, Brainspark B.V. 7 | * 8 | * This file is part of PolarSSL (http://www.polarssl.org) 9 | * Lead Maintainer: Paul Bakker 10 | * 11 | * All rights reserved. 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License along 24 | * with this program; if not, write to the Free Software Foundation, Inc., 25 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 26 | */ 27 | #ifndef POLARSSL_AES_H 28 | #define POLARSSL_AES_H 29 | 30 | #include 31 | 32 | #define AES_ENCRYPT 1 33 | #define AES_DECRYPT 0 34 | 35 | #define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0020 /**< Invalid key length. */ 36 | #define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0022 /**< Invalid data input length. */ 37 | 38 | /** 39 | * \brief AES context structure 40 | */ 41 | typedef struct 42 | { 43 | int nr; /*!< number of rounds */ 44 | unsigned long *rk; /*!< AES round keys */ 45 | unsigned long buf[68]; /*!< unaligned data */ 46 | } 47 | aes_context; 48 | 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | 53 | /** 54 | * \brief AES key schedule (encryption) 55 | * 56 | * \param ctx AES context to be initialized 57 | * \param key encryption key 58 | * \param keysize must be 128, 192 or 256 59 | * 60 | * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH 61 | */ 62 | int aes_setkey_enc( aes_context *ctx, const unsigned char *key, unsigned int keysize ); 63 | 64 | /** 65 | * \brief AES key schedule (decryption) 66 | * 67 | * \param ctx AES context to be initialized 68 | * \param key decryption key 69 | * \param keysize must be 128, 192 or 256 70 | * 71 | * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH 72 | */ 73 | int aes_setkey_dec( aes_context *ctx, const unsigned char *key, unsigned int keysize ); 74 | 75 | /** 76 | * \brief AES-ECB block encryption/decryption 77 | * 78 | * \param ctx AES context 79 | * \param mode AES_ENCRYPT or AES_DECRYPT 80 | * \param input 16-byte input block 81 | * \param output 16-byte output block 82 | * 83 | * \return 0 if successful 84 | */ 85 | int aes_crypt_ecb( aes_context *ctx, 86 | int mode, 87 | const unsigned char input[16], 88 | unsigned char output[16] ); 89 | 90 | /** 91 | * \brief AES-CBC buffer encryption/decryption 92 | * Length should be a multiple of the block 93 | * size (16 bytes) 94 | * 95 | * \param ctx AES context 96 | * \param mode AES_ENCRYPT or AES_DECRYPT 97 | * \param length length of the input data 98 | * \param iv initialization vector (updated after use) 99 | * \param input buffer holding the input data 100 | * \param output buffer holding the output data 101 | * 102 | * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH 103 | */ 104 | int aes_crypt_cbc( aes_context *ctx, 105 | int mode, 106 | size_t length, 107 | unsigned char iv[16], 108 | const unsigned char *input, 109 | unsigned char *output ); 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | 115 | #endif /* aes.h */ 116 | -------------------------------------------------------------------------------- /core/crypto/aes/aes_xts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 by naehrwert 3 | * This file is released under the GPLv2. 4 | */ 5 | 6 | #include "aes.h" 7 | #include "aes_xts.h" 8 | 9 | int aes_xts_init(aes_xts_ctxt_t *ctxt, int mode, const uint8_t *data_key, const uint8_t *tweak_key, int keybits) 10 | { 11 | ctxt->mode = mode; 12 | ctxt->keybits = keybits; 13 | aes_setkey_enc(&ctxt->twk_ctxt, tweak_key, keybits); 14 | 15 | if(mode == AES_ENCRYPT) 16 | aes_setkey_enc(&ctxt->aes_ctxt, data_key, keybits); 17 | else if(mode == AES_DECRYPT) 18 | aes_setkey_dec(&ctxt->aes_ctxt, data_key, keybits); 19 | else 20 | return -1; 21 | 22 | return 0; 23 | } 24 | 25 | int aes_xts_crypt(aes_xts_ctxt_t *ctxt, uint64_t seqno, uint32_t sector_size, const uint8_t *in, uint8_t *out) 26 | { 27 | uint32_t i = 0, j = 0; 28 | uint8_t tweak[0x10], buf[0x10]; 29 | uint8_t carry_in, carry_out; 30 | 31 | //Check alignment. 32 | if(!(sector_size % 0x10 == 0)) 33 | return -1; 34 | 35 | //Init tweak. 36 | ((uint64_t *)tweak)[0] = seqno;//_ES64(seqno); 37 | ((uint64_t *)tweak)[1] = 0; 38 | aes_crypt_ecb(&ctxt->twk_ctxt, AES_ENCRYPT, tweak, tweak); 39 | 40 | //En-/decrypt sector. 41 | for(i = 0; i < sector_size; i += 0x10) 42 | { 43 | for(j = 0; j < 0x10; j++) 44 | buf[j] = in[i+j] ^ tweak[j]; 45 | 46 | //En-/decrypt 0x10 bytes. 47 | aes_crypt_ecb(&ctxt->aes_ctxt, ctxt->mode, buf, buf); 48 | 49 | for(j = 0; j < 0x10; j++) 50 | out[i+j] = buf[j] ^ tweak[j]; 51 | 52 | //Update tweak (GF MUL). 53 | carry_in = 0; 54 | for(j = 0; j < 0x10; j++) 55 | { 56 | carry_out = (tweak[j] >> 7) & 1; 57 | tweak[j] = ((tweak[j] << 1) + carry_in) & 0xFF; 58 | carry_in = carry_out; 59 | } 60 | if(carry_out) 61 | tweak[0] ^= 0x87; 62 | } 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /core/crypto/aes/aes_xts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 by naehrwert 3 | * This file is released under the GPLv2. 4 | */ 5 | 6 | #ifndef _AES_XTS_H_ 7 | #define _AES_XTS_H_ 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #include 14 | //Using polarssl. 15 | #include "aes.h" 16 | 17 | /*! AES-XTS context. */ 18 | typedef struct _aes_xts_ctxt 19 | { 20 | aes_context twk_ctxt; 21 | aes_context aes_ctxt; 22 | int mode; 23 | int keybits; 24 | } aes_xts_ctxt_t; 25 | 26 | /*! Initialize AES-XTS context. */ 27 | int aes_xts_init(aes_xts_ctxt_t *ctxt, int mode, const uint8_t *data_key, const uint8_t *tweak_key, int keybits); 28 | /*! Crypt buffer (must be aligned to 0x10). */ 29 | int aes_xts_crypt(aes_xts_ctxt_t *ctxt, uint64_t seqno, uint32_t sector_size, const uint8_t *in, uint8_t *out); 30 | 31 | #ifdef __cplusplus 32 | } // extern C 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /core/crypto/aes_cbc_strategy.cpp: -------------------------------------------------------------------------------- 1 | #include "aes_cbc_strategy.hpp" 2 | #include "aes/aes.h" 3 | #include 4 | 5 | namespace crypto { 6 | 7 | AesCbcStrategy::AesCbcStrategy(std::vector& keyData) 8 | { 9 | aes_setkey_dec(&aes, reinterpret_cast(keyData.data()), 192); 10 | } 11 | 12 | void AesCbcStrategy::decrypt(char* data, uint32_t sector, uint32_t length) 13 | { 14 | for (uint32_t i = 0; i < length; i += kSectorSize) { 15 | if(*reinterpret_cast(data + i) != 0) { 16 | uint8_t iv[0x10] = {0}; 17 | aes_crypt_cbc(&aes, AES_DECRYPT, kSectorSize, iv, 18 | reinterpret_cast(data + i), 19 | reinterpret_cast(data + i)); 20 | } 21 | } 22 | } 23 | 24 | } /* namespace crypto */ 25 | -------------------------------------------------------------------------------- /core/crypto/aes_cbc_strategy.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AES_CBC_STRATEGY_HPP 2 | #define AES_CBC_STRATEGY_HPP 3 | 4 | #include 5 | 6 | #include "crypto_strategy.hpp" 7 | #include "aes/aes.h" 8 | 9 | namespace crypto { 10 | 11 | /** 12 | * @brief A crypto strategy that uses AES-CBC. 13 | */ 14 | class AesCbcStrategy : public CryptoStrategy 15 | { 16 | public: 17 | AesCbcStrategy(std::vector& keyData); 18 | 19 | void decrypt(char* data, uint32_t sector, uint32_t length) override; 20 | 21 | private: 22 | aes_context aes; 23 | const uint32_t kSectorSize = 0x200; 24 | }; 25 | 26 | } /* namespace crypto */ 27 | 28 | #endif /* AES_CBC_STRATEGY_HPP */ 29 | -------------------------------------------------------------------------------- /core/crypto/aes_cbc_swapped_strategy.cpp: -------------------------------------------------------------------------------- 1 | #include "aes_cbc_swapped_strategy.hpp" 2 | #include "aes/aes.h" 3 | #include 4 | 5 | namespace crypto { 6 | 7 | AesCbcSwappedStrategy::AesCbcSwappedStrategy(std::vector& keyData) 8 | { 9 | aes_setkey_dec(&aes, reinterpret_cast(keyData.data()), 192); 10 | } 11 | 12 | void AesCbcSwappedStrategy::decrypt(char* data, uint32_t sector, uint32_t length) 13 | { 14 | swapWords(data, length); 15 | for (uint32_t i = 0; i < length; i += kSectorSize) { 16 | if(*reinterpret_cast(data + i) != 0) { 17 | uint8_t iv[0x10] = {0}; 18 | aes_crypt_cbc(&aes, AES_DECRYPT, kSectorSize, iv, 19 | reinterpret_cast(data + i), 20 | reinterpret_cast(data + i)); 21 | } 22 | } 23 | } 24 | 25 | void AesCbcSwappedStrategy::swapWords(char* data, uint32_t length) 26 | { 27 | for (uint32_t i = 0; i < length / 2; i++) 28 | ((uint16_t*)data)[i] = swap16(((uint16_t*)data)[i]); 29 | } 30 | 31 | } /* namespace crypto */ 32 | -------------------------------------------------------------------------------- /core/crypto/aes_cbc_swapped_strategy.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AES_CBC_SWAPPED_STRATEGY_HPP 2 | #define AES_CBC_SWAPPED_STRATEGY_HPP 3 | 4 | #include 5 | 6 | #include "crypto_strategy.hpp" 7 | #include "aes/aes.h" 8 | 9 | namespace crypto { 10 | 11 | /** 12 | * @brief A crypto strategy that uses AES-CBC with 16-bit endian swaps. 13 | * 14 | */ 15 | class AesCbcSwappedStrategy : public CryptoStrategy 16 | { 17 | public: 18 | AesCbcSwappedStrategy(std::vector& keyData); 19 | 20 | void decrypt(char* data, uint32_t sector, uint32_t length) override; 21 | 22 | private: 23 | void swapWords(char* data, uint32_t length); 24 | 25 | aes_context aes; 26 | const uint32_t kSectorSize = 0x200; 27 | }; 28 | 29 | } /* namespace crypto */ 30 | 31 | #endif /* AES_CBC_SWAPPED_STRATEGY_HPP */ 32 | -------------------------------------------------------------------------------- /core/crypto/aes_xts_strategy.cpp: -------------------------------------------------------------------------------- 1 | #include "aes_xts_strategy.hpp" 2 | 3 | #include 4 | 5 | namespace crypto { 6 | 7 | AesXtsStrategy::AesXtsStrategy(char* key, char* tweak) 8 | { 9 | aes_xts_init(&xts_ctxt, AES_DECRYPT, 10 | reinterpret_cast(key), 11 | reinterpret_cast(tweak), 12 | 128 13 | ); 14 | } 15 | 16 | void AesXtsStrategy::decrypt(char* data, uint32_t sector, uint32_t length) 17 | { 18 | for (uint32_t i = 0; i < length; i += kSectorSize) { 19 | // Don't decrypt if data is not encrypted 20 | if (*reinterpret_cast(data + i) != 0) { 21 | aes_xts_crypt(&xts_ctxt, sector + i / kSectorSize, kSectorSize, 22 | reinterpret_cast(data) + i, 23 | reinterpret_cast(data) + i); 24 | } 25 | } 26 | } 27 | 28 | } /* namespace crypto */ 29 | -------------------------------------------------------------------------------- /core/crypto/aes_xts_strategy.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AES_XTS_STRATEGY_HPP 2 | #define AES_XTS_STRATEGY_HPP 3 | 4 | #include 5 | 6 | #include "crypto_strategy.hpp" 7 | #include "aes/aes_xts.h" 8 | 9 | namespace crypto { 10 | 11 | /** 12 | * @brief A crypto strategy that uses AES-XTS. 13 | */ 14 | class AesXtsStrategy : public CryptoStrategy 15 | { 16 | public: 17 | AesXtsStrategy(char* key, char* tweak); 18 | 19 | void decrypt(char* data, uint32_t sector, uint32_t length) override; 20 | 21 | private: 22 | aes_xts_ctxt_t xts_ctxt; 23 | const uint32_t kSectorSize = 0x200; 24 | }; 25 | 26 | } /* namespace crypto */ 27 | 28 | #endif /* AES_XTS_STRATEGY_HPP */ 29 | -------------------------------------------------------------------------------- /core/crypto/aes_xts_swapped_strategy.cpp: -------------------------------------------------------------------------------- 1 | #include "aes_xts_swapped_strategy.hpp" 2 | 3 | #include 4 | 5 | namespace crypto { 6 | 7 | AesXtsSwappedStrategy::AesXtsSwappedStrategy(char* key, char* tweak) 8 | { 9 | aes_xts_init(&xts_ctxt, AES_DECRYPT, 10 | reinterpret_cast(key), 11 | reinterpret_cast(tweak), 12 | 128 13 | ); 14 | } 15 | 16 | void AesXtsSwappedStrategy::decrypt(char* data, uint32_t sector, uint32_t length) 17 | { 18 | swapWords(data, length); 19 | for (uint32_t i = 0; i < length; i += kSectorSize) { 20 | // Don't decrypt if data is not encrypted 21 | if (*reinterpret_cast(data + i) != 0) { 22 | aes_xts_crypt(&xts_ctxt, sector + i / kSectorSize, kSectorSize, 23 | reinterpret_cast(data) + i, 24 | reinterpret_cast(data) + i); 25 | } 26 | } 27 | } 28 | 29 | void AesXtsSwappedStrategy::swapWords(char* data, uint32_t length) 30 | { 31 | for (uint32_t i = 0; i < length / 2; i++) 32 | ((uint16_t*)data)[i] = swap16(((uint16_t*)data)[i]); 33 | } 34 | 35 | } /* namespace crypto */ 36 | -------------------------------------------------------------------------------- /core/crypto/aes_xts_swapped_strategy.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AES_XTS_SWAPPED_STRATEGY_HPP 2 | #define AES_XTS_SWAPPED_STRATEGY_HPP 3 | 4 | #include 5 | 6 | #include "crypto_strategy.hpp" 7 | #include "aes/aes_xts.h" 8 | 9 | namespace crypto { 10 | 11 | /** 12 | * @brief A crypto strategy that uses AES-XTS with 16 bit endian swapping. 13 | */ 14 | class AesXtsSwappedStrategy : public CryptoStrategy 15 | { 16 | public: 17 | AesXtsSwappedStrategy(char*, char*); 18 | 19 | void decrypt(char* data, uint32_t sector, uint32_t length) override; 20 | 21 | private: 22 | void swapWords(char* data, uint32_t length); 23 | 24 | aes_xts_ctxt_t xts_ctxt; 25 | const uint32_t kSectorSize = 0x200; 26 | }; 27 | 28 | } /* namespace crypto */ 29 | 30 | #endif /* AES_XTS_SWAPPED_STRATEGY_HPP */ 31 | -------------------------------------------------------------------------------- /core/crypto/crypto_strategy.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRYPTO_STRATEGY_HPP 2 | #define CRYPTO_STRATEGY_HPP 3 | 4 | #include 5 | 6 | namespace crypto { 7 | 8 | /** 9 | * @brief A strategy for post/pre processing data using cryptography. 10 | */ 11 | class CryptoStrategy 12 | { 13 | public: 14 | //virtual ~CryptoStrategy() = 0; 15 | 16 | /** 17 | * @brief Decrypt ciphertext data. 18 | * 19 | * @param data Data to decrypt. 20 | * @param sector Sector index for crypto algorithms that require a sector. 21 | * @param length The length of the data that needs to be decrypted. 22 | */ 23 | virtual void decrypt(char* data, uint32_t sector, uint32_t length) = 0; 24 | 25 | /** 26 | * @brief Encrypt plaintext data. 27 | */ 28 | //virutal void encrypt(char* data, uint32_t sector, uint32_t length) = 0; 29 | }; 30 | 31 | } /* namespace crypto */ 32 | 33 | #endif /* CRYPTO_STRATEGY_HPP */ 34 | -------------------------------------------------------------------------------- /core/crypto/multi_layer_strategy.cpp: -------------------------------------------------------------------------------- 1 | #include "multi_layer_strategy.hpp" 2 | 3 | namespace crypto { 4 | 5 | MultiLayerStrategy::MultiLayerStrategy() {} 6 | 7 | void MultiLayerStrategy::decrypt(char* data, uint32_t sector, uint32_t length) 8 | { 9 | for (auto layer : layers) 10 | layer->decrypt(data, sector, length); 11 | } 12 | 13 | void MultiLayerStrategy::addLayer(CryptoStrategy* layer) 14 | { 15 | layers.push_back(layer); 16 | } 17 | 18 | } /* namespace crypto */ 19 | -------------------------------------------------------------------------------- /core/crypto/multi_layer_strategy.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MULTI_LAYER_STRATEGY_HPP 2 | #define MULTI_LAYER_STRATEGY_HPP 3 | 4 | #include 5 | 6 | #include "crypto_strategy.hpp" 7 | 8 | namespace crypto { 9 | 10 | /** 11 | * @brief Handles multiple layers of crypto. 12 | */ 13 | class MultiLayerStrategy : public CryptoStrategy 14 | { 15 | public: 16 | MultiLayerStrategy(); 17 | 18 | void decrypt(char* data, uint32_t sector, uint32_t length) override; 19 | 20 | /** 21 | * @brief Add a layer of a cryptographic handler. 22 | * 23 | * @param layer The crypto layer. 24 | */ 25 | void addLayer(CryptoStrategy* layer); 26 | 27 | private: 28 | std::vector layers; 29 | }; 30 | 31 | } /* namespace crypto */ 32 | 33 | #endif /* MULTI_LAYER_STRATEGY_HPP */ 34 | -------------------------------------------------------------------------------- /core/disk/disk.cpp: -------------------------------------------------------------------------------- 1 | #include "disk.hpp" 2 | #include "partition.hpp" 3 | 4 | namespace disk { 5 | 6 | Disk::Disk(io::stream::DiskStream* stream, uint32_t sectorSize) 7 | { 8 | this->dataProvider = new io::data::DataProvider(stream, sectorSize); 9 | this->sectorSize = sectorSize; 10 | } 11 | 12 | Disk::~Disk() 13 | { 14 | for (auto iter = partitions.begin(); iter != partitions.end(); ++iter) 15 | delete *iter; 16 | 17 | partitions.clear(); 18 | } 19 | 20 | io::data::DataProvider* Disk::getDataProvider() 21 | { 22 | return this->dataProvider; 23 | } 24 | 25 | const std::vector& Disk::getPartitions() const 26 | { 27 | return this->partitions; 28 | } 29 | 30 | Partition* Disk::getPartitionByName(std::string& name) const 31 | { 32 | for (auto partition : partitions) { 33 | if (partition->getName() == name) 34 | return partition; 35 | } 36 | return nullptr; 37 | } 38 | 39 | Partition* Disk::addPartition(uint64_t start, uint64_t length) 40 | { 41 | this->partitions.push_back(new Partition(this, start, length)); 42 | return this->partitions.back(); 43 | } 44 | 45 | uint32_t Disk::getSectorSize() const 46 | { 47 | return this->sectorSize; 48 | } 49 | 50 | } /* namespace disk */ 51 | -------------------------------------------------------------------------------- /core/disk/disk.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DISK_HPP 2 | #define DISK_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace disk { 10 | 11 | class Partition; 12 | 13 | /** 14 | * @brief Context class for a disk. 15 | */ 16 | class Disk 17 | { 18 | public: 19 | Disk(io::stream::DiskStream* stream, uint32_t sectorSize); 20 | ~Disk(); 21 | 22 | /** 23 | * @brief Get the Data Provider for this disk. 24 | * 25 | * @return io::data::DataProvider* 26 | */ 27 | io::data::DataProvider* getDataProvider(); 28 | 29 | /** 30 | * @brief Get a list of Partition's that exist in this disk. 31 | * 32 | * @return const std::vector& 33 | */ 34 | const std::vector& getPartitions() const; 35 | 36 | /** 37 | * @brief Get a partition by its name. 38 | * 39 | * @param name The name of the partition. 40 | * @return Partition* 41 | */ 42 | Partition* getPartitionByName(std::string& name) const; 43 | 44 | /** 45 | * @brief Add a Partition into this disk. 46 | * 47 | * @param start The start location of the partition 48 | * @param length The length of the partition 49 | * @return Partition* The partition if it was added or nullptr. 50 | */ 51 | Partition* addPartition(uint64_t start, uint64_t length); 52 | 53 | /** 54 | * @brief Get the size of a single sector in this disk. 55 | * 56 | * @return uint32_t Sector size 57 | */ 58 | uint32_t getSectorSize() const; 59 | 60 | private: 61 | io::data::DataProvider* dataProvider; 62 | std::vector partitions; 63 | uint32_t sectorSize; 64 | }; 65 | 66 | } /* namespace disk */ 67 | 68 | #endif /* DISK_HPP */ 69 | -------------------------------------------------------------------------------- /core/disk/disk_config.cpp: -------------------------------------------------------------------------------- 1 | #include "disk_config.hpp" 2 | 3 | #include 4 | 5 | namespace disk { 6 | 7 | void DiskConfig::setKeys(const char* data, uint32_t length) 8 | { 9 | this->keys.resize(length); 10 | memcpy(this->keys.data(), data, length); 11 | } 12 | 13 | std::vector DiskConfig::getKeys() const 14 | { 15 | return this->keys; 16 | } 17 | 18 | void DiskConfig::setStream(io::stream::DiskStream* stream) 19 | { 20 | this->stream = stream; 21 | } 22 | 23 | io::stream::DiskStream* DiskConfig::getStream() const 24 | { 25 | return this->stream; 26 | } 27 | 28 | } /* namespace disk */ 29 | -------------------------------------------------------------------------------- /core/disk/disk_config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DISK_CONFIG_HPP 2 | #define DISK_CONFIG_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace disk { 8 | 9 | /// Initial disk configuration information. This information should be 10 | /// provided by the user. 11 | class DiskConfig final 12 | { 13 | public: 14 | DiskConfig() {} 15 | 16 | /** 17 | * @brief Set the DiskStream. 18 | */ 19 | void setStream(io::stream::DiskStream*); 20 | 21 | /** 22 | * @brief Use this to provide the disk crypto keys. 23 | * 24 | * @param data The crypto key data. 25 | * @param length The length of the crypto key data. 26 | */ 27 | void setKeys(const char* data, uint32_t length); 28 | 29 | /** 30 | * @brief Get the crypto key data. 31 | * 32 | * @return std::vector 33 | */ 34 | std::vector getKeys() const; 35 | 36 | /** 37 | * @brief Get the DiskStream. 38 | * 39 | * @return io::stream::DiskStream* 40 | */ 41 | io::stream::DiskStream* getStream() const; 42 | 43 | private: 44 | std::vector keys; 45 | io::stream::DiskStream* stream; 46 | }; 47 | 48 | } /* namespace disk */ 49 | 50 | #endif /* DISK_CONFIG_HPP */ 51 | -------------------------------------------------------------------------------- /core/disk/partition.cpp: -------------------------------------------------------------------------------- 1 | #include "partition.hpp" 2 | #include "disk.hpp" 3 | 4 | #include 5 | 6 | namespace disk { 7 | 8 | Partition::Partition(Disk* disk, uint64_t start, uint64_t length) 9 | { 10 | this->dataProvider = new io::data::BoundedDataProvider( 11 | disk->getDataProvider(), start, length, disk->getSectorSize()); 12 | this->start = start; 13 | this->end = start + length; 14 | this->length = length; 15 | this->vfs = new vfs::Vfs; 16 | } 17 | 18 | Partition::~Partition() 19 | { 20 | delete this->dataProvider; 21 | } 22 | 23 | void Partition::mount() 24 | { 25 | this->vfs->mount(); 26 | } 27 | 28 | const std::string Partition::getName() 29 | { 30 | return this->name; 31 | } 32 | 33 | void Partition::setName(std::string name) 34 | { 35 | this->name = name; 36 | } 37 | 38 | vfs::Vfs* Partition::getVfs() 39 | { 40 | return vfs; 41 | } 42 | 43 | io::data::DataProvider* Partition::getDataProvider() 44 | { 45 | return this->dataProvider; 46 | } 47 | 48 | uint64_t Partition::getLength() const 49 | { 50 | return this->length; 51 | } 52 | 53 | uint64_t Partition::getStart() const 54 | { 55 | return this->start; 56 | } 57 | 58 | uint64_t Partition::getEnd() const 59 | { 60 | return this->end; 61 | } 62 | 63 | } /* namespace disk */ 64 | -------------------------------------------------------------------------------- /core/disk/partition.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PARTITION_HPP 2 | #define PARTITION_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace disk { 8 | 9 | class Disk; 10 | 11 | /// Partition context 12 | class Partition 13 | { 14 | public: 15 | Partition(Disk* disk, uint64_t start, uint64_t length); 16 | ~Partition(); 17 | 18 | /** 19 | * @brief Mount the file system that exists in this partition. 20 | */ 21 | void mount(); 22 | 23 | /** 24 | * @brief Get this partition's name. 25 | * 26 | * @return std::string The partition's name. 27 | */ 28 | const std::string getName(); 29 | 30 | /** 31 | * @brief Set this partition's name. 32 | * 33 | * @param name The partition's name. 34 | */ 35 | void setName(std::string name); 36 | 37 | /** 38 | * @brief Get the virtual file system for this partition. 39 | * 40 | * @return vfs::Vfs* This partition's virtual file system. 41 | */ 42 | vfs::Vfs* getVfs(); 43 | 44 | /** 45 | * @brief Get the Data Provider for this partition. 46 | * 47 | * @return io::data::DataProvider* 48 | */ 49 | io::data::DataProvider* getDataProvider(); 50 | 51 | /** 52 | * @brief Get the length of this partition. 53 | * 54 | * @return uint64_t 55 | */ 56 | uint64_t getLength() const; 57 | 58 | /** 59 | * @brief Get the start location of this partition in the disk. 60 | * 61 | * @return uint64_t 62 | */ 63 | uint64_t getStart() const; 64 | 65 | /** 66 | * @brief Get the end location of this partition in the disk. 67 | * 68 | * @return uint64_t 69 | */ 70 | uint64_t getEnd() const; 71 | 72 | private: 73 | std::string name; 74 | uint64_t start, end, length; 75 | io::data::DataProvider* dataProvider; // Should be a BoundedDataProvider 76 | // NOTE: I suppose it's possible to have more than one file system 77 | vfs::Vfs* vfs; 78 | }; 79 | 80 | } /* namespace disk */ 81 | 82 | #endif /* PARTITION_HPP */ 83 | -------------------------------------------------------------------------------- /core/formats/cell_decrypted_format.cpp: -------------------------------------------------------------------------------- 1 | #include "cell_decrypted_format.hpp" 2 | 3 | #include "partition/ps3.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | namespace formats { 14 | 15 | bool CellDecryptedDiskFormat::match(disk::Disk* disk, disk::DiskConfig* config) 16 | { 17 | auto dataProvider = disk->getDataProvider(); 18 | 19 | if (checkDiskLabel(dataProvider)) 20 | return true; 21 | 22 | return false; 23 | } 24 | 25 | bool CellDecryptedDiskFormat::checkDiskLabel(io::data::DataProvider* dataProvider) 26 | { 27 | std::vector buf(kSectorSize); 28 | 29 | dataProvider->seek(0); 30 | dataProvider->read(buf.data(), kSectorSize); 31 | 32 | disklabel* hdr = reinterpret_cast(buf.data()); 33 | 34 | if (swap64(hdr->d_magic1) == MAGIC1 && swap64(hdr->d_magic2) == MAGIC2) 35 | return true; 36 | 37 | return false; 38 | } 39 | 40 | void CellDecryptedDiskFormat::build(disk::Disk* disk, disk::DiskConfig* config) 41 | { 42 | auto dataProvider = disk->getDataProvider(); 43 | 44 | std::vector buf(0x2000); 45 | dataProvider->seek(0); 46 | dataProvider->read(buf.data(), 0x2000); 47 | 48 | auto table = reinterpret_cast(buf.data() + sizeof(disklabel)); 49 | 50 | d_partition* vflash = nullptr, 51 | * hdd0 = nullptr, 52 | * hdd1 = nullptr; 53 | 54 | auto vflashHeader = reinterpret_cast(buf.data() + 0x1000); 55 | 56 | if (swap64(vflashHeader->d_magic1) == MAGIC1 && swap64(vflashHeader->d_magic2) == MAGIC2) { 57 | // NOR 58 | vflash = &table[0]; 59 | hdd0 = &table[1]; 60 | hdd1 = &table[2]; 61 | } else { 62 | // NAND 63 | hdd0 = &table[0]; 64 | hdd1 = &table[1]; 65 | } 66 | 67 | if (vflash) { 68 | auto base = swap64(vflash->p_start); 69 | 70 | d_partition* vflashTable = reinterpret_cast(buf.data() + 0x1000 + sizeof(disklabel)); 71 | 72 | addFatPartition(disk, "dev_flash1", base + swap64(vflashTable[1].p_start), swap64(vflashTable[1].p_size)); 73 | addFatPartition(disk, "dev_flash2", base + swap64(vflashTable[2].p_start), swap64(vflashTable[2].p_size)); 74 | addFatPartition(disk, "dev_flash3", base + swap64(vflashTable[3].p_start), swap64(vflashTable[3].p_size)); 75 | } 76 | 77 | addUfsPartition(disk, "dev_hdd0", swap64(hdd0->p_start), swap64(hdd0->p_size)); 78 | addFatPartition(disk, "dev_hdd1", swap64(hdd1->p_start), swap64(hdd1->p_size)); 79 | } 80 | 81 | } /* namespace formats */ 82 | -------------------------------------------------------------------------------- /core/formats/cell_decrypted_format.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CELL_DECRYPTED_FORMAT_HPP 2 | #define CELL_DECRYPTED_FORMAT_HPP 3 | 4 | #include "cell_format.hpp" 5 | 6 | namespace formats { 7 | 8 | /** 9 | * @brief Detects and loads a decrypted PS3 disk format. 10 | */ 11 | class CellDecryptedDiskFormat : public CellDiskFormat 12 | { 13 | public: 14 | CellDecryptedDiskFormat() {} 15 | 16 | bool match(disk::Disk* disk, disk::DiskConfig* config) override; 17 | void build(disk::Disk* disk, disk::DiskConfig* config) override; 18 | 19 | private: 20 | bool checkDiskLabel(io::data::DataProvider* dataProvider); 21 | }; 22 | 23 | } /* namespace formats */ 24 | 25 | #endif /* CELL_DECRYPTED_FORMAT_HPP */ 26 | -------------------------------------------------------------------------------- /core/formats/cell_format.cpp: -------------------------------------------------------------------------------- 1 | #include "cell_format.hpp" 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | namespace formats { 19 | 20 | static uint8_t encdec_seed_00[] = 21 | { 22 | 0xE2, 0xD0, 0x5D, 0x40, 0x71, 0x94, 0x5B, 0x01, 0xC3, 0x6D, 0x51, 0x51, 0xE8, 0x8C, 0xB8, 0x33, 23 | 0x4A, 0xAA, 0x29, 0x80, 0x81, 0xD8, 0xC4, 0x4F, 0x18, 0x5D, 0xC6, 0x60, 0xED, 0x57, 0x56, 0x86 24 | }; 25 | 26 | static uint8_t encdec_seed_20[] = 27 | { 28 | 0x02, 0x08, 0x32, 0x92, 0xC3, 0x05, 0xD5, 0x38, 0xBC, 0x50, 0xE6, 0x99, 0x71, 0x0C, 0x0A, 0x3E, 29 | 0x55, 0xF5, 0x1C, 0xBA, 0xA5, 0x35, 0xA3, 0x80, 0x30, 0xB6, 0x7F, 0x79, 0xC9, 0x05, 0xBD, 0xA3 30 | }; 31 | 32 | static uint8_t sb_indiv_seed_00[] = 33 | { 34 | 0xD9, 0x2D, 0x65, 0xDB, 0x05, 0x7D, 0x49, 0xE1, 0xA6, 0x6F, 0x22, 0x74, 0xB8, 0xBA, 0xC5, 0x08, 35 | 0x83, 0x84, 0x4E, 0xD7, 0x56, 0xCA, 0x79, 0x51, 0x63, 0x62, 0xEA, 0x8A, 0xDA, 0xC6, 0x03, 0x26 36 | }; 37 | 38 | static uint8_t sb_indiv_seed_20[] = 39 | { 40 | 0xC3, 0xB3, 0xB5, 0xAA, 0xCC, 0x74, 0xCD, 0x6A, 0x48, 0xEF, 0xAB, 0xF4, 0x4D, 0xCD, 0xF1, 0x6E, 41 | 0x37, 0x9F, 0x55, 0xF5, 0x77, 0x7D, 0x09, 0xFB, 0xEE, 0xDE, 0x07, 0x05, 0x8E, 0x94, 0xBE, 0x08 42 | }; 43 | 44 | void CellDiskFormat::generateKeys(std::vector& eidRootKey) 45 | { 46 | auto generateKey = [](char* eid_key, char* eid_iv, uint8_t* seed, char* out) { 47 | aes_context aes_ctxt; 48 | uint8_t iv[0x10]; 49 | 50 | aes_setkey_enc(&aes_ctxt, (uint8_t*)eid_key, 0x100); 51 | memcpy(iv, eid_iv, 0x10); 52 | aes_crypt_cbc(&aes_ctxt, AES_ENCRYPT, 0x20, iv, seed, (uint8_t*)out); 53 | }; 54 | 55 | ataKeys.resize(0x40); 56 | 57 | generateKey(eidRootKey.data(), eidRootKey.data() + 0x20, sb_indiv_seed_00, ataKeys.data()); 58 | generateKey(eidRootKey.data(), eidRootKey.data() + 0x20, sb_indiv_seed_20, ataKeys.data() + 0x20); 59 | 60 | encDecKeys.resize(0x40); 61 | 62 | generateKey(eidRootKey.data(), eidRootKey.data() + 0x20, encdec_seed_00, encDecKeys.data()); 63 | generateKey(eidRootKey.data(), eidRootKey.data() + 0x20, encdec_seed_20, encDecKeys.data() + 0x20); 64 | } 65 | 66 | void CellDiskFormat::addVflashPartitions(disk::Disk* disk, io::data::DataProvider* dataProvider, d_partition* vflash) 67 | { 68 | // vflash has multiple partitions of its own.. 69 | // [0] = ? 70 | // [1] = dev_flash FAT16 71 | // [2] = dev_flash2 FAT16 72 | // [3] = dev_flash3 FAT12 73 | // [4] = dev_flash4 74 | 75 | // Save the current crypto strategy. 76 | auto diskStrategy = dataProvider->getCryptoStrategy(); 77 | 78 | // Vflash uses a multiple layers of crypto.. 79 | auto strategy = new crypto::MultiLayerStrategy(); 80 | strategy->addLayer(diskStrategy); 81 | strategy->addLayer(new crypto::AesXtsStrategy(encDecKeys.data(), encDecKeys.data() + 0x20)); 82 | 83 | // TODO: Should probably pass this to disk->addPartition instead of inheriting from the disk. 84 | dataProvider->setCryptoStrategy(strategy); 85 | 86 | // Read in the VFLASH partition table 87 | std::vector tmp(0x1000); 88 | dataProvider->seek(swap64(vflash->p_start) * kSectorSize); 89 | dataProvider->read(tmp.data(), 0x1000); 90 | 91 | auto base = swap64(vflash->p_start); 92 | 93 | d_partition* vflashTable = reinterpret_cast(tmp.data() 94 | + sizeof(disklabel)); 95 | 96 | addFatPartition(disk, "dev_flash1", base + swap64(vflashTable[1].p_start), swap64(vflashTable[1].p_size)); 97 | addFatPartition(disk, "dev_flash2", base + swap64(vflashTable[2].p_start), swap64(vflashTable[2].p_size)); 98 | addFatPartition(disk, "dev_flash3", base + swap64(vflashTable[3].p_start), swap64(vflashTable[3].p_size)); 99 | 100 | // Set the crypto strategyback to the original. 101 | dataProvider->setCryptoStrategy(diskStrategy); 102 | } 103 | 104 | void CellDiskFormat::addFatPartition(disk::Disk* disk, std::string name, uint64_t start, uint64_t length) 105 | { 106 | auto partition = disk->addPartition(start * kSectorSize, length * kSectorSize); 107 | 108 | partition->setName(name); 109 | partition->getVfs()->setAdapter(new vfs::adapters::FatAdapter(partition->getDataProvider())); 110 | } 111 | 112 | void CellDiskFormat::addUfsPartition(disk::Disk* disk, std::string name, uint64_t start, uint64_t length) 113 | { 114 | auto partition = disk->addPartition(start * kSectorSize, length * kSectorSize); 115 | 116 | partition->setName(name); 117 | partition->getVfs()->setAdapter(new vfs::adapters::Ufs2Adapter(partition->getDataProvider(), true)); 118 | } 119 | 120 | } /* namespace formats */ 121 | -------------------------------------------------------------------------------- /core/formats/cell_format.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CELL_FORMAT_HPP 2 | #define CELL_FORMAT_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "disk_format.hpp" 8 | #include "partition/ps3.h" 9 | 10 | namespace formats { 11 | 12 | /** 13 | * @brief Detects and loads the PS3 disk format. 14 | */ 15 | class CellDiskFormat : public IDiskFormat 16 | { 17 | protected: 18 | /** 19 | * @brief Generate crypto keys from an eid root key. 20 | * 21 | * @param eidRootKey 22 | */ 23 | void generateKeys(std::vector& eidRootKey); 24 | 25 | void addVflashPartitions(disk::Disk* disk, io::data::DataProvider* dataProvider, d_partition* vflash); 26 | void addFatPartition(disk::Disk* disk, std::string name, uint64_t start, uint64_t length); 27 | void addUfsPartition(disk::Disk* disk, std::string name, uint64_t start, uint64_t length); 28 | 29 | std::vector ataKeys; 30 | std::vector encDecKeys; 31 | // TODO: We should be getting this from DiskConfig 32 | const uint32_t kSectorSize = 0x200; 33 | }; 34 | 35 | } /* namespace formats */ 36 | 37 | #endif /* CELL_FORMAT_HPP */ 38 | -------------------------------------------------------------------------------- /core/formats/cell_phat_format.cpp: -------------------------------------------------------------------------------- 1 | #include "cell_phat_format.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace formats { 8 | 9 | bool CellPhatDiskFormat::match(disk::Disk* disk, disk::DiskConfig* config) 10 | { 11 | auto keyData = config->getKeys(); 12 | auto dataProvider = disk->getDataProvider(); 13 | 14 | if (keyData.size() < 0x30) 15 | return false; 16 | 17 | generateKeys(keyData); 18 | 19 | if (checkDiskLabel(dataProvider)) 20 | return true; 21 | 22 | return false; 23 | } 24 | 25 | bool CellPhatDiskFormat::checkDiskLabel(io::data::DataProvider* dataProvider) 26 | { 27 | std::vector buf(kSectorSize); 28 | 29 | dataProvider->setCryptoStrategy(new crypto::AesCbcSwappedStrategy(ataKeys)); 30 | 31 | dataProvider->seek(0); 32 | dataProvider->read(buf.data(), kSectorSize); 33 | 34 | disklabel* hdr = reinterpret_cast(buf.data()); 35 | 36 | if (swap64(hdr->d_magic1) == MAGIC1 && swap64(hdr->d_magic2) == MAGIC2) 37 | return true; 38 | 39 | return false; 40 | } 41 | 42 | void CellPhatDiskFormat::build(disk::Disk* disk, disk::DiskConfig* config) 43 | { 44 | auto dataProvider = disk->getDataProvider(); 45 | 46 | std::vector buf(0x2000); 47 | dataProvider->seek(0); 48 | dataProvider->read(buf.data(), 0x2000); 49 | 50 | auto table = reinterpret_cast(buf.data() + sizeof(disklabel)); 51 | 52 | d_partition *vflash = nullptr, 53 | *hdd0 = nullptr, 54 | *hdd1 = nullptr; 55 | 56 | // Check if it has the vflash partition table. 57 | auto strategy = crypto::AesXtsStrategy(encDecKeys.data(), encDecKeys.data() + 0x20); 58 | strategy.decrypt(buf.data() + 0x1000, 8, 0x1000); 59 | 60 | auto vflashHeader = reinterpret_cast(buf.data() + 0x1000); 61 | 62 | if (swap64(vflashHeader->d_magic1) == MAGIC1 && swap64(vflashHeader->d_magic2) == MAGIC2) { 63 | // NOR 64 | vflash = &table[0]; 65 | hdd0 = &table[1]; 66 | hdd1 = &table[2]; 67 | } else { 68 | // NAND 69 | hdd0 = &table[0]; 70 | hdd1 = &table[1]; 71 | } 72 | 73 | if (vflash) 74 | addVflashPartitions(disk, dataProvider, vflash); 75 | 76 | addUfsPartition(disk, "dev_hdd0", swap64(hdd0->p_start), swap64(hdd0->p_size)); 77 | addFatPartition(disk, "dev_hdd1", swap64(hdd1->p_start), swap64(hdd1->p_size)); 78 | } 79 | 80 | } /* namespace formats */ 81 | -------------------------------------------------------------------------------- /core/formats/cell_phat_format.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CELL_PHAT_FORMAT_HPP 2 | #define CELL_PHAT_FORMAT_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "cell_format.hpp" 8 | 9 | namespace formats { 10 | 11 | /** 12 | * @brief Detects and loads a phat PS3 disk format. 13 | */ 14 | class CellPhatDiskFormat : public CellDiskFormat 15 | { 16 | public: 17 | CellPhatDiskFormat() {} 18 | 19 | bool match(disk::Disk* disk, disk::DiskConfig* config) override; 20 | void build(disk::Disk* disk, disk::DiskConfig* config) override; 21 | 22 | private: 23 | bool checkDiskLabel(io::data::DataProvider* dataProvider); 24 | }; 25 | 26 | } /* namespace formats */ 27 | 28 | #endif /* CELL_PHAT_FORMAT_HPP */ -------------------------------------------------------------------------------- /core/formats/cell_slim_format.cpp: -------------------------------------------------------------------------------- 1 | #include "cell_slim_format.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace formats { 7 | 8 | bool CellSlimDiskFormat::match(disk::Disk* disk, disk::DiskConfig* config) 9 | { 10 | auto keyData = config->getKeys(); 11 | auto dataProvider = disk->getDataProvider(); 12 | 13 | if (keyData.size() < 0x30) 14 | return false; 15 | 16 | generateKeys(keyData); 17 | 18 | if (checkDiskLabel(dataProvider)) 19 | return true; 20 | 21 | return false; 22 | } 23 | 24 | bool CellSlimDiskFormat::checkDiskLabel(io::data::DataProvider* dataProvider) 25 | { 26 | std::vector buf(kSectorSize); 27 | 28 | dataProvider->setCryptoStrategy(new crypto::AesXtsSwappedStrategy(ataKeys.data(), ataKeys.data() + 0x20)); 29 | 30 | dataProvider->seek(0); 31 | dataProvider->read(buf.data(), kSectorSize); 32 | 33 | disklabel* hdr = reinterpret_cast(buf.data()); 34 | 35 | if (swap64(hdr->d_magic1) == MAGIC1 && swap64(hdr->d_magic2) == MAGIC2) 36 | return true; 37 | 38 | return false; 39 | } 40 | 41 | void CellSlimDiskFormat::build(disk::Disk* disk, disk::DiskConfig* config) 42 | { 43 | auto dataProvider = disk->getDataProvider(); 44 | 45 | std::vector buf(0x2000); 46 | dataProvider->seek(0); 47 | dataProvider->read(buf.data(), 0x2000); 48 | 49 | auto table = reinterpret_cast(buf.data() + sizeof(disklabel)); 50 | d_partition* vflash = &table[0], 51 | * hdd0 = &table[1], 52 | * hdd1 = &table[2]; 53 | 54 | addVflashPartitions(disk, dataProvider, vflash); 55 | 56 | addUfsPartition(disk, "dev_hdd0", swap64(hdd0->p_start), swap64(hdd0->p_size)); 57 | addFatPartition(disk, "dev_hdd1", swap64(hdd1->p_start), swap64(hdd1->p_size)); 58 | } 59 | 60 | } /* namespace formats */ 61 | -------------------------------------------------------------------------------- /core/formats/cell_slim_format.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CELL_SLIM_FORMAT_HPP 2 | #define CELL_SLIM_FORMAT_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "cell_format.hpp" 8 | 9 | namespace formats { 10 | 11 | /** 12 | * @brief Detects and loads a slim PS3 disk format. 13 | */ 14 | class CellSlimDiskFormat : public CellDiskFormat 15 | { 16 | public: 17 | CellSlimDiskFormat() {} 18 | 19 | bool match(disk::Disk* disk, disk::DiskConfig* config) override; 20 | void build(disk::Disk* disk, disk::DiskConfig* config) override; 21 | 22 | private: 23 | bool checkDiskLabel(io::data::DataProvider* dataProvider); 24 | }; 25 | 26 | } /* namespace formats */ 27 | 28 | #endif /* CELL_SLIM_FORMAT_HPP */ -------------------------------------------------------------------------------- /core/formats/disk_format.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DISK_FORMAT_HPP 2 | #define DISK_FORMAT_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace formats { 13 | 14 | /// Abstract interface for a disk format builder 15 | class IDiskFormat 16 | { 17 | public: 18 | /** 19 | * @brief This method should return whether or not the disk matches a disk 20 | * format. 21 | * 22 | * @param disk The input device. 23 | * @param config Disk information provided by the user. 24 | * @return true If the disk format has been detected. 25 | * @return false If the disk format was not detected. 26 | */ 27 | virtual bool match(disk::Disk* disk, disk::DiskConfig* config) = 0; 28 | 29 | /** 30 | * @brief This method builds the Disk object. 31 | * 32 | * @param disk The input disk context. 33 | * @param config Disk information provided by the user. 34 | */ 35 | virtual void build(disk::Disk* disk, disk::DiskConfig* config) = 0; 36 | }; 37 | 38 | } /* namespace formats */ 39 | 40 | #endif /* DISK_FORMAT_HPP */ 41 | -------------------------------------------------------------------------------- /core/formats/disk_format_factory.cpp: -------------------------------------------------------------------------------- 1 | #include "disk_format_factory.hpp" 2 | 3 | #include "orbis_format.hpp" 4 | #include "cell_phat_format.hpp" 5 | #include "cell_slim_format.hpp" 6 | #include "cell_decrypted_format.hpp" 7 | 8 | namespace formats { 9 | 10 | DiskFormatFactory::DiskFormatFactory() 11 | { 12 | registerFormat(new OrbisDiskFormat()); 13 | registerFormat(new CellPhatDiskFormat()); 14 | registerFormat(new CellSlimDiskFormat()); 15 | registerFormat(new CellDecryptedDiskFormat()); 16 | } 17 | 18 | DiskFormatFactory* DiskFormatFactory::getInstance() 19 | { 20 | static DiskFormatFactory g_diskDecryptFactory; 21 | return &g_diskDecryptFactory; 22 | } 23 | 24 | disk::Disk* DiskFormatFactory::detectFormat(disk::DiskConfig* config) 25 | { 26 | // Eventually we will return a vector if multiple disk format are detected. 27 | for (const auto format : this->formats) { 28 | // TODO: Sector size should be detected by some disk 29 | // geometry detection service 30 | // For now we default to a sector size of 0x200 31 | disk::Disk* disk = new disk::Disk(config->getStream(), 0x200); 32 | if (format->match(disk, config)) { 33 | format->build(disk, config); 34 | return disk; 35 | } else { 36 | delete disk; 37 | } 38 | } 39 | 40 | return nullptr; 41 | } 42 | 43 | void DiskFormatFactory::registerFormat(IDiskFormat* format) 44 | { 45 | this->formats.push_back(format); 46 | } 47 | 48 | } /* namespace formats */ 49 | -------------------------------------------------------------------------------- /core/formats/disk_format_factory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DISK_FORMAT_FACTORY_HPP 2 | #define DISK_FORMAT_FACTORY_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "disk_format.hpp" 8 | 9 | namespace formats { 10 | 11 | /// Disk format detection service 12 | class DiskFormatFactory 13 | { 14 | std::vector formats; 15 | public: 16 | DiskFormatFactory(); 17 | 18 | static DiskFormatFactory* getInstance(); 19 | 20 | /** 21 | * @brief Try and detect the disk format. 22 | * 23 | * @param config 24 | * @return disk::Disk* 25 | */ 26 | disk::Disk* detectFormat(disk::DiskConfig* config); 27 | 28 | /** 29 | * @brief Register a disk format. 30 | * 31 | * @param format 32 | */ 33 | void registerFormat(IDiskFormat* format); 34 | }; 35 | 36 | } /* namespace formats */ 37 | 38 | #endif /* DISK_FORMAT_FACTORY_HPP */ 39 | -------------------------------------------------------------------------------- /core/formats/orbis_format.cpp: -------------------------------------------------------------------------------- 1 | #include "orbis_format.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace formats { 15 | 16 | bool OrbisDiskFormat::match(disk::Disk* disk, disk::DiskConfig* config) 17 | { 18 | auto keyData = config->getKeys(); 19 | auto dataProvider = disk->getDataProvider(); 20 | 21 | if (keyData.size() < 0x20) 22 | return false; 23 | 24 | dataProvider->seek(0x200); 25 | dataProvider->read(reinterpret_cast(&hdr), sizeof(hdr)); 26 | 27 | if (!verifyGptHeader()) 28 | return false; 29 | 30 | return !memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)); 31 | } 32 | 33 | bool OrbisDiskFormat::verifyGptHeader() 34 | { 35 | if (hdr.entsz > sizeof(gpt_ent)) 36 | return false; 37 | 38 | if (hdr.entries > 0x80) 39 | return false; 40 | 41 | if (hdr.lba_table != 0x2) 42 | return false; 43 | 44 | return true; 45 | } 46 | 47 | void OrbisDiskFormat::build(disk::Disk* disk, disk::DiskConfig* config) 48 | { 49 | auto dataProvider = disk->getDataProvider(); 50 | this->loadGptEntTable(dataProvider); 51 | 52 | auto keys = config->getKeys(); 53 | 54 | auto userStrategy = new 55 | crypto::AesXtsStrategy(keys.data(), keys.data() + 0x10); 56 | 57 | addUfsPartition(disk, userStrategy, "user", getGptEntByType(&user_ent)); 58 | addUfsPartition(disk, userStrategy, "eap_user", getGptEntByType(&eap_user_ent)); 59 | addFatPartition(disk, userStrategy, "eap_vsh", getGptEntByType(&eap_vsh_ent)); 60 | addFatPartition(disk, userStrategy, "update", getGptEntByType(&update_ent)); 61 | } 62 | 63 | void OrbisDiskFormat::loadGptEntTable(io::data::DataProvider* dataProvider) 64 | { 65 | const uint64_t offset = hdr.lba_table * kSectorSize; 66 | dataProvider->seek(offset); 67 | partitions.resize(hdr.entries); 68 | dataProvider->read(reinterpret_cast(partitions.data()), 69 | partitions.size() * hdr.entsz); 70 | } 71 | 72 | const gpt_ent* OrbisDiskFormat::getGptEntByType(const uuid* type) 73 | { 74 | for (const auto& ent : partitions) 75 | if (!memcmp(&ent.type, type, sizeof(uuid))) 76 | return &ent; 77 | return nullptr; 78 | } 79 | 80 | void OrbisDiskFormat::addFatPartition(disk::Disk* disk, crypto::CryptoStrategy* strategy, 81 | std::string name, const gpt_ent* ent) 82 | { 83 | if (ent) { 84 | auto partition = disk->addPartition(ent->lba_start * kSectorSize, (ent->lba_end - ent->lba_start) * kSectorSize); 85 | 86 | auto partitionDataProvider = partition->getDataProvider(); 87 | partitionDataProvider->setSectorBias(ent->lba_start); 88 | partitionDataProvider->setCryptoStrategy(strategy); 89 | 90 | partition->setName(name); 91 | partition->getVfs()->setAdapter(new vfs::adapters::FatAdapter(partition->getDataProvider())); 92 | } 93 | } 94 | 95 | void OrbisDiskFormat::addUfsPartition(disk::Disk* disk, crypto::CryptoStrategy* strategy, 96 | std::string name, const gpt_ent* ent) 97 | { 98 | if (ent) { 99 | auto partition = disk->addPartition(ent->lba_start * kSectorSize, (ent->lba_end - ent->lba_start) * kSectorSize); 100 | 101 | auto partitionDataProvider = partition->getDataProvider(); 102 | partitionDataProvider->setSectorBias(ent->lba_start); 103 | partitionDataProvider->setCryptoStrategy(strategy); 104 | 105 | partition->setName(name); 106 | partition->getVfs()->setAdapter(new vfs::adapters::Ufs2Adapter(partition->getDataProvider())); 107 | } 108 | } 109 | 110 | } /* namespace formats */ 111 | -------------------------------------------------------------------------------- /core/formats/orbis_format.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ORBIS_FORMAT_HPP 2 | #define ORBIS_FORMAT_HPP 3 | 4 | #include "disk_format.hpp" 5 | #include "partition/gpt.h" 6 | 7 | namespace formats { 8 | 9 | /** 10 | * @brief Detects and loads the PS4 disk format. 11 | */ 12 | class OrbisDiskFormat : public IDiskFormat 13 | { 14 | public: 15 | OrbisDiskFormat() {} 16 | 17 | bool match(disk::Disk*, disk::DiskConfig*) override; 18 | void build(disk::Disk*, disk::DiskConfig*) override; 19 | 20 | private: 21 | // TODO: Move these to a GPT Disk Format class. 22 | /** 23 | * @brief Verify that this is a GPT header. 24 | * 25 | * @return true 26 | * @return false 27 | */ 28 | bool verifyGptHeader(); 29 | 30 | /** 31 | * @brief Load the GPT Entry Table from the disk. 32 | * 33 | * @param dataProvider 34 | */ 35 | void loadGptEntTable(io::data::DataProvider* dataProvider); 36 | 37 | /** 38 | * @brief Get the a GPT Entry by its type from the GPT Ent Table. 39 | * 40 | * @param type 41 | * @return const gpt_ent* 42 | */ 43 | const gpt_ent* getGptEntByType(const uuid* type); 44 | 45 | void addFatPartition(disk::Disk* disk, crypto::CryptoStrategy* strategy, std::string name, const gpt_ent* ent); 46 | void addUfsPartition(disk::Disk* disk, crypto::CryptoStrategy* strategy, std::string name, const gpt_ent* ent); 47 | 48 | gpt_hdr hdr; 49 | std::vector partitions; 50 | const uuid user_ent = GPT_ENT_TYPE_ORBIS_USER; 51 | const uuid eap_user_ent = GPT_ENT_TYPE_ORBIS_EAP_USER; 52 | const uuid eap_vsh_ent = GPT_ENT_TYPE_ORBIS_EAP_VSH; 53 | const uuid update_ent = GPT_ENT_TYPE_ORBIS_UPDATE; 54 | const uint32_t kSectorSize = 0x200; 55 | }; 56 | 57 | } /* namespace formats */ 58 | 59 | #endif /* ORBIS_FORMAT_HPP */ 60 | -------------------------------------------------------------------------------- /core/formats/partition/gpt.h: -------------------------------------------------------------------------------- 1 | #ifndef GPT_H 2 | #define GPT_H 3 | 4 | #include 5 | 6 | struct uuid { 7 | uint32_t time_low; 8 | uint16_t time_mid; 9 | uint16_t time_hi_and_version; 10 | uint8_t clock_seq_hi_and_reserved; 11 | uint8_t clock_seq_low; 12 | uint8_t node[6]; 13 | }; 14 | 15 | #define GPT_HDR_SIG "EFI PART" 16 | #define GPT_HDR_REVISION 0x00010000 17 | 18 | struct gpt_hdr { 19 | uint8_t hdr_sig[8]; // 0x0 20 | uint32_t hdr_revision; // 0x8 21 | uint32_t size; // 0xC 22 | uint32_t crc_self; // 0x10 23 | uint32_t reserved; // 0x14 24 | uint64_t lba_self; // 0x18 25 | uint64_t lba_alt; // 0x20 26 | uint64_t lba_start; // 0x28 27 | uint64_t lba_end; // 0x30 28 | struct uuid uuid; // 0x40 29 | uint64_t lba_table; // 0x48 30 | uint32_t entries; // 0x50 31 | uint32_t entsz; // 0x54 32 | uint32_t crc_table; // 0x58 33 | uint32_t padding; // 0x5C 34 | }; // 0x60 35 | 36 | struct gpt_ent { 37 | struct uuid type; // 0x0 38 | struct uuid uuid; // 0x10 39 | uint64_t lba_start; // 0x20 40 | uint64_t lba_end; // 0x28 41 | uint64_t attr; // 0x30 42 | uint16_t name[36]; // 0x38 43 | }; // 0x80 44 | 45 | 46 | 47 | #define GPT_ENT_TYPE_UNUSED \ 48 | {0x00000000,0x0000,0x0000,0x00,0x00,{0x00,0x00,0x00,0x00,0x00,0x00}} 49 | #define GPT_ENT_TYPE_EFI \ 50 | {0xc12a7328,0xf81f,0x11d2,0xba,0x4b,{0x00,0xa0,0xc9,0x3e,0xc9,0x3b}} 51 | #define GPT_ENT_TYPE_MBR \ 52 | {0x024dee41,0x33e7,0x11d3,0x9d,0x69,{0x00,0x08,0xc7,0x81,0xf3,0x9f}} 53 | #define GPT_ENT_TYPE_FREEBSD \ 54 | {0x516e7cb4,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} 55 | #define GPT_ENT_TYPE_FREEBSD_BOOT \ 56 | {0x83bd6b9d,0x7f41,0x11dc,0xbe,0x0b,{0x00,0x15,0x60,0xb8,0x4f,0x0f}} 57 | #define GPT_ENT_TYPE_FREEBSD_NANDFS \ 58 | {0x74ba7dd9,0xa689,0x11e1,0xbd,0x04,{0x00,0xe0,0x81,0x28,0x6a,0xcf}} 59 | #define GPT_ENT_TYPE_FREEBSD_SWAP \ 60 | {0x516e7cb5,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} 61 | #define GPT_ENT_TYPE_FREEBSD_UFS \ 62 | {0x516e7cb6,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} 63 | #define GPT_ENT_TYPE_FREEBSD_VINUM \ 64 | {0x516e7cb8,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} 65 | #define GPT_ENT_TYPE_FREEBSD_ZFS \ 66 | {0x516e7cba,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} 67 | 68 | #define GPT_ENT_TYPE_ORBIS_PREINST \ 69 | {0x17800F17,0xB9E1,0x425D,0xB9,0x37,{0x01,0x19,0xA0,0x81,0x31,0x72}} 70 | #define GPT_ENT_TYPE_ORBIS_PREINST2 \ 71 | {0xccb52e94,0xebef,0x48c4,0xa1,0x95,{0x9e,0x2d,0xa5,0xb0,0x29,0x2c}} 72 | #define GPT_ENT_TYPE_ORBIS_EAP_KERN \ 73 | {0x145268bf,0x63ad,0x47c1,0x93,0x78,{0x9a,0xac,0xd9,0xbe,0xed,0x7c}} 74 | #define GPT_ENT_TYPE_ORBIS_EAP_VSH \ 75 | {0x6e0c5310,0x8445,0x4066,0xb5,0x71,{0x9b,0x65,0xfd,0xb7,0x59,0x35}} 76 | #define GPT_ENT_TYPE_ORBIS_UNKNOWN \ 77 | {0xeabbf00b,0xc299,0x4488,0x9d,0xe9,{0xb2,0x83,0x9b,0xce,0x75,0x46}} 78 | #define GPT_ENT_TYPE_ORBIS_SYSTEM \ 79 | {0x757A614B,0x6179,0x5361,0x6B,0x61,{0x6B,0x69,0x68,0x61,0x72,0x61}} 80 | #define GPT_ENT_TYPE_ORBIS_SYSTEM_EX \ 81 | {0xdc85025f,0xa694,0x4109,0xbe,0x44,{0xfa,0x0c,0x06,0x3e,0x8b,0x81}} 82 | #define GPT_ENT_TYPE_ORBIS_SWAP \ 83 | {0x76a9a5b4,0x44b0,0x472a,0xbd,0xe3,{0x31,0x07,0x47,0x2a,0xde,0xe2}} 84 | #define GPT_ENT_TYPE_ORBIS_APP_TMP \ 85 | {0x80dd49e3,0xa985,0x4887,0x81,0xde,{0x1d,0xac,0xa4,0x7a,0xed,0x90}} 86 | #define GPT_ENT_TYPE_ORBIS_SYSTEM_DATA \ 87 | {0xa71ff62d,0x1421,0x4dd9,0x93,0x5d,{0x25,0xda,0xbd,0x81,0xbe,0xc5}} 88 | #define GPT_ENT_TYPE_ORBIS_UPDATE \ 89 | {0xfdb5ede1,0x73c3,0x4c43,0x8c,0x5b,{0x2d,0x3d,0xcf,0xcd,0xdf,0xf8}} 90 | #define GPT_ENT_TYPE_ORBIS_USER \ 91 | {0xc638477a,0xe002,0x4b57,0xa4,0x54,{0xa2,0x7f,0xb6,0x3a,0x33,0xa8}} 92 | #define GPT_ENT_TYPE_ORBIS_EAP_USER \ 93 | {0x21e4dfb4,0x0040,0x4934,0xa0,0x37,{0xea,0x9d,0xc0,0x58,0xee,0xa6}} 94 | #define GPT_ENT_TYPE_ORBIS_APP_RESERVED \ 95 | {0x3ef7290a,0xde81,0x4887,0xa1,0x1f,{0x46,0xfb,0xa7,0x65,0xc7,0x1c}} 96 | 97 | /* 98 | * The following are unused but documented here to avoid reuse. 99 | * 100 | * GPT_ENT_TYPE_FREEBSD_UFS2 \ 101 | * {0x516e7cb7,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}} 102 | */ 103 | 104 | /* 105 | * Foreign partition types that we're likely to encounter. Note that Linux 106 | * apparently choose to share data partitions with MS. I don't what the 107 | * advantage might be. I can see how sharing swap partitions is advantageous 108 | * though. 109 | */ 110 | #define GPT_ENT_TYPE_MS_RESERVED \ 111 | {0xe3c9e316,0x0b5c,0x4db8,0x81,0x7d,{0xf9,0x2d,0xf0,0x02,0x15,0xae}} 112 | #define GPT_ENT_TYPE_MS_BASIC_DATA \ 113 | {0xebd0a0a2,0xb9e5,0x4433,0x87,0xc0,{0x68,0xb6,0xb7,0x26,0x99,0xc7}} 114 | #define GPT_ENT_TYPE_MS_LDM_METADATA \ 115 | {0x5808c8aa,0x7e8f,0x42e0,0x85,0xd2,{0xe1,0xe9,0x04,0x34,0xcf,0xb3}} 116 | #define GPT_ENT_TYPE_MS_LDM_DATA \ 117 | {0xaf9b60a0,0x1431,0x4f62,0xbc,0x68,{0x33,0x11,0x71,0x4a,0x69,0xad}} 118 | 119 | #define GPT_ENT_TYPE_LINUX_DATA GPT_ENT_TYPE_MS_BASIC_DATA 120 | #define GPT_ENT_TYPE_LINUX_RAID \ 121 | {0xa19d880f,0x05fc,0x4d3b,0xa0,0x06,{0x74,0x3f,0x0f,0x84,0x91,0x1e}} 122 | #define GPT_ENT_TYPE_LINUX_SWAP \ 123 | {0x0657fd6d,0xa4ab,0x43c4,0x84,0xe5,{0x09,0x33,0xc8,0x4b,0x4f,0x4f}} 124 | #define GPT_ENT_TYPE_LINUX_LVM \ 125 | {0xe6d6d379,0xf507,0x44c2,0xa2,0x3c,{0x23,0x8f,0x2a,0x3d,0xf9,0x28}} 126 | 127 | #define GPT_ENT_TYPE_VMFS \ 128 | {0xaa31e02a,0x400f,0x11db,0x95,0x90,{0x00,0x0c,0x29,0x11,0xd1,0xb8}} 129 | #define GPT_ENT_TYPE_VMKDIAG \ 130 | {0x9d275380,0x40ad,0x11db,0xbf,0x97,{0x00,0x0c,0x29,0x11,0xd1,0xb8}} 131 | #define GPT_ENT_TYPE_VMRESERVED \ 132 | {0x9198effc,0x31c0,0x11db,0x8f,0x78,{0x00,0x0c,0x29,0x11,0xd1,0xb8}} 133 | #define GPT_ENT_TYPE_VMVSANHDR \ 134 | {0x381cfccc,0x7288,0x11e0,0x92,0xee,{0x00,0x0c,0x29,0x11,0xd0,0xb2}} 135 | 136 | #define GPT_ENT_TYPE_APPLE_BOOT \ 137 | {0x426F6F74,0x0000,0x11aa,0xaa,0x11,{0x00,0x30,0x65,0x43,0xec,0xac}} 138 | #define GPT_ENT_TYPE_APPLE_HFS \ 139 | {0x48465300,0x0000,0x11aa,0xaa,0x11,{0x00,0x30,0x65,0x43,0xec,0xac}} 140 | #define GPT_ENT_TYPE_APPLE_UFS \ 141 | {0x55465300,0x0000,0x11aa,0xaa,0x11,{0x00,0x30,0x65,0x43,0xec,0xac}} 142 | #define GPT_ENT_TYPE_APPLE_ZFS \ 143 | {0x6a898cc3,0x1dd2,0x11b2,0x99,0xa6,{0x08,0x00,0x20,0x73,0x66,0x31}} 144 | #define GPT_ENT_TYPE_APPLE_RAID \ 145 | {0x52414944,0x0000,0x11aa,0xaa,0x22,{0x00,0x30,0x65,0x43,0xec,0xac}} 146 | #define GPT_ENT_TYPE_APPLE_RAID_OFFLINE \ 147 | {0x52414944,0x5f4f,0x11aa,0xaa,0x22,{0x00,0x30,0x65,0x43,0xec,0xac}} 148 | #define GPT_ENT_TYPE_APPLE_LABEL \ 149 | {0x4C616265,0x6c00,0x11aa,0xaa,0x11,{0x00,0x30,0x65,0x43,0xec,0xac}} 150 | #define GPT_ENT_TYPE_APPLE_TV_RECOVERY \ 151 | {0x5265636f,0x7665,0x11AA,0xaa,0x11,{0x00,0x30,0x65,0x43,0xec,0xac}} 152 | 153 | #define GPT_ENT_TYPE_NETBSD_FFS \ 154 | {0x49f48d5a,0xb10e,0x11dc,0xb9,0x9b,{0x00,0x19,0xd1,0x87,0x96,0x48}} 155 | #define GPT_ENT_TYPE_NETBSD_LFS \ 156 | {0x49f48d82,0xb10e,0x11dc,0xb9,0x9b,{0x00,0x19,0xd1,0x87,0x96,0x48}} 157 | #define GPT_ENT_TYPE_NETBSD_SWAP \ 158 | {0x49f48d32,0xb10e,0x11dc,0xB9,0x9B,{0x00,0x19,0xd1,0x87,0x96,0x48}} 159 | #define GPT_ENT_TYPE_NETBSD_RAID \ 160 | {0x49f48daa,0xb10e,0x11dc,0xb9,0x9b,{0x00,0x19,0xd1,0x87,0x96,0x48}} 161 | #define GPT_ENT_TYPE_NETBSD_CCD \ 162 | {0x2db519c4,0xb10f,0x11dc,0xb9,0x9b,{0x00,0x19,0xd1,0x87,0x96,0x48}} 163 | #define GPT_ENT_TYPE_NETBSD_CGD \ 164 | {0x2db519ec,0xb10f,0x11dc,0xb9,0x9b,{0x00,0x19,0xd1,0x87,0x96,0x48}} 165 | 166 | /* 167 | * Boot partition used by GRUB 2. 168 | */ 169 | #define GPT_ENT_TYPE_BIOS_BOOT \ 170 | {0x21686148,0x6449,0x6e6f,0x74,0x4e,{0x65,0x65,0x64,0x45,0x46,0x49}} 171 | 172 | #endif /* GPT_H */ 173 | -------------------------------------------------------------------------------- /core/formats/partition/ps3.h: -------------------------------------------------------------------------------- 1 | #ifndef PS3_H 2 | #define PS3_H 3 | 4 | #include 5 | 6 | #define MAGIC1 0x0FACE0FFULL 7 | #define MAGIC2 0xDEADFACEULL 8 | 9 | struct p_acl_entry { 10 | uint64_t laid; 11 | uint64_t rights; 12 | }; 13 | 14 | struct d_partition { 15 | uint64_t p_start; 16 | uint64_t p_size; 17 | struct p_acl_entry p_acl[8]; 18 | }; 19 | 20 | struct disklabel { 21 | uint8_t d_res1[16]; 22 | uint64_t d_magic1; 23 | uint64_t d_magic2; 24 | uint64_t d_res2; 25 | uint64_t d_res3; 26 | }; 27 | 28 | #endif /* PS3_H */ 29 | -------------------------------------------------------------------------------- /core/io/data/bounded_data_provider.cpp: -------------------------------------------------------------------------------- 1 | #include "bounded_data_provider.hpp" 2 | 3 | #include 4 | 5 | namespace io { 6 | namespace data { 7 | 8 | BoundedDataProvider::BoundedDataProvider( 9 | DataProvider* base, // Parent data provider 10 | uint64_t start, // Offset in bytes to start of boundary 11 | uint64_t length, // Length in bytes of boundary 12 | uint32_t sectorSize) 13 | : DataProvider(base->getStream(), sectorSize) 14 | { 15 | this->position = 0; 16 | this->cryptoStrategy = base->getCryptoStrategy(); 17 | this->start = start; 18 | this->end = start + length; 19 | this->length = length; 20 | } 21 | 22 | uint64_t BoundedDataProvider::read(char* data, uint32_t length) 23 | { 24 | auto readLen = readInternal(this->start + this->tell(), data, length); 25 | this->position += readLen; 26 | return readLen; 27 | } 28 | 29 | uint64_t BoundedDataProvider::seek(int64_t offset, uint32_t whence) 30 | { 31 | // Contain the position within our boundaries 32 | switch (whence) { 33 | case 0: 34 | // Seek from beginning 35 | if (offset < 0) 36 | throw std::invalid_argument("Cannot seek before start of stream"); 37 | if (offset > this->length) 38 | offset = this->length; 39 | this->position = offset; 40 | break; 41 | case 1: 42 | // Seek from current position 43 | if (this->position + offset < 0) 44 | throw std::invalid_argument("Cannot seek before start of stream"); 45 | if (this->position + offset > this->length) 46 | offset = this->length - this->position; 47 | this->position += offset; 48 | break; 49 | case 2: 50 | // Seek from end, usually negative. Positive offsets have no effect. 51 | if (this->length + offset < 0) 52 | throw std::invalid_argument("Cannot seek before start of stream"); 53 | if (this->length + offset > this->length) 54 | offset = 0; 55 | this->position = this->length + offset; 56 | break; 57 | default: 58 | throw std::invalid_argument("Invalid whence. Must be 0, 1, or 2."); 59 | } 60 | return this->position; 61 | } 62 | 63 | uint64_t BoundedDataProvider::tell() 64 | { 65 | return this->position; 66 | } 67 | 68 | uint64_t BoundedDataProvider::getLength() const 69 | { 70 | return this->length; 71 | } 72 | 73 | } /* namespace data */ 74 | } /* namespace io */ 75 | -------------------------------------------------------------------------------- /core/io/data/bounded_data_provider.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOUNDED_DATA_PROVIDER_HPP 2 | #define BOUNDED_DATA_PROVIDER_HPP 3 | 4 | #include "data_provider.hpp" 5 | 6 | namespace io { 7 | namespace data { 8 | 9 | /** 10 | * @brief DataProvider with boundaries. 11 | * 12 | * This creates a sub-view of a parent DataProvider. 13 | */ 14 | class BoundedDataProvider : public DataProvider 15 | { 16 | public: 17 | BoundedDataProvider(DataProvider* base, 18 | uint64_t start, uint64_t length, uint32_t sectorSize); 19 | 20 | uint64_t read(char* data, uint32_t length) override; 21 | uint64_t seek(int64_t offset, uint32_t whence = 0) override; 22 | uint64_t tell() override; 23 | uint64_t getLength() const override; 24 | 25 | private: 26 | uint64_t position; 27 | uint64_t start; 28 | uint64_t end; 29 | uint64_t length; 30 | }; 31 | 32 | } /* namespace data */ 33 | } /* namespace io */ 34 | 35 | #endif /* BOUNDED_DATA_PROVIDER_HPP */ 36 | -------------------------------------------------------------------------------- /core/io/data/data_provider.cpp: -------------------------------------------------------------------------------- 1 | #include "data_provider.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace io { 9 | namespace data { 10 | 11 | DataProvider::DataProvider(stream::DiskStream* stream, uint32_t sectorSize) 12 | : stream(stream), sectorSize(sectorSize) 13 | { 14 | this->cryptoStrategy = nullptr; 15 | this->sectorBias = 0; 16 | } 17 | 18 | void DataProvider::setCryptoStrategy(crypto::CryptoStrategy* strategy) 19 | { 20 | this->cryptoStrategy = strategy; 21 | } 22 | 23 | void DataProvider::setSectorBias(uint32_t sector) 24 | { 25 | // TODO: Move this method to AesXtsStrategy or CryptoStrategy 26 | this->sectorBias = sector; 27 | } 28 | 29 | uint64_t DataProvider::read(char* data, uint32_t length) 30 | { 31 | auto readLen = readInternal(this->tell(), data, length); 32 | return readLen; 33 | } 34 | 35 | uint64_t DataProvider::readInternal(uint64_t offset, char* data, uint32_t length) 36 | { 37 | auto alignedOffset = offset; 38 | 39 | // Align our offset to the nearest sector 40 | if (alignedOffset % this->sectorSize != 0) 41 | alignedOffset -= offset % this->sectorSize; 42 | 43 | this->stream->seek(alignedOffset); 44 | 45 | auto alignedLength = ((offset - alignedOffset) + length + (this->sectorSize - 1)) 46 | & ~(this->sectorSize - 1); 47 | 48 | std::vector buffer(alignedLength); 49 | 50 | uint32_t sectorIndex = alignedOffset / this->sectorSize; 51 | 52 | auto readLen = this->stream->read(buffer.data(), alignedLength); 53 | 54 | if (this->cryptoStrategy) 55 | this->cryptoStrategy->decrypt(buffer.data(), sectorIndex - sectorBias, alignedLength); 56 | 57 | memcpy(data, buffer.data() + (offset - alignedOffset), length); 58 | 59 | return (readLen > length) ? length : readLen; 60 | } 61 | 62 | uint64_t DataProvider::seek(int64_t offset, uint32_t whence) 63 | { 64 | return this->stream->seek(offset, whence); 65 | } 66 | 67 | uint64_t DataProvider::tell() 68 | { 69 | return this->stream->tell(); 70 | } 71 | 72 | stream::DiskStream* DataProvider::getStream() const 73 | { 74 | return this->stream; 75 | } 76 | 77 | crypto::CryptoStrategy* DataProvider::getCryptoStrategy() const 78 | { 79 | return this->cryptoStrategy; 80 | } 81 | 82 | uint64_t DataProvider::getLength() const 83 | { 84 | return this->stream->getLength(); 85 | } 86 | 87 | } /* namespace data */ 88 | } /* namespace io */ 89 | -------------------------------------------------------------------------------- /core/io/data/data_provider.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DATA_PROVIDER_HPP 2 | #define DATA_PROVIDER_HPP 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | namespace io { 10 | namespace data { 11 | 12 | /** 13 | * @brief Provides data from the disk. 14 | * 15 | * This data provider takes an input `DiskStream` which is what is used to 16 | * access raw data from a disk and uses an optional `CryptoStrategy` to provide 17 | * post/pre processing. It also ensures that data is read using sector aligned 18 | * reads. 19 | */ 20 | class DataProvider 21 | { 22 | public: 23 | DataProvider(stream::DiskStream* stream, uint32_t sectorSize); 24 | 25 | /** 26 | * @brief Set a crypto strategy for this data provider. 27 | * 28 | * This crypto strategy will be used to either decrypt data after it is read, 29 | * or to encrypt data before it is written to the disk. 30 | * 31 | * @param strategy 32 | */ 33 | void setCryptoStrategy(crypto::CryptoStrategy* strategy); 34 | void setSectorBias(uint32_t sector); 35 | 36 | /** 37 | * @brief Read data from the disk. 38 | * 39 | * @param data The location to store the data being read. 40 | * @param length The amount of bytes to read. 41 | * @return uint64_t The amount of bytes read. 42 | */ 43 | virtual uint64_t read(char* data, uint32_t length); 44 | 45 | /** 46 | * @brief Seek to a location into the disk. 47 | * 48 | * @param offset The offset into the disk to seek to. 49 | * @param whence Where to seek from. 50 | * Use 0 to seek to an absolute offset. 51 | * Use 1 to seek from the current position. 52 | * Use 2 to seek from the end of the stream. 53 | * @return uint64_t The location seeked to. 54 | */ 55 | virtual uint64_t seek(int64_t offset, uint32_t whence = 0); 56 | 57 | /** 58 | * @brief Returns the current stream position. 59 | * 60 | * @return uint64_t Current position into the stream. 61 | */ 62 | virtual uint64_t tell(); 63 | 64 | /** 65 | * @brief Get the crypto strategy being used for this data provider. 66 | * 67 | * @return crypto::CryptoStrategy* 68 | */ 69 | crypto::CryptoStrategy* getCryptoStrategy() const; 70 | 71 | /** 72 | * @brief Get the raw input disk stream. 73 | * 74 | * @return stream::DiskStream* 75 | */ 76 | stream::DiskStream* getStream() const; 77 | 78 | /** 79 | * @brief Get the length of the input disk stream. 80 | * 81 | * @return uint64_t 82 | */ 83 | virtual uint64_t getLength() const; 84 | 85 | protected: 86 | virtual uint64_t readInternal(uint64_t offset, char* data, uint32_t length); 87 | 88 | stream::DiskStream* stream; 89 | crypto::CryptoStrategy* cryptoStrategy; 90 | uint32_t sectorSize; 91 | uint32_t sectorBias; 92 | }; 93 | 94 | } /* namespace data */ 95 | } /* namespace io */ 96 | 97 | #endif /* DATA_PROVIDER_HPP */ 98 | -------------------------------------------------------------------------------- /core/io/stream/disk_stream.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DISK_STREAM_HPP 2 | #define DISK_STREAM_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace io { 8 | namespace stream { 9 | 10 | /// Abstract interface for a disk stream 11 | class DiskStream 12 | { 13 | public: 14 | virtual uint64_t read(char* data, uint32_t length) = 0; 15 | virtual uint64_t seek(int64_t offset, uint32_t whence = 0) = 0; 16 | virtual uint64_t tell() = 0; 17 | virtual uint64_t getLength() const = 0; 18 | }; 19 | 20 | } /* namespace stream */ 21 | } /* namespace io */ 22 | 23 | #endif /* DISK_STREAM_HPP */ 24 | -------------------------------------------------------------------------------- /core/io/stream/disk_stream_factory.cpp: -------------------------------------------------------------------------------- 1 | #include "disk_stream_factory.hpp" 2 | 3 | #include "file_disk_stream.hpp" 4 | #include "physical_disk_stream.hpp" 5 | 6 | #if defined(_WIN32) 7 | #include 8 | #elif defined(__unix__) || defined(__APPLE__) 9 | #include 10 | #include 11 | #include 12 | #endif 13 | 14 | namespace io { 15 | namespace stream { 16 | 17 | DiskStream *DiskStreamFactory::getStream(std::string &path) { 18 | 19 | if (isDevice(path)) { 20 | return new io::stream::PhysicalDiskStream(path); 21 | } 22 | 23 | if (isFile(path)) { 24 | return new io::stream::FileDiskStream(path); 25 | } 26 | 27 | return nullptr; 28 | } 29 | 30 | bool DiskStreamFactory::isDevice(std::string &path) { 31 | // TODO: C++17 has std::filesystem::is_block_file() 32 | //std::filesystem::path path(path); 33 | //return std::filesystem::is_block_file(path); 34 | 35 | #if defined(_WIN32) 36 | 37 | DWORD attr = GetFileAttributes(path.c_str()); 38 | 39 | return attr & FILE_ATTRIBUTE_DEVICE; 40 | 41 | #elif defined(__unix__) || defined(__APPLE__) 42 | 43 | struct stat st; 44 | stat(path.c_str(), &st); 45 | return S_ISBLK(st.st_mode); 46 | 47 | #endif 48 | } 49 | 50 | bool DiskStreamFactory::isFile(std::string &path) { 51 | // TODO: C++17 has std::filesystem::is_regular_file() 52 | //std::filesystem::path path(path); 53 | //return std::filesystem::is_regular_file(path); 54 | 55 | #if defined(_WIN32) 56 | 57 | DWORD attr = GetFileAttributes(path.c_str()); 58 | 59 | // TODO: Might need some work. This just checks that it is not a device. 60 | // There doesn't seem to be a way to check if it's a file? 61 | return !(attr & FILE_ATTRIBUTE_DEVICE); 62 | 63 | #elif defined(__unix__) || defined(__APPLE__) 64 | 65 | struct stat st; 66 | stat(path.c_str(), &st); 67 | return S_ISREG(st.st_mode); 68 | 69 | #endif 70 | } 71 | 72 | } /* namespace stream */ 73 | } /* namespace io */ 74 | -------------------------------------------------------------------------------- /core/io/stream/disk_stream_factory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DISK_STREAM_FACTORY_HPP 2 | #define DISK_STREAM_FACTORY_HPP 3 | 4 | #include "disk_stream.hpp" 5 | 6 | #include 7 | 8 | namespace io { 9 | namespace stream { 10 | 11 | class DiskStreamFactory 12 | { 13 | public: 14 | /** 15 | * Detects the file type and returns the correct DiskStream type. 16 | * 17 | * @param path The path to the file or device. 18 | * @returns io::stream::DiskStream* 19 | */ 20 | static DiskStream *getStream(std::string &path); 21 | 22 | private: 23 | static bool isFile(std::string &path); 24 | static bool isDevice(std::string &path); 25 | }; 26 | 27 | } /* namespace stream */ 28 | } /* namespace io */ 29 | 30 | #endif /* DISK_STREAM_FACTORY_HPP */ 31 | -------------------------------------------------------------------------------- /core/io/stream/file_disk_stream.cpp: -------------------------------------------------------------------------------- 1 | #include "file_disk_stream.hpp" 2 | 3 | namespace io { 4 | namespace stream { 5 | 6 | FileDiskStream::FileDiskStream(std::string& path) 7 | { 8 | stream = new std::ifstream(path, std::ios::binary); 9 | const auto pos = stream->tellg(); 10 | stream->seekg(0, std::ios_base::end); 11 | this->length = stream->tellg(); 12 | stream->seekg(pos); 13 | } 14 | 15 | FileDiskStream::FileDiskStream(std::ifstream* file) 16 | : stream(file) 17 | { 18 | const auto pos = file->tellg(); 19 | file->seekg(0, std::ios_base::end); 20 | this->length = file->tellg(); 21 | file->seekg(pos); 22 | } 23 | 24 | uint64_t FileDiskStream::read(char* data, uint32_t length) 25 | { 26 | return stream->read(data, length).gcount(); 27 | } 28 | 29 | uint64_t FileDiskStream::seek(int64_t offset, uint32_t whence) 30 | { 31 | return stream->seekg(offset, std::ios_base::seekdir(whence)).tellg(); 32 | } 33 | 34 | uint64_t FileDiskStream::tell() 35 | { 36 | return stream->tellg(); 37 | } 38 | 39 | uint64_t FileDiskStream::getLength() const 40 | { 41 | return this->length; 42 | } 43 | 44 | } /* namespace stream */ 45 | } /* namespace io */ 46 | -------------------------------------------------------------------------------- /core/io/stream/file_disk_stream.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILE_DISK_STREAM_HPP 2 | #define FILE_DISK_STREAM_HPP 3 | 4 | #include "disk_stream.hpp" 5 | 6 | #include 7 | 8 | namespace io { 9 | namespace stream { 10 | 11 | /// Standard file stream handler 12 | class FileDiskStream : public DiskStream 13 | { 14 | public: 15 | FileDiskStream(std::string&); 16 | FileDiskStream(std::ifstream*); 17 | 18 | uint64_t read(char* data, uint32_t length) override; 19 | uint64_t seek(int64_t offset, uint32_t whence = 0) override; 20 | uint64_t tell() override; 21 | uint64_t getLength() const override; 22 | 23 | private: 24 | std::ifstream* stream; 25 | uint64_t length; 26 | }; 27 | 28 | } /* namespace stream */ 29 | } /* namespace io */ 30 | 31 | #endif /* FILE_DISK_STREAM_HPP */ 32 | -------------------------------------------------------------------------------- /core/io/stream/physical_disk_stream.cpp: -------------------------------------------------------------------------------- 1 | #include "physical_disk_stream.hpp" 2 | 3 | #if defined(__unix__) || defined(__APPLE__) 4 | #include 5 | #include 6 | #include 7 | #endif 8 | 9 | #if defined(__linux__) 10 | #include 11 | #elif defined(__APPLE__) 12 | #include 13 | #endif 14 | 15 | namespace io { 16 | namespace stream { 17 | 18 | PhysicalDiskStream::PhysicalDiskStream(std::string& path) 19 | { 20 | #if defined(_WIN32) 21 | handle = CreateFile( 22 | path.c_str(), // lpFileName 23 | GENERIC_READ, // dwDesiredAccess 24 | 0, // dwShareMode 25 | NULL, // lpSecurityAttributes 26 | OPEN_EXISTING,// dwCreationDisposition 27 | 0, // dwFlagsAndAttributes 28 | NULL // hTemplateFile 29 | ); 30 | 31 | DWORD dw = GetLastError(); 32 | 33 | GET_LENGTH_INFORMATION info = {0}; 34 | DWORD bytesReturned; 35 | DeviceIoControl(handle, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &info, 36 | sizeof(info), &bytesReturned, NULL); 37 | 38 | this->length = info.Length.QuadPart; 39 | #elif defined(__unix__) 40 | fd = open(path.c_str(), O_RDONLY); 41 | 42 | #if defined(__linux__) 43 | uint64_t size = 0; 44 | ioctl(fd, BLKGETSIZE64, &size); 45 | size *= 512ULL; 46 | #elif defined(__APPLE__) 47 | uint32_t bsize = 0; 48 | uint64_t count = 0; 49 | ioctl(fd, DKIOCGETBLOCKCOUNT, &count); 50 | ioctl(fd, DKIOCGETBLOCKSIZE, &bsize); 51 | uint64_t size = count * bsize; 52 | #endif 53 | 54 | this->length = size; 55 | #elif defined(__APPLE__) 56 | #endif 57 | } 58 | 59 | #if defined(_WIN32) 60 | PhysicalDiskStream::PhysicalDiskStream(HANDLE handle) 61 | : handle(handle) 62 | { 63 | GET_LENGTH_INFORMATION info = {0}; 64 | DWORD bytesReturned; 65 | DeviceIoControl(handle, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &info, 66 | sizeof(info), &bytesReturned, NULL); 67 | 68 | this->length = info.Length.QuadPart; 69 | } 70 | #endif 71 | 72 | uint64_t PhysicalDiskStream::read(char* data, uint32_t length) 73 | { 74 | #if defined(_WIN32) 75 | DWORD bytesRead; 76 | 77 | BOOL result = ReadFile(handle, (LPVOID)data, length, &bytesRead, NULL); 78 | 79 | if (result) 80 | return bytesRead; 81 | 82 | // TODO: Set a standard for what we return. 83 | return -1; 84 | #elif defined(__unix__) || defined(__APPLE__) 85 | return ::read(fd, (void *)data, length); 86 | #else 87 | throw "read() not implemented"; 88 | #endif 89 | } 90 | 91 | uint64_t PhysicalDiskStream::seek(int64_t offset, uint32_t whence) 92 | { 93 | #if defined(_WIN32) 94 | LARGE_INTEGER pos; 95 | 96 | SetFilePointerEx(handle, *(LARGE_INTEGER*)&offset, &pos, whence); 97 | 98 | return (uint64_t)pos.QuadPart; 99 | #elif defined(__unix__) || defined(__APPLE__) 100 | // TODO: We can optimize by keeping track of position, 101 | // then use pread() in our read() method. 102 | return lseek(fd, offset, whence); 103 | #else 104 | throw "seek() not implemented"; 105 | #endif 106 | } 107 | 108 | uint64_t PhysicalDiskStream::tell() 109 | { 110 | #if defined(_WIN32) 111 | LARGE_INTEGER pos; 112 | LARGE_INTEGER offset = {0}; 113 | 114 | SetFilePointerEx(handle, offset, &pos, FILE_CURRENT); 115 | 116 | return pos.QuadPart; 117 | #elif defined(__unix__) || defined(__APPLE__) 118 | return lseek(fd, 0, SEEK_CUR); 119 | #else 120 | throw "tell() not implemented"; 121 | #endif 122 | } 123 | 124 | uint64_t PhysicalDiskStream::getLength() const 125 | { 126 | return length; 127 | } 128 | 129 | } /* namespace stream */ 130 | } /* namespace io */ 131 | -------------------------------------------------------------------------------- /core/io/stream/physical_disk_stream.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PHYSICAL_DISK_STREAM_HPP 2 | #define PHYSICAL_DISK_STREAM_HPP 3 | 4 | #include "disk_stream.hpp" 5 | 6 | #include 7 | 8 | #if defined(_WIN32) 9 | #include 10 | #elif defined(__unix__) || defined(__APPLE__) 11 | #include 12 | #include 13 | #include 14 | #endif 15 | 16 | namespace io { 17 | namespace stream { 18 | 19 | /** 20 | * A DiskStream for physical devices (e.g. PhysicalDriveX or /dev/sda). 21 | * 22 | * This was added because standard file streams don't seem to support these 23 | * file types. 24 | */ 25 | class PhysicalDiskStream : public DiskStream 26 | { 27 | public: 28 | PhysicalDiskStream(std::string &); 29 | #if defined(_WIN32) 30 | PhysicalDiskStream(HANDLE handle); 31 | #endif 32 | 33 | uint64_t read(char *data, uint32_t length) override; 34 | uint64_t seek(int64_t offset, uint32_t whence = 0) override; 35 | uint64_t tell() override; 36 | uint64_t getLength() const override; 37 | 38 | private: 39 | #if defined(_WIN32) 40 | HANDLE handle; 41 | #elif defined(__unix__) || defined(__APPLE__) 42 | int fd; 43 | #endif 44 | uint64_t length; 45 | }; 46 | 47 | } /* namespace stream */ 48 | } /* namepsace io */ 49 | 50 | #endif /* PHYSICAL_DISK_STREAM_HPP */ 51 | -------------------------------------------------------------------------------- /core/logging/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.hpp" 2 | 3 | namespace logging { 4 | 5 | ILogHandler::ILogHandler() : level(LogLevel::Error) {} 6 | 7 | void ILogHandler::setLogLevel(LogLevel level) 8 | { 9 | this->level = level; 10 | } 11 | 12 | bool ILogHandler::isLevelEnabled(LogLevel level) const 13 | { 14 | return level <= this->level; 15 | } 16 | 17 | LogManager* LogManager::getInstance() 18 | { 19 | static LogManager g_logger; 20 | return &g_logger; 21 | } 22 | 23 | void LogManager::addHandler(std::string name, ILogHandler* handler) 24 | { 25 | handlers[name] = handler; 26 | } 27 | 28 | void LogManager::removeHandler(std::string name) 29 | { 30 | handlers.erase(name); 31 | } 32 | 33 | void LogManager::log(std::string& msg, LogLevel level) 34 | { 35 | for (auto const& kv : handlers) { 36 | auto handler = kv.second; 37 | if (handler->isLevelEnabled(level)) { 38 | switch (level) { 39 | case LogLevel::Info: 40 | handler->info(msg); 41 | break; 42 | case LogLevel::Warn: 43 | handler->warn(msg); 44 | break; 45 | case LogLevel::Error: 46 | handler->error(msg); 47 | break; 48 | case LogLevel::Debug: 49 | handler->debug(msg); 50 | break; 51 | } 52 | } 53 | } 54 | } 55 | 56 | void LogManager::info(std::string msg) 57 | { 58 | log(msg, LogLevel::Info); 59 | } 60 | 61 | void LogManager::warn(std::string msg) 62 | { 63 | log(msg, LogLevel::Warn); 64 | } 65 | 66 | void LogManager::error(std::string msg) 67 | { 68 | log(msg, LogLevel::Error); 69 | } 70 | 71 | void LogManager::debug(std::string msg) 72 | { 73 | log(msg, LogLevel::Debug); 74 | } 75 | 76 | } /* namespace logging */ 77 | -------------------------------------------------------------------------------- /core/logging/logger.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_HPP 2 | #define LOGGER_HPP 3 | 4 | #include 5 | #include 6 | 7 | /// Add a log handler to the logger. 8 | #define rAddHandler(name, handler) \ 9 | logging::LogManager::getInstance()->addHandler(name, handler) 10 | 11 | /// Print an info message. 12 | #define rInfo(msg) logging::LogManager::getInstance()->info(msg) 13 | /// Print a warning message. 14 | #define rWarn(msg) logging::LogManager::getInstance()->warn(msg) 15 | /// Print an error message. 16 | #define rError(msg) logging::LogManager::getInstance()->error(msg) 17 | /// Print a debug message. 18 | #define rDebug(msg) logging::LogManager::getInstance()->debug(msg) 19 | 20 | namespace logging { 21 | 22 | enum class LogLevel 23 | { 24 | Info, 25 | Warn, 26 | Error, 27 | Debug 28 | }; 29 | 30 | /** 31 | * @brief An interface for a log handler. 32 | * 33 | * Implement this interface, then add it to the LogManager. 34 | */ 35 | class ILogHandler 36 | { 37 | public: 38 | ILogHandler(); 39 | 40 | /** 41 | * @brief Set the log level for this log handler. 42 | * 43 | * @param level 44 | */ 45 | void setLogLevel(LogLevel level); 46 | 47 | /** 48 | * @brief Check whether or not this log level is enabled. 49 | * 50 | * @param level 51 | * @return true 52 | * @return false 53 | */ 54 | bool isLevelEnabled(LogLevel level) const; 55 | 56 | /** 57 | * @brief Handles printing of info messages. 58 | * 59 | * @param msg 60 | */ 61 | virtual void info(std::string& msg) {}; 62 | 63 | /** 64 | * @brief Handles printing of warning messages. 65 | * 66 | * @param msg 67 | */ 68 | virtual void warn(std::string& msg) {}; 69 | 70 | /** 71 | * @brief Handles printing of error messages. 72 | * 73 | * @param msg 74 | */ 75 | virtual void error(std::string& msg) {}; 76 | 77 | /** 78 | * @brief Handles printing of debug messages. 79 | * 80 | * @param msg 81 | */ 82 | virtual void debug(std::string& msg) {}; 83 | 84 | private: 85 | LogLevel level; 86 | }; 87 | 88 | class LogManager 89 | { 90 | public: 91 | static LogManager* getInstance(); 92 | 93 | /** 94 | * @brief Add a log handler. 95 | * 96 | * @param name Name of this log handler. 97 | * @param handler The log handler. 98 | */ 99 | void addHandler(std::string name, ILogHandler* handler); 100 | 101 | /** 102 | * @brief Remove a log handler by name. 103 | * 104 | * @param name Name of the log handler to remove. 105 | */ 106 | void removeHandler(std::string name); 107 | 108 | /** 109 | * @brief Push a log message to each log handler. 110 | * 111 | * @param msg The message. 112 | * @param level The log level to print. 113 | */ 114 | void log(std::string& msg, LogLevel level); 115 | 116 | /** 117 | * @brief Push an info message to each log handler. 118 | * 119 | * @param msg The message. 120 | */ 121 | void info(std::string msg); 122 | 123 | /** 124 | * @brief Push a warning message to each log handler. 125 | * 126 | * @param msg The message. 127 | */ 128 | void warn(std::string msg); 129 | 130 | /** 131 | * @brief Push an error message to each log handler. 132 | * 133 | * @param msg The message. 134 | */ 135 | void error(std::string msg); 136 | 137 | /** 138 | * @brief Push a debug message to each log handler. 139 | * 140 | * @param msg The message. 141 | */ 142 | void debug(std::string msg); 143 | 144 | private: 145 | std::map handlers; 146 | }; 147 | 148 | } /* namespace logging */ 149 | 150 | #endif /* LOGGER_HPP */ 151 | -------------------------------------------------------------------------------- /core/logging/stdout_log_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "stdout_log_handler.hpp" 2 | 3 | #include 4 | 5 | namespace logging { 6 | 7 | void StdOutLogHandler::info(std::string& msg) 8 | { 9 | std::cout << "\033[0m" << "[Info] " << msg << "\033[0m" << std::endl; 10 | } 11 | 12 | void StdOutLogHandler::warn(std::string& msg) 13 | { 14 | std::cout << "\033[0;33m" << "[Warn] " << msg << "\033[0m" << std::endl; 15 | } 16 | 17 | void StdOutLogHandler::error(std::string& msg) 18 | { 19 | std::cout << "\033[0;31m" << "[Error] " << msg << "\033[0m" << std::endl; 20 | } 21 | 22 | void StdOutLogHandler::debug(std::string& msg) 23 | { 24 | std::cout << "\033[0;34m" << "[Debug] " << msg << "\033[0m" << std::endl; 25 | } 26 | 27 | } /* namespace logging */ 28 | -------------------------------------------------------------------------------- /core/logging/stdout_log_handler.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDOUT_LOG_HANDLER_HPP 2 | #define STDOUT_LOG_HANDLER_HPP 3 | 4 | #include "logger.hpp" 5 | 6 | namespace logging { 7 | 8 | /** 9 | * @brief Log handler that prints to stdout. 10 | */ 11 | class StdOutLogHandler : public ILogHandler 12 | { 13 | public: 14 | void info(std::string& msg) override; 15 | void warn(std::string& msg) override; 16 | void error(std::string& msg) override; 17 | void debug(std::string& msg) override; 18 | }; 19 | 20 | } /* namespace logging */ 21 | 22 | #endif /* STDOUT_LOG_HANDLER_HPP */ 23 | -------------------------------------------------------------------------------- /core/utilities/endian.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ENDIAN_HPP 2 | #define ENDIAN_HPP 3 | 4 | #define swap16(val) \ 5 | (uint16_t)(((val & 0xff00) >> 8) | ((val & 0x00ff) << 8)) 6 | 7 | #define swap32(val) \ 8 | ((uint32_t)(((((uint32_t)val) & 0xff000000) >> 24) | \ 9 | ((((uint32_t)val) & 0x00ff0000) >> 8 ) | \ 10 | ((((uint32_t)val) & 0x0000ff00) << 8 ) | \ 11 | ((((uint32_t)val) & 0x000000ff) << 24))) 12 | 13 | #define swap64(val) \ 14 | ((uint64_t)(((((uint64_t)val) & 0xff00000000000000ull) >> 56) | \ 15 | ((((uint64_t)val) & 0x00ff000000000000ull) >> 40) | \ 16 | ((((uint64_t)val) & 0x0000ff0000000000ull) >> 24) | \ 17 | ((((uint64_t)val) & 0x000000ff00000000ull) >> 8 ) | \ 18 | ((((uint64_t)val) & 0x00000000ff000000ull) << 8 ) | \ 19 | ((((uint64_t)val) & 0x0000000000ff0000ull) << 24) | \ 20 | ((((uint64_t)val) & 0x000000000000ff00ull) << 40) | \ 21 | ((((uint64_t)val) & 0x00000000000000ffull) << 56))) 22 | 23 | #endif /* ENDIAN_HPP */ 24 | -------------------------------------------------------------------------------- /core/vfs/adapters/adapter.cpp: -------------------------------------------------------------------------------- 1 | #include "adapter.hpp" 2 | 3 | namespace vfs { 4 | 5 | VfsAdapter::VfsAdapter(io::data::DataProvider* dataProvider) 6 | { 7 | this->dataProvider = dataProvider; 8 | } 9 | 10 | } /* namespace vfs */ 11 | -------------------------------------------------------------------------------- /core/vfs/adapters/adapter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ADAPTER_HPP 2 | #define ADAPTER_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace vfs { 12 | 13 | /** 14 | * *** File System Operations *** 15 | * Mount: 16 | * Returns the VfsNode that represents the root. 17 | * Unmount: 18 | * Unmounts the file system 19 | * GetFsInfo: 20 | * Get information about the file system (block size, ...) 21 | * Create/Open: 22 | * Create a new file and return the file handle 23 | * Close: 24 | * Close a file handle 25 | * Read: 26 | * Read from a file 27 | * Write: 28 | * Write to a file 29 | * List: 30 | * Get a list of VfsNodes from within a directory 31 | * GetFileInfo: 32 | * Get information about a file 33 | * SetFileInfo: 34 | * Set file information 35 | **/ 36 | 37 | // struct IoRequestParams 38 | // { 39 | // uint64_t offset; 40 | // }; 41 | 42 | class VfsAdapter 43 | { 44 | public: 45 | VfsAdapter(io::data::DataProvider* dataProvider); 46 | 47 | /** 48 | * @brief Mount the file system. 49 | * 50 | * Currently this operation will index the file system and return the root 51 | * node. 52 | * 53 | * @return VfsDirectory* 54 | */ 55 | virtual VfsDirectory* mount() = 0; 56 | 57 | /** 58 | * @brief Unmount the file system. 59 | */ 60 | virtual void unmount() = 0; 61 | 62 | // Eventually we will need more dynamic accessor methods, but for now, 63 | // we will keep it simple and just create an index of the file system. 64 | /** 65 | * @brief Get file system information. 66 | */ 67 | //virtual void getFsInfo() = 0; 68 | 69 | /** 70 | * @brief Create a new file in this file system. 71 | */ 72 | //virtual VfsFile* create(std::string& path) = 0; 73 | 74 | /** 75 | * @brief Remove a file from this file system. 76 | */ 77 | //virtual void remove(VfsFile* entry) = 0; 78 | 79 | /** 80 | * @brief Read data from a file. 81 | */ 82 | //virtual void read(VfsFile* file, IoRequestParams* params) = 0; 83 | 84 | /** 85 | * @brief Write data to a file. 86 | */ 87 | //virtual void write(VfsFile* file, IoRequestParams* params) = 0; 88 | 89 | /** 90 | * @brief List files in a directory. 91 | */ 92 | //virtual std::vector list(VfsDirectory* dir) = 0; 93 | 94 | /** 95 | * @brief Get information about a file. 96 | */ 97 | //virtual void getFileInfo(VfsFile* file, VfsFileInfo* info) = 0; 98 | 99 | /** 100 | * @brief Set information about a file. 101 | */ 102 | //virtual void setFileInfo(VfsFile* file) = 0; 103 | 104 | protected: 105 | io::data::DataProvider* dataProvider; 106 | }; 107 | 108 | } /* namespace vfs */ 109 | 110 | #endif /* ADAPTER_HPP */ 111 | -------------------------------------------------------------------------------- /core/vfs/adapters/fat/fat_.h: -------------------------------------------------------------------------------- 1 | #ifndef FAT_H 2 | #define FAT_H 3 | 4 | struct bpb { 5 | uint16_t bytesPerSector; 6 | uint8_t sectorsPerCluster; 7 | uint16_t reservedSectors; 8 | uint8_t fats; 9 | uint16_t rootEntries; 10 | uint16_t sectors; // Total number of sectors in the volume 11 | uint8_t media; 12 | uint16_t sectorsPerFat; 13 | uint16_t sectorsPerTrack; 14 | uint16_t heads; 15 | uint16_t hiddenSectors; 16 | uint16_t largeSectors; 17 | }; 18 | 19 | #pragma pack(push,1) 20 | struct bpb_ex { 21 | uint16_t bytesPerSector; // 0x0 22 | uint8_t sectorsPerCluster; // 0x2 23 | uint16_t reservedSectors; // 0x3 24 | uint8_t fats; // 0x5 25 | uint16_t rootEntries; // 0x6 26 | uint16_t sectors; // 0x8 27 | uint8_t media; // 0xA 28 | uint16_t sectorsPerFat; // 0xB 29 | uint16_t sectorsPerTrack; // 0xD 30 | uint16_t heads; // 0xF 31 | uint32_t hiddenSectors; // 0x11 32 | uint32_t largeSectors; // 0x15 33 | uint32_t largeSectorsPerFat; // 0x19 34 | uint16_t extendedFlags; // 0x1D 35 | uint16_t fsVersion; // 0x1F 36 | uint32_t rootDirFirstCluster; // 0x21 37 | uint16_t fsInfoSector; // 0x25 38 | uint16_t backupBootSector; // 0x27 39 | uint8_t reserved[12]; // 0x29 40 | }; // 0x35 41 | #pragma pack(pop) 42 | 43 | struct bootsector { 44 | uint8_t jump; 45 | uint8_t oem[8]; 46 | bpb packedBpb; 47 | uint8_t physicalDriveNumber; 48 | uint8_t currentHead; 49 | uint8_t signature; 50 | uint8_t id[4]; 51 | uint8_t volumeLabel[11]; 52 | uint8_t systemId[8]; 53 | }; 54 | 55 | struct bootsector_ex { 56 | uint8_t jump; 57 | uint8_t oem[8]; 58 | bpb_ex packedBpb; 59 | uint8_t physicalDriveNumber; 60 | uint8_t currentHead; 61 | uint8_t signature; 62 | uint8_t id[4]; 63 | uint8_t volumeLabel[11]; 64 | uint8_t systemId[8]; 65 | }; 66 | 67 | struct fsinfo { 68 | uint32_t sectorBeginSignature; 69 | uint8_t extraBootCode[480]; 70 | uint32_t fsInfoSignature; 71 | uint32_t freeClusterCount; 72 | uint32_t nextFreeCluster; 73 | uint8_t reserved[12]; 74 | uint32_t sectorEndSignature; 75 | }; 76 | 77 | struct fat_time { 78 | uint16_t doubleSeconds : 5; 79 | uint16_t minute : 6; 80 | uint16_t hour : 5; 81 | }; 82 | 83 | struct fat_date { 84 | uint16_t day : 5; 85 | uint16_t month : 4; 86 | uint16_t year : 7; 87 | }; 88 | 89 | struct fat_time_stamp { 90 | fat_time time; 91 | fat_date date; 92 | }; 93 | 94 | struct dirent { 95 | uint8_t fileName[11]; 96 | uint8_t attributes; 97 | uint8_t ntByte; 98 | uint8_t creationMsec; 99 | fat_time_stamp creationTime; 100 | fat_date lastAccessDate; 101 | union { 102 | uint16_t extendedAttributes; 103 | uint16_t firstClusterOfFileHi; 104 | }; 105 | fat_time_stamp lastWriteTime; 106 | uint16_t firstClusterOfFile; 107 | uint32_t fileSize; 108 | }; 109 | 110 | #pragma pack(push,1) 111 | struct dirent_lfn { 112 | uint8_t ordinal; 113 | uint16_t name1[5]; 114 | uint8_t attributes; 115 | uint8_t type; 116 | uint8_t checksum; 117 | uint16_t name2[6]; 118 | uint16_t mustBeZero; 119 | uint16_t name3[2]; 120 | }; 121 | #pragma pack(pop) 122 | 123 | #define FAT_DIRENT_NEVER_USED 0x00 124 | #define FAT_DIRENT_REALLY_0E5 0x05 125 | #define FAT_DIRENT_DIRECTORY_ALIAS 0x2e 126 | #define FAT_DIRENT_DELETED 0xe5 127 | 128 | #define FAT_DIRENT_ATTR_READ_ONLY 0x01 129 | #define FAT_DIRENT_ATTR_HIDDEN 0x02 130 | #define FAT_DIRENT_ATTR_SYSTEM 0x04 131 | #define FAT_DIRENT_ATTR_VOLUME_ID 0x08 132 | #define FAT_DIRENT_ATTR_DIRECTORY 0x10 133 | #define FAT_DIRENT_ATTR_ARCHIVE 0x20 134 | #define FAT_DIRENT_ATTR_LFN (FAT_DIRENT_ATTR_READ_ONLY | \ 135 | FAT_DIRENT_ATTR_HIDDEN | \ 136 | FAT_DIRENT_ATTR_SYSTEM | \ 137 | FAT_DIRENT_ATTR_VOLUME_ID) 138 | 139 | #define FAT_CLUSTER_AVAILABLE 0x00000000 140 | #define FAT_CLUSTER_RESERVED 0x0ffffff0 141 | #define FAT_CLUSTER_BAD 0x0ffffff7 142 | #define FAT_CLUSTER_LAST 0x0fffffff 143 | 144 | #define FAT_LAST_LONG_ENTRY 0x40 145 | 146 | #define IsBpbFat32(bpb) (*(uint16_t*)(&(bpb)->sectorsPerFat) == 0) 147 | 148 | #define FatBytesPerCluster(B) ((uint32_t)(B->bytesPerSector * B->sectorsPerCluster)) 149 | #define FatBytesPerFat(B) (IsBpbFat32(B) ? \ 150 | (uint32_t)(B->bytesPerSector * B->largeSectorsPerFat) : \ 151 | (uint32_t)(B->bytesPerSector * B->sectorsPerFat)) 152 | #define FatReservedBytes(B) (B->bytesPerSector * B->reservedSectors) 153 | 154 | #endif /* FAT_H */ 155 | -------------------------------------------------------------------------------- /core/vfs/adapters/fat_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include "fat_adapter.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace vfs { 8 | namespace adapters { 9 | 10 | FatAdapter::FatAdapter(io::data::DataProvider* dataProvider, bool swap) 11 | : VfsAdapter(dataProvider) 12 | { 13 | this->needsSwap = swap; 14 | } 15 | 16 | VfsDirectory* FatAdapter::mount() 17 | { 18 | bootSector = new FatBootSector(dataProvider); 19 | fat = new FatFileAllocationTable(dataProvider, bootSector); 20 | 21 | fileAreaByteOffset = bootSector->getFileAreaByteOffset(); 22 | bytesPerCluster = bootSector->getBytesPerCluster(); 23 | maxDirents = bytesPerCluster / sizeof(dirent); 24 | 25 | return readRootDirectory(); 26 | } 27 | 28 | void FatAdapter::unmount() 29 | { 30 | } 31 | 32 | VfsDirectory* FatAdapter::readRootDirectory() 33 | { 34 | VfsDirectory* root = new VfsDirectory(); 35 | 36 | if (bootSector->getFatType() == FatType::FAT32) { 37 | std::vector chain; 38 | fat->getClusterChain(bootSector->getRootDirFirstCluster(), chain); 39 | 40 | // For FAT32, we read the root directory like a regular file 41 | std::vector buffer(bytesPerCluster); 42 | for (auto cluster : chain) { 43 | auto clusterOffset = clusterToFileAreaByteOffset(cluster); 44 | dataProvider->seek(clusterOffset); 45 | dataProvider->read(buffer.data(), bytesPerCluster); 46 | 47 | loadDirectory(buffer, root); 48 | } 49 | } 50 | else { 51 | auto rootDirOffset = bootSector->getFatByteOffset() + 52 | (bootSector->getBytesPerFat() * bootSector->getNumFats()); 53 | auto rootDirSize = bootSector->getRootDirEntryCount() * sizeof(dirent); 54 | 55 | std::vector buffer(rootDirSize); 56 | dataProvider->seek(rootDirOffset); 57 | dataProvider->read(buffer.data(), rootDirSize); 58 | 59 | loadDirectory(buffer, root); 60 | } 61 | 62 | return root; 63 | } 64 | 65 | void FatAdapter::loadDirectory(std::vector& buffer, VfsDirectory* root) 66 | { 67 | // TODO: Move all this to a FatIndexer class. 68 | std::u16string longFileName; 69 | auto lfnIndex = 0; 70 | auto useLfn = false; 71 | auto curr = buffer.data(); 72 | auto end = buffer.data() + buffer.size(); 73 | 74 | while (curr < end) { 75 | auto* dir = reinterpret_cast(curr); 76 | 77 | // Check if we are at the end of the list 78 | if (dir->fileName[0] == FAT_DIRENT_NEVER_USED) 79 | break; 80 | 81 | // Skip deleted entries 82 | if (dir->fileName[0] == FAT_DIRENT_DELETED) { 83 | curr += sizeof(dirent); 84 | continue; 85 | } 86 | 87 | // Check if it's a long file name entry 88 | if (dir->attributes == FAT_DIRENT_ATTR_LFN) { 89 | useLfn = true; 90 | 91 | auto lfn = reinterpret_cast(dir); 92 | 93 | longFileName.insert(0, reinterpret_cast(&lfn->name1), 5); 94 | longFileName.insert(5, reinterpret_cast(&lfn->name2), 6); 95 | longFileName.insert(11, reinterpret_cast(&lfn->name3), 2); 96 | } 97 | else { 98 | VfsNode* node; 99 | 100 | std::string name; 101 | if (useLfn) { 102 | // Convert unicode name to ascii 103 | for (auto i = 0; i < longFileName.length(); i++) { 104 | auto u = longFileName[i]; 105 | if (u == 0xffff || u == 0x0000) 106 | break; 107 | 108 | name.push_back(u); 109 | } 110 | 111 | rDebug(name); 112 | 113 | longFileName.clear(); 114 | } 115 | else { 116 | name.resize(0xB); 117 | name.assign(dir->fileName, dir->fileName + 0xb); 118 | } 119 | 120 | if (name.compare(". ") && name.compare(".. ")) { 121 | if (dir->attributes & FAT_DIRENT_ATTR_DIRECTORY) { 122 | node = new VfsDirectory(); 123 | 124 | std::vector chain; 125 | fat->getClusterChain(dir->firstClusterOfFile, chain); 126 | 127 | // TODO: Copy and paste from readRootDirectory(), refactor this. 128 | std::vector ents(bytesPerCluster); 129 | for (auto cluster : chain) { 130 | auto clusterOffset = clusterToFileAreaByteOffset(cluster); 131 | dataProvider->seek(clusterOffset); 132 | dataProvider->read(ents.data(), bytesPerCluster); 133 | 134 | loadDirectory(ents, static_cast(node)); 135 | } 136 | } 137 | else { 138 | VfsFile* file = new VfsFile(); 139 | 140 | file->setFileSize(dir->fileSize); 141 | 142 | node = file; 143 | } 144 | 145 | node->setName(name); 146 | node->setCreationTime( 147 | dir->creationTime.date.day, 148 | dir->creationTime.date.month, 149 | dir->creationTime.date.year + 1980, 150 | dir->creationTime.time.doubleSeconds * 2, 151 | dir->creationTime.time.minute, 152 | dir->creationTime.time.hour 153 | ); 154 | 155 | node->setLastModifiedTime( 156 | dir->lastWriteTime.date.day, 157 | dir->lastWriteTime.date.month, 158 | dir->lastWriteTime.date.year + 1980, 159 | dir->lastWriteTime.time.doubleSeconds * 2, 160 | dir->lastWriteTime.time.minute, 161 | dir->lastWriteTime.time.hour 162 | ); 163 | 164 | node->setLastAccessTime( 165 | dir->lastAccessDate.day, 166 | dir->lastAccessDate.month, 167 | dir->lastAccessDate.year + 1980 168 | ); 169 | 170 | root->addChild(node); 171 | } 172 | 173 | useLfn = false; 174 | } 175 | 176 | curr += sizeof(dirent); 177 | } 178 | } 179 | 180 | uint64_t FatAdapter::clusterToFileAreaByteOffset(uint32_t clusterIndex) 181 | { 182 | return fileAreaByteOffset + ((clusterIndex - 2) * bytesPerCluster); 183 | } 184 | 185 | } /* namespace adapters */ 186 | } /* namespace vfs */ 187 | -------------------------------------------------------------------------------- /core/vfs/adapters/fat_adapter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FAT_ADAPTER_HPP 2 | #define FAT_ADAPTER_HPP 3 | 4 | #include 5 | #include 6 | #include "fat/fat_.h" 7 | #include "fat_boot_sector.hpp" 8 | #include "fat_file_allocation_table.hpp" 9 | 10 | namespace vfs { 11 | namespace adapters { 12 | 13 | class FatAdapter : public VfsAdapter 14 | { 15 | public: 16 | FatAdapter(io::data::DataProvider* dataProvider, bool swap = false); 17 | 18 | VfsDirectory* mount() override; 19 | void unmount() override; 20 | 21 | private: 22 | VfsDirectory* readRootDirectory(); 23 | void loadDirectory(std::vector& buffer, VfsDirectory* root); 24 | uint64_t clusterToFileAreaByteOffset(uint32_t clusterIndex); 25 | 26 | FatBootSector* bootSector; 27 | FatFileAllocationTable* fat; 28 | uint64_t fileAreaByteOffset; 29 | uint32_t bytesPerCluster; 30 | uint32_t maxDirents; 31 | bool needsSwap; 32 | }; 33 | 34 | } /* namespace adapters */ 35 | } /* namespace vfs */ 36 | 37 | #endif /* FAT_ADAPTER_HPP */ 38 | -------------------------------------------------------------------------------- /core/vfs/adapters/fat_boot_sector.cpp: -------------------------------------------------------------------------------- 1 | #include "fat_boot_sector.hpp" 2 | 3 | #include "fat/fat_.h" 4 | 5 | namespace vfs { 6 | namespace adapters { 7 | 8 | FatBootSector::FatBootSector(io::data::DataProvider* dataProvider) 9 | { 10 | this->dataProvider = dataProvider; 11 | 12 | this->readBootSector(0); 13 | } 14 | 15 | void FatBootSector::readBootSector(uint64_t offset) 16 | { 17 | dataProvider->seek(offset + 0xb); 18 | 19 | bytesPerSector = readUInt16(); 20 | sectorsPerCluster = readUInt8(); 21 | reservedSectors = readUInt16(); 22 | numFats = readUInt8(); 23 | rootEntryCount = readUInt16(); 24 | totalSectors = readUInt16(); 25 | media = readUInt8(); 26 | sectorsPerFat = readUInt16(); 27 | sectorsPerTrack = readUInt16(); 28 | numHeads = readUInt16(); 29 | hiddenSectors = readUInt32(); 30 | totalSectors32 = readUInt32(); 31 | 32 | if (sectorsPerFat == 0) { 33 | type = FatType::FAT32; 34 | 35 | largeSectorsPerFat = readUInt32(); 36 | extendedFlags = readUInt16(); 37 | fsVersion = readUInt16(); 38 | rootDirFirstCluster = readUInt32(); 39 | fsInfoSector = readUInt16(); 40 | backupBootSector = readUInt16(); 41 | } 42 | else { 43 | auto clusterCount = 0; // Clusters available in the file area 44 | auto fileAreaLbo = reservedSectors + (numFats * sectorsPerFat) 45 | + ((rootEntryCount * sizeof(dirent)) / bytesPerSector); 46 | 47 | if (totalSectors) 48 | clusterCount = (totalSectors - fileAreaLbo) / sectorsPerCluster; 49 | else 50 | clusterCount = (totalSectors32 - fileAreaLbo) / sectorsPerCluster; 51 | 52 | if (clusterCount < 4087) 53 | type = FatType::FAT12; 54 | else 55 | type = FatType::FAT16; 56 | } 57 | } 58 | 59 | uint16_t FatBootSector::getReservedSectors() 60 | { 61 | return this->reservedSectors; 62 | } 63 | 64 | uint8_t FatBootSector::getNumFats() 65 | { 66 | return this->numFats; 67 | } 68 | 69 | uint32_t FatBootSector::getSectorsPerFat() 70 | { 71 | return (type == FatType::FAT32) ? largeSectorsPerFat : sectorsPerFat; 72 | } 73 | 74 | uint64_t FatBootSector::getFatByteOffset() 75 | { 76 | return this->reservedSectors * this->bytesPerSector; 77 | } 78 | 79 | uint64_t FatBootSector::getBytesPerFat() 80 | { 81 | return this->getSectorsPerFat() * this->bytesPerSector; 82 | } 83 | 84 | uint64_t FatBootSector::getFileAreaByteOffset() 85 | { 86 | if (type == FatType::FAT32) { 87 | return getFatByteOffset() + (getBytesPerFat() * getNumFats()); 88 | } 89 | else { 90 | return getFatByteOffset() + (getBytesPerFat() * getNumFats()) + 91 | rootEntryCount * sizeof(dirent); 92 | } 93 | } 94 | 95 | uint32_t FatBootSector::getRootDirFirstCluster() 96 | { 97 | return (type == FatType::FAT32) ? rootDirFirstCluster : 2; 98 | } 99 | 100 | uint32_t FatBootSector::getRootDirEntryCount() 101 | { 102 | return this->rootEntryCount; 103 | } 104 | 105 | uint32_t FatBootSector::getSectorsPerCluster() 106 | { 107 | return this->sectorsPerCluster; 108 | } 109 | 110 | uint32_t FatBootSector::getBytesPerCluster() 111 | { 112 | return this->sectorsPerCluster * this->bytesPerSector; 113 | } 114 | 115 | FatType FatBootSector::getFatType() 116 | { 117 | return this->type; 118 | } 119 | 120 | uint8_t FatBootSector::readUInt8() 121 | { 122 | uint8_t value; 123 | dataProvider->read(reinterpret_cast(&value), sizeof(value)); 124 | return value; 125 | } 126 | 127 | uint16_t FatBootSector::readUInt16() 128 | { 129 | uint16_t value; 130 | dataProvider->read(reinterpret_cast(&value), sizeof(value)); 131 | return value; 132 | } 133 | 134 | uint32_t FatBootSector::readUInt32() 135 | { 136 | uint32_t value; 137 | dataProvider->read(reinterpret_cast(&value), sizeof(value)); 138 | return value; 139 | } 140 | 141 | } /* namespace adapters */ 142 | } /* namespace vfs */ 143 | -------------------------------------------------------------------------------- /core/vfs/adapters/fat_boot_sector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FAT_BOOT_SECTOR_HPP 2 | #define FAT_BOOT_SECTOR_HPP 3 | 4 | #include 5 | 6 | namespace vfs { 7 | namespace adapters { 8 | 9 | enum class FatType { 10 | FAT12, 11 | FAT16, 12 | FAT32 13 | }; 14 | 15 | class FatBootSector 16 | { 17 | public: 18 | FatBootSector(io::data::DataProvider* dataProvider); 19 | 20 | uint16_t getReservedSectors(); 21 | uint8_t getNumFats(); 22 | uint32_t getSectorsPerFat(); 23 | uint64_t getFatByteOffset(); 24 | uint64_t getBytesPerFat(); 25 | uint64_t getFileAreaByteOffset(); 26 | uint32_t getRootDirFirstCluster(); 27 | uint32_t getRootDirEntryCount(); 28 | uint32_t getSectorsPerCluster(); 29 | uint32_t getBytesPerCluster(); 30 | FatType getFatType(); 31 | 32 | private: 33 | void readBootSector(uint64_t offset); 34 | 35 | // TODO: Write a helper class for this. 36 | uint8_t readUInt8(); 37 | uint16_t readUInt16(); 38 | uint32_t readUInt32(); 39 | 40 | io::data::DataProvider* dataProvider; 41 | FatType type; 42 | 43 | // These fields are read from the boot sector. 44 | uint16_t bytesPerSector; 45 | uint8_t sectorsPerCluster; 46 | uint16_t reservedSectors; 47 | uint8_t numFats; 48 | uint16_t rootEntryCount; 49 | uint16_t totalSectors; 50 | uint8_t media; 51 | uint16_t sectorsPerFat; 52 | uint16_t sectorsPerTrack; 53 | uint16_t numHeads; 54 | uint32_t hiddenSectors; 55 | uint32_t totalSectors32; 56 | 57 | uint32_t largeSectorsPerFat; 58 | uint16_t extendedFlags; 59 | uint16_t fsVersion; 60 | uint32_t rootDirFirstCluster; 61 | uint16_t fsInfoSector; 62 | uint16_t backupBootSector; 63 | }; 64 | 65 | } 66 | } 67 | 68 | #endif /* FAT_BOOT_SECTOR_HPP */ -------------------------------------------------------------------------------- /core/vfs/adapters/fat_file_allocation_table.cpp: -------------------------------------------------------------------------------- 1 | #include "fat_file_allocation_table.hpp" 2 | 3 | namespace vfs { 4 | namespace adapters { 5 | 6 | FatFileAllocationTable::FatFileAllocationTable( 7 | io::data::DataProvider* dataProvider, FatBootSector* bootSector) 8 | { 9 | this->dataProvider = dataProvider; 10 | this->bootSector = bootSector; 11 | this->readFileAllocationTable(); 12 | } 13 | 14 | void FatFileAllocationTable::readFileAllocationTable() 15 | { 16 | auto fatByteOffset = bootSector->getFatByteOffset(); 17 | auto bytesPerFat = bootSector->getBytesPerFat(); 18 | dataProvider->seek(fatByteOffset); 19 | 20 | fileAllocationTableBuffer.resize(bytesPerFat); 21 | dataProvider->read(reinterpret_cast(fileAllocationTableBuffer.data()), 22 | bytesPerFat); 23 | 24 | // backupFileAllocationTableBuffer.resize(bytesPerFat); 25 | // dataProvider->read(reinterpret_cast(backupFileAllocationTableBuffer.data()), 26 | // bytesPerFat); 27 | } 28 | 29 | void FatFileAllocationTable::getClusterChain(uint32_t firstCluster, 30 | std::vector& chain) 31 | { 32 | chain.push_back(firstCluster); 33 | 34 | auto nextCluster = getFatEntry(firstCluster); 35 | while (nextCluster <= FAT_CLUSTER_RESERVED) { 36 | chain.push_back(nextCluster); 37 | nextCluster = getFatEntry(nextCluster); 38 | } 39 | } 40 | 41 | uint32_t FatFileAllocationTable::getFatEntry(uint32_t clusterIndex) 42 | { 43 | switch (bootSector->getFatType()) { 44 | case FatType::FAT12: 45 | return this->getFat12Entry(clusterIndex); 46 | case FatType::FAT16: 47 | return this->getFat16Entry(clusterIndex); 48 | case FatType::FAT32: 49 | return this->getFat32Entry(clusterIndex); 50 | } 51 | } 52 | 53 | uint32_t FatFileAllocationTable::getFat12Entry(uint32_t clusterIndex) 54 | { 55 | return FAT_CLUSTER_LAST; 56 | } 57 | 58 | uint32_t FatFileAllocationTable::getFat16Entry(uint32_t clusterIndex) 59 | { 60 | uint32_t entry = reinterpret_cast(fileAllocationTableBuffer.data())[clusterIndex]; 61 | if (entry >= 0x0FFF0) { 62 | entry |= 0x0FFF0000; 63 | } 64 | return entry; 65 | } 66 | 67 | uint32_t FatFileAllocationTable::getFat32Entry(uint32_t clusterIndex) 68 | { 69 | return reinterpret_cast(fileAllocationTableBuffer.data())[clusterIndex]; 70 | } 71 | 72 | } /* namespace adapters */ 73 | } /* namespace vfs */ 74 | -------------------------------------------------------------------------------- /core/vfs/adapters/fat_file_allocation_table.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILE_ALLOCATION_TABLE_HPP 2 | #define FILE_ALLOCATION_TABLE_HPP 3 | 4 | #include 5 | 6 | #include "fat_boot_sector.hpp" 7 | #include "fat/fat_.h" 8 | 9 | namespace vfs { 10 | namespace adapters { 11 | 12 | class FatFileAllocationTable 13 | { 14 | public: 15 | FatFileAllocationTable(io::data::DataProvider* dataProvider, 16 | FatBootSector* bootSector); 17 | 18 | void getClusterChain(uint32_t firstCluster, std::vector& chain); 19 | 20 | private: 21 | void readFileAllocationTable(); 22 | 23 | uint32_t getFatEntry(uint32_t clusterIndex); 24 | uint32_t getFat12Entry(uint32_t clusterIndex); 25 | uint32_t getFat16Entry(uint32_t clusterIndex); 26 | uint32_t getFat32Entry(uint32_t clusterIndex); 27 | 28 | io::data::DataProvider* dataProvider; 29 | FatBootSector* bootSector; 30 | 31 | std::vector fileAllocationTableBuffer; 32 | }; 33 | 34 | } /* namespace adapters */ 35 | } /* namespace vfs */ 36 | 37 | #endif /* FILE_ALLOCATION_TABLE_HPP */ -------------------------------------------------------------------------------- /core/vfs/adapters/fatx_adapter.cpp: -------------------------------------------------------------------------------- 1 | #include "fatx_adapter.hpp" -------------------------------------------------------------------------------- /core/vfs/adapters/fatx_adapter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FATX_ADAPTER_HPP 2 | #define FATX_ADAPTER_HPP 3 | 4 | #include "adapter.hpp" 5 | 6 | namespace vfs { 7 | namespace adapters { 8 | 9 | class FatXAdapter : public VfsAdapter 10 | { 11 | VfsDirectory* mount(); // initialize file system 12 | void unmount(); // probably not needed 13 | // We will need: 14 | // A bitmap of occupied blocks 15 | // The sector size and block size 16 | // Max file size 17 | // void getFsInfo(); 18 | // void read(VfsFile* file, IoRequestParams* params); 19 | // void write(VfsFile* file, IoRequestParams* params); 20 | // std::vector list(VfsDirectory* dir); 21 | // void getFileInfo(VfsFile* file, VfsFileInfo* info); 22 | }; 23 | 24 | } /* namespace adapters */ 25 | } /* namespace vfs */ 26 | 27 | #endif /* FATX_ADAPTER_HPP */ -------------------------------------------------------------------------------- /core/vfs/adapters/ufs/types.h: -------------------------------------------------------------------------------- 1 | #ifndef UFS_TYPES_H 2 | #define UFS_TYPES_H 3 | 4 | typedef unsigned char u_char; 5 | typedef unsigned short u_short; 6 | typedef unsigned int u_int; 7 | typedef unsigned long u_long; 8 | 9 | typedef uint8_t u_int8_t; 10 | typedef uint16_t u_int16_t; 11 | typedef uint32_t u_int32_t; 12 | typedef uint64_t u_int64_t; 13 | 14 | #define ino_t uint32_t 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /core/vfs/adapters/ufs/ufs/dinode.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2002 Networks Associates Technology, Inc. 3 | * All rights reserved. 4 | * 5 | * This software was developed for the FreeBSD Project by Marshall 6 | * Kirk McKusick and Network Associates Laboratories, the Security 7 | * Research Division of Network Associates, Inc. under DARPA/SPAWAR 8 | * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS 9 | * research program 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | * 32 | * Copyright (c) 1982, 1989, 1993 33 | * The Regents of the University of California. All rights reserved. 34 | * (c) UNIX System Laboratories, Inc. 35 | * All or some portions of this file are derived from material licensed 36 | * to the University of California by American Telephone and Telegraph 37 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with 38 | * the permission of UNIX System Laboratories, Inc. 39 | * 40 | * Redistribution and use in source and binary forms, with or without 41 | * modification, are permitted provided that the following conditions 42 | * are met: 43 | * 1. Redistributions of source code must retain the above copyright 44 | * notice, this list of conditions and the following disclaimer. 45 | * 2. Redistributions in binary form must reproduce the above copyright 46 | * notice, this list of conditions and the following disclaimer in the 47 | * documentation and/or other materials provided with the distribution. 48 | * 3. The names of the authors may not be used to endorse or promote 49 | * products derived from this software without specific prior written 50 | * permission. 51 | * 52 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 53 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 56 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 | * SUCH DAMAGE. 63 | * 64 | * @(#)dinode.h 8.3 (Berkeley) 1/21/94 65 | * $FreeBSD$ 66 | */ 67 | 68 | #ifndef _UFS_UFS_DINODE_H_ 69 | #define _UFS_UFS_DINODE_H_ 70 | 71 | /* 72 | * The root inode is the root of the filesystem. Inode 0 can't be used for 73 | * normal purposes and historically bad blocks were linked to inode 1, thus 74 | * the root inode is 2. (Inode 1 is no longer used for this purpose, however 75 | * numerous dump tapes make this assumption, so we are stuck with it). 76 | */ 77 | #define ROOTINO ((ino_t)2) 78 | 79 | /* 80 | * The Whiteout inode# is a dummy non-zero inode number which will 81 | * never be allocated to a real file. It is used as a place holder 82 | * in the directory entry which has been tagged as a DT_WHT entry. 83 | * See the comments about ROOTINO above. 84 | */ 85 | #define WINO ((ino_t)1) 86 | 87 | /* 88 | * The size of physical and logical block numbers and time fields in UFS. 89 | */ 90 | typedef int32_t ufs1_daddr_t; 91 | typedef int64_t ufs2_daddr_t; 92 | typedef int64_t ufs_lbn_t; 93 | typedef int64_t ufs_time_t; 94 | 95 | /* File permissions. */ 96 | #define IEXEC 0000100 /* Executable. */ 97 | #define IWRITE 0000200 /* Writeable. */ 98 | #define IREAD 0000400 /* Readable. */ 99 | #define ISVTX 0001000 /* Sticky bit. */ 100 | #define ISGID 0002000 /* Set-gid. */ 101 | #define ISUID 0004000 /* Set-uid. */ 102 | 103 | /* File types. */ 104 | #define IFMT 0170000 /* Mask of file type. */ 105 | #define IFIFO 0010000 /* Named pipe (fifo). */ 106 | #define IFCHR 0020000 /* Character device. */ 107 | #define IFDIR 0040000 /* Directory file. */ 108 | #define IFBLK 0060000 /* Block device. */ 109 | #define IFREG 0100000 /* Regular file. */ 110 | #define IFLNK 0120000 /* Symbolic link. */ 111 | #define IFSOCK 0140000 /* UNIX domain socket. */ 112 | #define IFWHT 0160000 /* Whiteout. */ 113 | 114 | /* 115 | * A dinode contains all the meta-data associated with a UFS2 file. 116 | * This structure defines the on-disk format of a dinode. Since 117 | * this structure describes an on-disk structure, all its fields 118 | * are defined by types with precise widths. 119 | */ 120 | 121 | #define NXADDR 2 /* External addresses in inode. */ 122 | #define NDADDR 12 /* Direct addresses in inode. */ 123 | #define NIADDR 3 /* Indirect addresses in inode. */ 124 | 125 | struct ufs2_dinode { 126 | u_int16_t di_mode; /* 0: IFMT, permissions; see below. */ 127 | int16_t di_nlink; /* 2: File link count. */ 128 | u_int32_t di_uid; /* 4: File owner. */ 129 | u_int32_t di_gid; /* 8: File group. */ 130 | u_int32_t di_blksize; /* 12: Inode blocksize. */ 131 | u_int64_t di_size; /* 16: File byte count. */ 132 | u_int64_t di_blocks; /* 24: Blocks actually held. */ 133 | ufs_time_t di_atime; /* 32: Last access time. */ 134 | ufs_time_t di_mtime; /* 40: Last modified time. */ 135 | ufs_time_t di_ctime; /* 48: Last inode change time. */ 136 | ufs_time_t di_birthtime; /* 56: Inode creation time. */ 137 | int32_t di_mtimensec; /* 64: Last modified time. */ 138 | int32_t di_atimensec; /* 68: Last access time. */ 139 | int32_t di_ctimensec; /* 72: Last inode change time. */ 140 | int32_t di_birthnsec; /* 76: Inode creation time. */ 141 | u_int32_t di_gen; /* 80: Generation number. */ 142 | u_int32_t di_kernflags; /* 84: Kernel flags. */ 143 | u_int32_t di_flags; /* 88: Status flags (chflags). */ 144 | u_int32_t di_extsize; /* 92: External attributes size. */ 145 | ufs2_daddr_t di_extb[NXADDR];/* 96: External attributes block. */ 146 | ufs2_daddr_t di_db[NDADDR]; /* 112: Direct disk blocks. */ 147 | ufs2_daddr_t di_ib[NIADDR]; /* 208: Indirect disk blocks. */ 148 | u_int64_t di_modrev; /* 232: i_modrev for NFSv4 */ 149 | ino_t di_freelink; /* 240: SUJ: Next unlinked inode. */ 150 | uint32_t di_spare[3]; /* 244: Reserved; currently unused */ 151 | }; 152 | 153 | /* 154 | * The di_db fields may be overlaid with other information for 155 | * file types that do not have associated disk storage. Block 156 | * and character devices overlay the first data block with their 157 | * dev_t value. Short symbolic links place their path in the 158 | * di_db area. 159 | */ 160 | #define di_rdev di_db[0] 161 | 162 | /* 163 | * A UFS1 dinode contains all the meta-data associated with a UFS1 file. 164 | * This structure defines the on-disk format of a UFS1 dinode. Since 165 | * this structure describes an on-disk structure, all its fields 166 | * are defined by types with precise widths. 167 | */ 168 | struct ufs1_dinode { 169 | u_int16_t di_mode; /* 0: IFMT, permissions; see below. */ 170 | int16_t di_nlink; /* 2: File link count. */ 171 | ino_t di_freelink; /* 4: SUJ: Next unlinked inode. */ 172 | u_int64_t di_size; /* 8: File byte count. */ 173 | int32_t di_atime; /* 16: Last access time. */ 174 | int32_t di_atimensec; /* 20: Last access time. */ 175 | int32_t di_mtime; /* 24: Last modified time. */ 176 | int32_t di_mtimensec; /* 28: Last modified time. */ 177 | int32_t di_ctime; /* 32: Last inode change time. */ 178 | int32_t di_ctimensec; /* 36: Last inode change time. */ 179 | ufs1_daddr_t di_db[NDADDR]; /* 40: Direct disk blocks. */ 180 | ufs1_daddr_t di_ib[NIADDR]; /* 88: Indirect disk blocks. */ 181 | u_int32_t di_flags; /* 100: Status flags (chflags). */ 182 | u_int32_t di_blocks; /* 104: Blocks actually held. */ 183 | u_int32_t di_gen; /* 108: Generation number. */ 184 | u_int32_t di_uid; /* 112: File owner. */ 185 | u_int32_t di_gid; /* 116: File group. */ 186 | u_int64_t di_modrev; /* 120: i_modrev for NFSv4 */ 187 | }; 188 | 189 | #endif /* _UFS_UFS_DINODE_H_ */ 190 | -------------------------------------------------------------------------------- /core/vfs/adapters/ufs/ufs/dir.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1982, 1986, 1989, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * (c) UNIX System Laboratories, Inc. 5 | * All or some portions of this file are derived from material licensed 6 | * to the University of California by American Telephone and Telegraph 7 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 | * the permission of UNIX System Laboratories, Inc. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 4. Neither the name of the University nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | * SUCH DAMAGE. 33 | * 34 | * @(#)dir.h 8.2 (Berkeley) 1/21/94 35 | * $FreeBSD$ 36 | */ 37 | 38 | #ifndef _UFS_UFS_DIR_H_ 39 | #define _UFS_UFS_DIR_H_ 40 | 41 | /* 42 | * Theoretically, directories can be more than 2Gb in length, however, in 43 | * practice this seems unlikely. So, we define the type doff_t as a 32-bit 44 | * quantity to keep down the cost of doing lookup on a 32-bit machine. 45 | */ 46 | #define doff_t int32_t 47 | #define MAXDIRSIZE (0x7fffffff) 48 | 49 | /* 50 | * A directory consists of some number of blocks of DIRBLKSIZ 51 | * bytes, where DIRBLKSIZ is chosen such that it can be transferred 52 | * to disk in a single atomic operation (e.g. 512 bytes on most machines). 53 | * 54 | * Each DIRBLKSIZ byte block contains some number of directory entry 55 | * structures, which are of variable length. Each directory entry has 56 | * a struct direct at the front of it, containing its inode number, 57 | * the length of the entry, and the length of the name contained in 58 | * the entry. These are followed by the name padded to a 4 byte boundary 59 | * with null bytes. All names are guaranteed null terminated. 60 | * The maximum length of a name in a directory is MAXNAMLEN. 61 | * 62 | * The macro DIRSIZ(fmt, dp) gives the amount of space required to represent 63 | * a directory entry. Free space in a directory is represented by 64 | * entries which have dp->d_reclen > DIRSIZ(fmt, dp). All DIRBLKSIZ bytes 65 | * in a directory block are claimed by the directory entries. This 66 | * usually results in the last entry in a directory having a large 67 | * dp->d_reclen. When entries are deleted from a directory, the 68 | * space is returned to the previous entry in the same directory 69 | * block by increasing its dp->d_reclen. If the first entry of 70 | * a directory block is free, then its dp->d_ino is set to 0. 71 | * Entries other than the first in a directory do not normally have 72 | * dp->d_ino set to 0. 73 | */ 74 | #define DIRBLKSIZ DEV_BSIZE 75 | #define MAXNAMLEN 255 76 | 77 | struct direct { 78 | u_int32_t d_ino; /* inode number of entry */ 79 | u_int16_t d_reclen; /* length of this record */ 80 | u_int8_t d_type; /* file type, see below */ 81 | u_int8_t d_namlen; /* length of string in d_name */ 82 | char d_name[MAXNAMLEN + 1];/* name with length <= MAXNAMLEN */ 83 | }; 84 | 85 | /* 86 | * File types 87 | */ 88 | #define DT_UNKNOWN 0 89 | #define DT_FIFO 1 90 | #define DT_CHR 2 91 | #define DT_DIR 4 92 | #define DT_BLK 6 93 | #define DT_REG 8 94 | #define DT_LNK 10 95 | #define DT_SOCK 12 96 | #define DT_WHT 14 97 | 98 | /* 99 | * Convert between stat structure types and directory types. 100 | */ 101 | #define IFTODT(mode) (((mode) & 0170000) >> 12) 102 | #define DTTOIF(dirtype) ((dirtype) << 12) 103 | 104 | /* 105 | * The DIRSIZ macro gives the minimum record length which will hold 106 | * the directory entry. This requires the amount of space in struct direct 107 | * without the d_name field, plus enough space for the name with a terminating 108 | * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. 109 | * 110 | * 111 | */ 112 | #define DIRECTSIZ(namlen) \ 113 | (((uintptr_t)&((struct direct *)0)->d_name + \ 114 | ((namlen)+1)*sizeof(((struct direct *)0)->d_name[0]) + 3) & ~3) 115 | #if (BYTE_ORDER == LITTLE_ENDIAN) 116 | #define DIRSIZ(oldfmt, dp) \ 117 | ((oldfmt) ? DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen)) 118 | #else 119 | #define DIRSIZ(oldfmt, dp) \ 120 | DIRECTSIZ((dp)->d_namlen) 121 | #endif 122 | #define OLDDIRFMT 1 123 | #define NEWDIRFMT 0 124 | 125 | /* 126 | * Template for manipulating directories. Should use struct direct's, 127 | * but the name field is MAXNAMLEN - 1, and this just won't do. 128 | */ 129 | struct dirtemplate { 130 | u_int32_t dot_ino; 131 | int16_t dot_reclen; 132 | u_int8_t dot_type; 133 | u_int8_t dot_namlen; 134 | char dot_name[4]; /* must be multiple of 4 */ 135 | u_int32_t dotdot_ino; 136 | int16_t dotdot_reclen; 137 | u_int8_t dotdot_type; 138 | u_int8_t dotdot_namlen; 139 | char dotdot_name[4]; /* ditto */ 140 | }; 141 | 142 | /* 143 | * This is the old format of directories, sanz type element. 144 | */ 145 | struct odirtemplate { 146 | u_int32_t dot_ino; 147 | int16_t dot_reclen; 148 | u_int16_t dot_namlen; 149 | char dot_name[4]; /* must be multiple of 4 */ 150 | u_int32_t dotdot_ino; 151 | int16_t dotdot_reclen; 152 | u_int16_t dotdot_namlen; 153 | char dotdot_name[4]; /* ditto */ 154 | }; 155 | #endif /* !_DIR_H_ */ 156 | -------------------------------------------------------------------------------- /core/vfs/adapters/ufs2_adapter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UFS2_ADAPTER_HPP 2 | #define UFS2_ADAPTER_HPP 3 | 4 | #include "adapter.hpp" 5 | #include "utilities/endian.hpp" 6 | 7 | #include "ufs/types.h" 8 | #include "ufs/ffs/fs.h" 9 | #include "ufs/ufs/dir.h" 10 | #include "ufs/ufs/dinode.h" 11 | 12 | #include 13 | 14 | namespace vfs { 15 | namespace adapters { 16 | 17 | class Ufs2Adapter : public VfsAdapter 18 | { 19 | public: 20 | Ufs2Adapter(io::data::DataProvider* dataProvider, bool swap = false); 21 | 22 | VfsDirectory* mount(); 23 | void unmount(); 24 | 25 | private: 26 | void getInode(uint32_t ino, ufs2_dinode* inode); 27 | void loadDirectory(VfsDirectory* root, ufs2_dinode* dinode); 28 | std::vector getBlockListForInode(ufs2_dinode* inode); 29 | std::vector readBlocks(std::vector blockList); 30 | void swapInode(ufs2_dinode* inode); 31 | void swapDirect(direct* di); 32 | void swapSuperblock(fs* superblock); 33 | bool checkSuperblock(fs* superblock); 34 | void loadDataOffsets(VfsNode* node, ufs2_dinode* inode); 35 | void loadIndirectBlockTable(VfsNode* node, ufs2_daddr_t addr); 36 | 37 | /* In-place endian swap functions.. */ 38 | 39 | template ::type = true> 40 | T swap(T& v) { 41 | v = needsSwap ? swap64(v) : v; 42 | return v; 43 | } 44 | 45 | template ::type = true> 46 | T swap(T& v) { 47 | v = needsSwap ? swap32(v) : v; 48 | return v; 49 | } 50 | 51 | template ::type = true> 52 | T swap(T& v) { 53 | v = needsSwap ? swap16(v) : v; 54 | return v; 55 | } 56 | 57 | fs* super; 58 | bool needsSwap; 59 | }; 60 | 61 | } /* namespace adapters */ 62 | } /* namespace vfs */ 63 | 64 | #endif /* UFS2_ADAPTER_HPP */ 65 | -------------------------------------------------------------------------------- /core/vfs/date_time.cpp: -------------------------------------------------------------------------------- 1 | #include "date_time.hpp" 2 | 3 | namespace vfs { 4 | 5 | VfsDateTime::VfsDateTime() 6 | { 7 | init(); 8 | } 9 | 10 | VfsDateTime::VfsDateTime(int day, int month, int year) 11 | { 12 | init(day, month, year); 13 | } 14 | 15 | VfsDateTime::VfsDateTime(int day, int month, int year, 16 | int second, int minute, int hour) 17 | { 18 | init(day, month, year, second, minute, hour); 19 | } 20 | 21 | void VfsDateTime::init(int day, int month, int year, 22 | int second, int minute, int hour) 23 | { 24 | this->day = day; 25 | this->month = month; 26 | this->year = year; 27 | this->second = second; 28 | this->minute = minute; 29 | this->hour = hour; 30 | } 31 | 32 | void VfsDateTime::setDateTime(int day, int month, int year, 33 | int second, int minute, int hour) 34 | { 35 | init(day, month, year, second, minute, hour); 36 | } 37 | 38 | void VfsDateTime::setFromTm(const tm* t) 39 | { 40 | this->day = t->tm_mday; 41 | this->month = t->tm_mon; 42 | this->year = t->tm_year; 43 | this->second = t->tm_sec; 44 | this->minute = t->tm_min; 45 | this->hour = t->tm_hour; 46 | } 47 | 48 | void VfsDateTime::setFromTime(const time_t* t) 49 | { 50 | auto time = localtime(t); 51 | 52 | this->day = time->tm_mday; 53 | this->month = time->tm_mon + 1; 54 | this->year = time->tm_year + 1900; 55 | this->second = time->tm_sec; 56 | this->minute = time->tm_min; 57 | this->hour = time->tm_hour; 58 | } 59 | 60 | VfsDateTime* VfsDateTime::setDay(int day) 61 | { 62 | this->day = day; 63 | return this; 64 | } 65 | 66 | VfsDateTime* VfsDateTime::setMonth(int month) 67 | { 68 | this->month = month; 69 | return this; 70 | } 71 | 72 | VfsDateTime* VfsDateTime::setYear(int year) 73 | { 74 | this->year = year; 75 | return this; 76 | } 77 | 78 | VfsDateTime* VfsDateTime::setSecond(int second) 79 | { 80 | this->second = second; 81 | return this; 82 | } 83 | 84 | VfsDateTime* VfsDateTime::setMinute(int minute) 85 | { 86 | this->minute = minute; 87 | return this; 88 | } 89 | 90 | VfsDateTime* VfsDateTime::setHour(int hour) 91 | { 92 | this->hour = hour; 93 | return this; 94 | } 95 | 96 | int VfsDateTime::getDay() const 97 | { 98 | return this->day; 99 | } 100 | 101 | int VfsDateTime::getMonth() const 102 | { 103 | return this->month; 104 | } 105 | 106 | int VfsDateTime::getYear() const 107 | { 108 | return this->year; 109 | } 110 | 111 | int VfsDateTime::getSecond() const 112 | { 113 | return this->second; 114 | } 115 | 116 | int VfsDateTime::getMinute() const 117 | { 118 | return this->minute; 119 | } 120 | 121 | int VfsDateTime::getHour() const 122 | { 123 | return this->hour; 124 | } 125 | 126 | } /* namespace vfs */ 127 | -------------------------------------------------------------------------------- /core/vfs/date_time.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DATE_TIME_HPP 2 | #define DATE_TIME_HPP 3 | 4 | #include 5 | 6 | namespace vfs { 7 | 8 | class VfsDateTime 9 | { 10 | public: 11 | VfsDateTime(); 12 | VfsDateTime(int day, int month, int year); 13 | VfsDateTime(int day, int month, int year, int second, int minute, int hour); 14 | 15 | void setFromTm(const tm* t); 16 | void setFromTime(const time_t* t); 17 | void setDateTime(int day, int month, int year, 18 | int second, int minute, int hour); 19 | 20 | VfsDateTime* setDay(int day); 21 | VfsDateTime* setMonth(int month); 22 | VfsDateTime* setYear(int year); 23 | VfsDateTime* setSecond(int second); 24 | VfsDateTime* setMinute(int minute); 25 | VfsDateTime* setHour(int hour); 26 | 27 | int getDay() const; 28 | int getMonth() const; 29 | int getYear() const; 30 | int getSecond() const; 31 | int getMinute() const; 32 | int getHour() const; 33 | 34 | private: 35 | void init(int day = 0, int month = 0, int year = 0, 36 | int second = 0, int minute = 0, int hour = 0); 37 | 38 | int day; 39 | int month; 40 | int year; 41 | int second; 42 | int minute; 43 | int hour; 44 | }; 45 | 46 | } /* namespace vfs */ 47 | 48 | #endif -------------------------------------------------------------------------------- /core/vfs/directory.cpp: -------------------------------------------------------------------------------- 1 | #include "directory.hpp" 2 | 3 | namespace vfs { 4 | 5 | VfsDirectory::VfsDirectory() : VfsNode(VfsNodeType::DIRECTORY) {} 6 | 7 | void VfsDirectory::addChild(VfsNode* child) 8 | { 9 | children.push_back(child); 10 | } 11 | 12 | std::vector VfsDirectory::getChildren() 13 | { 14 | return children; 15 | } 16 | 17 | uint32_t VfsDirectory::getChildCount() const 18 | { 19 | return children.size(); 20 | } 21 | 22 | } /* namespace vfs */ -------------------------------------------------------------------------------- /core/vfs/directory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DIRECTORY_HPP 2 | #define DIRECTORY_HPP 3 | 4 | #include "node.hpp" 5 | 6 | #include 7 | 8 | namespace vfs { 9 | 10 | class VfsDirectory : public VfsNode 11 | { 12 | public: 13 | VfsDirectory(); 14 | 15 | /** 16 | * @brief Add a child node to this directory. 17 | * 18 | * @param child 19 | */ 20 | void addChild(VfsNode* child); 21 | 22 | /** 23 | * @brief Get a list of child node's. 24 | * 25 | * @return std::vector 26 | */ 27 | std::vector getChildren(); 28 | 29 | /** 30 | * @brief Get the number of nodes in this directory. 31 | * 32 | * @return uint32_t 33 | */ 34 | uint32_t getChildCount() const; 35 | 36 | private: 37 | std::vector children; 38 | }; 39 | 40 | } /* namespace vfs */ 41 | 42 | #endif /* DIRECTORY_HPP */ -------------------------------------------------------------------------------- /core/vfs/file.cpp: -------------------------------------------------------------------------------- 1 | #include "file.hpp" 2 | 3 | namespace vfs { 4 | 5 | VfsFile::VfsFile() : VfsNode(VfsNodeType::FILE) {} 6 | 7 | void VfsFile::setFileSize(uint64_t size) 8 | { 9 | this->fileSize = size; 10 | } 11 | 12 | uint64_t VfsFile::getFileSize() const 13 | { 14 | return this->fileSize; 15 | } 16 | 17 | } /* namespace vfs */ -------------------------------------------------------------------------------- /core/vfs/file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILE_HPP 2 | #define FILE_HPP 3 | 4 | #include "node.hpp" 5 | 6 | namespace vfs { 7 | 8 | class VfsFile : public VfsNode 9 | { 10 | public: 11 | VfsFile(); 12 | 13 | //VfsFileStream* getStream(); 14 | 15 | /** 16 | * @brief Set this file's size. 17 | * 18 | * @param size The file size. 19 | */ 20 | void setFileSize(uint64_t size); 21 | 22 | /** 23 | * @brief Get this file's size. 24 | * 25 | * @return uint64_t The file size. 26 | */ 27 | uint64_t getFileSize() const; 28 | 29 | private: 30 | uint64_t fileSize; 31 | // File handle attributes 32 | // uint64_t offset; 33 | // uint32_t id; 34 | // uint32_t flags; 35 | }; 36 | 37 | } /* namespace vfs */ 38 | 39 | #endif /* FILE_HPP */ 40 | -------------------------------------------------------------------------------- /core/vfs/file_system.cpp: -------------------------------------------------------------------------------- 1 | #include "file_system.hpp" 2 | 3 | namespace vfs { 4 | 5 | Vfs::Vfs() 6 | { 7 | this->root = nullptr; 8 | this->adapter = nullptr; 9 | this->mounted = false; 10 | } 11 | 12 | void Vfs::setAdapter(VfsAdapter* adapter) 13 | { 14 | this->adapter = adapter; 15 | } 16 | 17 | void Vfs::mount() 18 | { 19 | this->root = this->adapter->mount(); 20 | if (this->root) 21 | this->mounted = true; 22 | } 23 | 24 | VfsDirectory* Vfs::getRoot() const 25 | { 26 | return this->root; 27 | } 28 | 29 | bool Vfs::isMounted() const 30 | { 31 | return this->mounted; 32 | } 33 | 34 | } /* namespace vfs */ 35 | -------------------------------------------------------------------------------- /core/vfs/file_system.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILE_SYSTEM_HPP 2 | #define FILE_SYSTEM_HPP 3 | 4 | #include "directory.hpp" 5 | #include "adapters/adapter.hpp" 6 | 7 | namespace vfs { 8 | 9 | class Vfs 10 | { 11 | public: 12 | Vfs(); 13 | 14 | /** 15 | * @brief Mount the file system. 16 | */ 17 | void mount(); 18 | 19 | /** 20 | * @brief Get the root directory for this file system. 21 | * 22 | * @return VfsDirectory* 23 | */ 24 | VfsDirectory* getRoot() const; 25 | 26 | /** 27 | * @brief Set the adapter to use to mount the file system. 28 | * 29 | * @param adapter 30 | */ 31 | void setAdapter(VfsAdapter* adapter); 32 | 33 | /** 34 | * @brief Check if this file system is currently mounted. 35 | * 36 | * @return true 37 | * @return false 38 | */ 39 | bool isMounted() const; 40 | 41 | private: 42 | VfsDirectory* root; 43 | VfsAdapter* adapter; 44 | bool mounted; 45 | }; 46 | 47 | } /* namespace vfs */ 48 | 49 | #endif /* FILE_SYSTEM_HPP */ -------------------------------------------------------------------------------- /core/vfs/node.cpp: -------------------------------------------------------------------------------- 1 | #include "node.hpp" 2 | 3 | namespace vfs { 4 | 5 | VfsNode::VfsNode(VfsNodeType type) 6 | { 7 | this->type = type; 8 | } 9 | 10 | VfsNodeType VfsNode::getType() const 11 | { 12 | return this->type; 13 | } 14 | 15 | void VfsNode::setName(std::string name) 16 | { 17 | this->name = name; 18 | } 19 | 20 | std::string VfsNode::getName() const 21 | { 22 | return this->name; 23 | } 24 | 25 | VfsDateTime* VfsNode::getLastModifiedTime() 26 | { 27 | return &this->lastModifiedTime; 28 | } 29 | 30 | VfsDateTime* VfsNode::getLastAccessTime() 31 | { 32 | return &this->lastAccessTime; 33 | } 34 | 35 | VfsDateTime* VfsNode::getCreationTime() 36 | { 37 | return &this->creationTime; 38 | } 39 | 40 | void VfsNode::setLastModifiedTime(int day, int month, int year, 41 | int second, int minute, int hour) 42 | { 43 | lastModifiedTime.setDateTime(day, month, year, second, minute, hour); 44 | } 45 | 46 | void VfsNode::setLastAccessTime(int day, int month, int year, 47 | int second, int minute, int hour) 48 | { 49 | lastAccessTime.setDateTime(day, month, year, second, minute, hour); 50 | } 51 | 52 | void VfsNode::setCreationTime(int day, int month, int year, 53 | int second, int minute, int hour) 54 | { 55 | creationTime.setDateTime(day, month, year, second, minute, hour); 56 | } 57 | 58 | void VfsNode::setParent(VfsNode* parent) 59 | { 60 | this->parent = parent; 61 | } 62 | 63 | void VfsNode::addOffset(std::string key, uint64_t value) 64 | { 65 | offsets.insert({{key, value}}); 66 | } 67 | 68 | std::vector VfsNode::getOffsets(std::string& key) 69 | { 70 | std::vector result; 71 | auto range = offsets.equal_range(key); 72 | for (auto it = range.first; it != range.second; it++) 73 | result.push_back(it->second); 74 | return result; 75 | } 76 | 77 | } /* namespace vfs */ -------------------------------------------------------------------------------- /core/vfs/node.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NODE_HPP 2 | #define NODE_HPP 3 | 4 | #include "date_time.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace vfs { 12 | 13 | enum class VfsNodeType 14 | { 15 | DIRECTORY, 16 | FILE 17 | }; 18 | 19 | class VfsNode 20 | { 21 | public: 22 | VfsNode(VfsNodeType type); 23 | virtual ~VfsNode() = default; 24 | 25 | /** 26 | * @brief Get the type of node this is. See VfsNodeType. 27 | * 28 | * @return VfsNodeType 29 | */ 30 | VfsNodeType getType() const; 31 | 32 | /** 33 | * @brief Get the name of this node. 34 | * 35 | * This should represent the file or directory name. 36 | * 37 | * @return std::string 38 | */ 39 | std::string getName() const; 40 | 41 | VfsDateTime* getLastAccessTime(); 42 | 43 | VfsDateTime* getLastModifiedTime(); 44 | 45 | VfsDateTime* getCreationTime(); 46 | 47 | /** 48 | * @brief Set this node's name. 49 | * 50 | * This should be set to the file or directory name. 51 | * 52 | * @param name 53 | */ 54 | void setName(std::string name); 55 | 56 | /** 57 | * @brief Set this node's parent node. 58 | * 59 | * @param parent The parent node. 60 | */ 61 | void setParent(VfsNode* parent); 62 | 63 | /** 64 | * @brief Set the last access time for this node. 65 | */ 66 | void setLastAccessTime(int day = 0, int month = 0, int year = 0, 67 | int second = 0, int minute = 0, int hour = 0); 68 | 69 | /** 70 | * @brief Set the last modified time for this node. 71 | */ 72 | void setLastModifiedTime(int day = 0, int month = 0, int year = 0, 73 | int second = 0, int minute = 0, int hour = 0); 74 | 75 | /** 76 | * @brief Set the creation time for this node. 77 | */ 78 | void setCreationTime(int day = 0, int month = 0, int year = 0, 79 | int second = 0, int minute = 0, int hour = 0); 80 | 81 | /** 82 | * @brief Add an offset associated with this file. 83 | * 84 | * @param key The name or group this offset belongs to. 85 | * @param value The offset. 86 | */ 87 | void addOffset(std::string key, uint64_t value); 88 | 89 | /** 90 | * @brief Get offsets by group name. 91 | * 92 | * @param key Group name. 93 | * @return std::vector Offsets associated with this file. 94 | */ 95 | std::vector getOffsets(std::string& key); 96 | 97 | protected: 98 | VfsNodeType type; 99 | std::string name; 100 | std::vector blockIndexes; 101 | VfsNode* parent; 102 | // TODO: Eventually these will be more dynamic. We can move these to 103 | // some sort of std::map. 104 | VfsDateTime creationTime, 105 | lastModifiedTime, 106 | lastAccessTime; 107 | std::unordered_multimap offsets; 108 | }; 109 | 110 | } /* namespace vfs */ 111 | 112 | #endif /* NODE_HPP */ -------------------------------------------------------------------------------- /decrypt-tool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}/core) 2 | #add_definitions("-Wall" "-g") 3 | 4 | add_executable(decrypt-tool main.cpp) 5 | 6 | target_link_libraries(decrypt-tool core) 7 | 8 | set_target_properties(decrypt-tool PROPERTIES 9 | RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin/Debug" 10 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin/Release" 11 | RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin/RelWithDebInfo" 12 | RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${PROJECT_SOURCE_DIR}/bin/MinSizeRel" 13 | ) -------------------------------------------------------------------------------- /decrypt-tool/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void writePartitionToFile(disk::Partition* partition, std::ofstream& file) 15 | { 16 | const uint32_t kBufSize = 0x200 * 0x800; 17 | std::vector buf(kBufSize); 18 | 19 | auto length = partition->getLength(); 20 | auto dataProvider = partition->getDataProvider(); 21 | 22 | // Make sure we're at the start of the partition 23 | dataProvider->seek(0); 24 | for (uint64_t sector = 0; sector < length; sector += kBufSize) { 25 | auto readLen = dataProvider->read(buf.data(), kBufSize); 26 | file.write(buf.data(), readLen); 27 | } 28 | } 29 | 30 | void listPartitions(disk::Disk* disk) 31 | { 32 | std::cout << std::setw(26) << std::left << "Partition Name" 33 | << std::setw(16) << std::left << "Start" 34 | << std::setw(16) << std::left << "End" 35 | << std::setw(16) << std::left << "Length" 36 | << std::endl; 37 | 38 | for (auto partition : disk->getPartitions()) { 39 | std::cout << std::setw(26) << std::left << partition->getName() 40 | << std::setw(16) << std::left << std::hex << partition->getStart() 41 | << std::setw(16) << std::left << std::hex << partition->getEnd() 42 | << std::setw(16) << std::left << std::hex << partition->getLength() 43 | << std::endl; 44 | } 45 | } 46 | 47 | void buildConfig(disk::DiskConfig* config, std::string& imageFile, std::ifstream& keyFile) 48 | { 49 | keyFile.seekg(0, std::ios::end); 50 | auto keyLen = keyFile.tellg(); 51 | keyFile.seekg(0, std::ios::beg); 52 | 53 | std::vector keyData(keyLen); 54 | keyFile.read(keyData.data(), keyLen); 55 | 56 | config->setKeys(keyData.data(), keyLen); 57 | 58 | auto* stream = io::stream::DiskStreamFactory::getStream(imageFile); 59 | config->setStream(stream); 60 | } 61 | 62 | void printUsage(int argc, char** argv) 63 | { 64 | std::cout << "Usage: " << argv[0] << " [command] [arguments...]" << std::endl 65 | << std::endl 66 | << "Commands:" << std::endl 67 | << " list List available partitions." << std::endl 68 | << " decrypt Decrypt a partition to a new file." << std::endl 69 | << std::endl; 70 | } 71 | 72 | int main(int argc, char** argv) 73 | { 74 | if (argc < 4) { 75 | printUsage(argc, argv); 76 | return 1; 77 | } 78 | 79 | auto handler = new logging::StdOutLogHandler(); 80 | #ifndef NDEBUG 81 | handler->setLogLevel(logging::LogLevel::Debug); 82 | #endif 83 | rAddHandler("stdout", handler); 84 | 85 | std::string command(argv[1]); 86 | std::string input(argv[2]); 87 | 88 | std::ifstream keyFile; 89 | keyFile.open(argv[3], std::ios::binary); 90 | if (!keyFile.is_open()) { 91 | rError(std::string("Failed to open key file: ") + std::string(argv[3])); 92 | return 1; 93 | } 94 | 95 | disk::DiskConfig config; 96 | buildConfig(&config, input, keyFile); 97 | 98 | disk::Disk* disk = formats::DiskFormatFactory::getInstance()->detectFormat(&config); 99 | if (!disk) { 100 | rError( "Could not detect disk format"); 101 | return 1; 102 | } 103 | 104 | if (!disk->getPartitions().size()) { 105 | rError("Could not find any partitions in this disk"); 106 | return 1; 107 | } 108 | 109 | if (command == "list") { 110 | listPartitions(disk); 111 | } 112 | else if (command == "decrypt") { 113 | std::string partitionName(argv[4]); 114 | auto partition = disk->getPartitionByName(partitionName); 115 | if (partition == nullptr) { 116 | rError("No partition named: " + partitionName); 117 | return 1; 118 | } 119 | 120 | std::ofstream outputFile; 121 | outputFile.open(argv[5], std::ios::binary); 122 | 123 | writePartitionToFile(partition, outputFile); 124 | } 125 | 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /file-explorer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | set(CMAKE_AUTOMOC ON) 4 | set(CMAKE_AUTORCC ON) 5 | set(CMAKE_AUTOUIC ON) 6 | 7 | find_package(Qt5 COMPONENTS Core REQUIRED) 8 | find_package(Qt5 COMPONENTS Widgets REQUIRED) 9 | 10 | include_directories(${Qt5Widgets_INCLUDE_DIRS}) 11 | include_directories(${Qt5Core_INCLUDE_DIRS}) 12 | include_directories(${PROJECT_SOURCE_DIR}/core) 13 | 14 | set(SOURCES 15 | main.cpp 16 | mainwindow.cpp 17 | mainwindow.hpp 18 | #QDiskDevice.cpp 19 | #QDiskDevice.hpp 20 | QFileDiskStream.cpp 21 | QFileDiskStream.hpp 22 | #QDiskBuffer.cpp 23 | #QDiskBuffer.hpp 24 | ) 25 | 26 | add_executable(file-explorer ${SOURCES}) 27 | 28 | target_link_libraries(file-explorer 29 | core 30 | Qt5::Widgets 31 | Qt5::Core 32 | Qt5::Gui 33 | ) 34 | 35 | set_target_properties(file-explorer PROPERTIES 36 | RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin/Debug" 37 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin/Release" 38 | RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin/RelWithDebInfo" 39 | RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${PROJECT_SOURCE_DIR}/bin/MinSizeRel" 40 | ) -------------------------------------------------------------------------------- /file-explorer/QFileDiskStream.cpp: -------------------------------------------------------------------------------- 1 | #include "QFileDiskStream.hpp" 2 | #include 3 | 4 | QFileDiskStream::QFileDiskStream(QFile* file) 5 | : file(file) 6 | {} 7 | 8 | uint64_t QFileDiskStream::read(char* data, uint32_t length) 9 | { 10 | //qInfo() << "Reading at: " << this->file->pos() << " of length " << length; 11 | return this->file->read(data, length); 12 | } 13 | 14 | uint64_t QFileDiskStream::seek(int64_t offset, uint32_t whence) 15 | { 16 | // TODO: handle whence 17 | this->file->seek(offset); 18 | return this->file->pos(); 19 | } 20 | 21 | uint64_t QFileDiskStream::tell() 22 | { 23 | return this->file->pos(); 24 | } 25 | 26 | uint64_t QFileDiskStream::getLength() const 27 | { 28 | return this->file->size(); 29 | } -------------------------------------------------------------------------------- /file-explorer/QFileDiskStream.hpp: -------------------------------------------------------------------------------- 1 | #ifndef QDISKSTREAM_HPP 2 | #define QDISKSTREAM_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | /// A DiskStream adapter for QFile 9 | class QFileDiskStream : public io::stream::DiskStream 10 | { 11 | QFile* file; 12 | public: 13 | explicit QFileDiskStream(QFile*); 14 | uint64_t read(char*, uint32_t) override; 15 | uint64_t seek(int64_t, uint32_t) override; 16 | uint64_t tell() override; 17 | uint64_t getLength() const override; 18 | }; 19 | 20 | #endif -------------------------------------------------------------------------------- /file-explorer/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.hpp" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | int main(int argc, char* argv[]) 9 | { 10 | QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 11 | 12 | auto logHandler = new logging::StdOutLogHandler(); 13 | #ifndef NDEBUG 14 | logHandler->setLogLevel(logging::LogLevel::Debug); 15 | #endif 16 | rAddHandler("stdout", logHandler); 17 | 18 | QApplication app(argc, argv); 19 | MainWindow w; 20 | w.show(); 21 | return app.exec(); 22 | } -------------------------------------------------------------------------------- /file-explorer/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.hpp" 2 | #include "QFileDiskStream.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | MainWindow::MainWindow(QWidget* parent) 16 | : QMainWindow(parent) 17 | { 18 | m_treeView = new QTreeView; 19 | m_treeView->setEditTriggers(QAbstractItemView::NoEditTriggers); 20 | 21 | //m_treeView->setModel(m_treeViewModel); 22 | 23 | setCentralWidget(m_treeView); 24 | 25 | addToolBar("File"); 26 | QMenu* fileMenu = menuBar()->addMenu("&File"); 27 | fileMenu->addAction("Open..", this, SLOT(slotOpenFile())); 28 | } 29 | 30 | void MainWindow::slotOpenFile() 31 | { 32 | auto fileName = QFileDialog::getOpenFileName(this, tr("Open Image File")); 33 | if (fileName.isNull()) 34 | return; 35 | 36 | auto keyFileName = QFileDialog::getOpenFileName(this, tr("Open Key File")); 37 | if (keyFileName.isNull()) 38 | return; 39 | 40 | QFile* imageFile = new QFile(fileName); 41 | imageFile->open(QIODevice::ReadOnly); 42 | 43 | QFile* keyFile = new QFile(keyFileName); 44 | keyFile->open(QIODevice::ReadOnly); 45 | 46 | populateExplorer(imageFile, keyFile); 47 | } 48 | 49 | void MainWindow::populateExplorer(QFile* imageFile, QFile* keyFile) 50 | { 51 | QFileDiskStream* stream = new QFileDiskStream(imageFile); 52 | 53 | disk::DiskConfig config; 54 | config.setStream(stream); 55 | config.setKeys(keyFile->readAll().data(), keyFile->size()); 56 | 57 | m_disk = formats::DiskFormatFactory::getInstance()->detectFormat(&config); 58 | if (!m_disk) { 59 | QMessageBox::warning(this, tr("Cannot load disk image"), 60 | tr("Disk format not recognized.")); 61 | return; 62 | } 63 | 64 | m_treeViewModel = new QStandardItemModel; 65 | m_treeViewModel->setHorizontalHeaderLabels(QStringList() << "File Name" 66 | << "File Size" << "Date Created" << "Date Modified" << "Date Accessed"); 67 | 68 | for (auto partition : m_disk->getPartitions()) { 69 | partition->mount(); 70 | 71 | auto vfs = partition->getVfs(); 72 | auto root = vfs->getRoot(); 73 | 74 | QStandardItem* item = new QStandardItem(partition->getName().c_str()); 75 | item->setIcon(QApplication::style()->standardIcon(QStyle::SP_DriveHDIcon)); 76 | m_treeViewModel->appendRow(item); 77 | 78 | populateTreeView(item, root); 79 | } 80 | 81 | m_treeView->setModel(m_treeViewModel); 82 | } 83 | 84 | void MainWindow::populateTreeView(QStandardItem* parent, vfs::VfsDirectory* root) 85 | { 86 | for (auto node : root->getChildren()) { 87 | // Add the node 88 | QIcon icon = node->getType() == vfs::VfsNodeType::DIRECTORY ? 89 | QApplication::style()->standardIcon(QStyle::SP_DirIcon) : 90 | QApplication::style()->standardIcon(QStyle::SP_FileIcon); 91 | 92 | QList row; 93 | auto item = new QStandardItem(icon, node->getName().c_str()); 94 | row.append(item); 95 | 96 | auto fileSizeCell = new QStandardItem(""); 97 | if (node->getType() == vfs::VfsNodeType::FILE) { 98 | fileSizeCell->setText( 99 | QString::number((ulong)static_cast(node)->getFileSize()) 100 | ); 101 | } 102 | 103 | row.append(fileSizeCell); 104 | row.append(new QStandardItem( 105 | QString("%1/%2/%3 %4:%5:%6") 106 | .arg(node->getCreationTime()->getMonth()) 107 | .arg(node->getCreationTime()->getDay()) 108 | .arg(node->getCreationTime()->getYear()) 109 | .arg(node->getCreationTime()->getHour(), 2, 10, QLatin1Char('0')) 110 | .arg(node->getCreationTime()->getMinute(), 2, 10, QLatin1Char('0')) 111 | .arg(node->getCreationTime()->getSecond(), 2, 10, QLatin1Char('0')) 112 | )); 113 | 114 | row.append(new QStandardItem( 115 | QString("%1/%2/%3 %4:%5:%6") 116 | .arg(node->getLastModifiedTime()->getMonth()) 117 | .arg(node->getLastModifiedTime()->getDay()) 118 | .arg(node->getLastModifiedTime()->getYear()) 119 | .arg(node->getLastModifiedTime()->getHour(), 2, 10, QLatin1Char('0')) 120 | .arg(node->getLastModifiedTime()->getMinute(), 2, 10, QLatin1Char('0')) 121 | .arg(node->getLastModifiedTime()->getSecond(), 2, 10, QLatin1Char('0')) 122 | )); 123 | 124 | row.append(new QStandardItem( 125 | QString("%1/%2/%3 %4:%5:%6") 126 | .arg(node->getLastAccessTime()->getMonth()) 127 | .arg(node->getLastAccessTime()->getDay()) 128 | .arg(node->getLastAccessTime()->getYear()) 129 | .arg(node->getLastAccessTime()->getHour(), 2, 10, QLatin1Char('0')) 130 | .arg(node->getLastAccessTime()->getMinute(), 2, 10, QLatin1Char('0')) 131 | .arg(node->getLastAccessTime()->getSecond(), 2, 10, QLatin1Char('0')) 132 | )); 133 | 134 | parent->appendRow(row); 135 | 136 | if (node->getType() == vfs::VfsNodeType::DIRECTORY) { 137 | populateTreeView(item, static_cast(node)); 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /file-explorer/mainwindow.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class MainWindow : public QMainWindow 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | MainWindow(QWidget* parent = Q_NULLPTR); 18 | 19 | private: 20 | void populateExplorer(QFile* imageFile, QFile* keyFile); 21 | void populateTreeView(QStandardItem* parent, vfs::VfsDirectory* root); 22 | 23 | QTreeView* m_treeView; 24 | QStandardItemModel* m_treeViewModel; 25 | 26 | disk::Disk* m_disk; 27 | disk::Partition* m_partition; 28 | 29 | private slots: 30 | void slotOpenFile(); 31 | }; -------------------------------------------------------------------------------- /hex-viewer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | set(CMAKE_AUTOMOC ON) 4 | set(CMAKE_AUTORCC ON) 5 | set(CMAKE_AUTOUIC ON) 6 | 7 | find_package(Qt5 COMPONENTS Core REQUIRED) 8 | find_package(Qt5 COMPONENTS Widgets REQUIRED) 9 | 10 | include_directories(${Qt5Widgets_INCLUDE_DIRS}) 11 | include_directories(${Qt5Core_INCLUDE_DIRS}) 12 | include_directories(${PROJECT_SOURCE_DIR}/QHexView) 13 | include_directories(${PROJECT_SOURCE_DIR}/core) 14 | 15 | FetchContent_Declare( 16 | QHexView 17 | SOURCE_DIR ${PROJECT_SOURCE_DIR}/QHexView 18 | GIT_REPOSITORY https://github.com/Dax89/QHexView.git 19 | GIT_TAG master 20 | ) 21 | 22 | FetchContent_GetProperties(QHexView) 23 | FetchContent_MakeAvailable(QHexView) 24 | 25 | # For legacy QHexView 26 | #add_library(QHexView 27 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexedit.cpp 28 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexedit.h 29 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditcomments.cpp 30 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditcomments.h 31 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditdata.cpp 32 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditdata.h 33 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditdatadevice.cpp 34 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditdatadevice.h 35 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditdatareader.cpp 36 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditdatareader.h 37 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditdatawriter.cpp 38 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditdatawriter.h 39 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexedithighlighter.cpp 40 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexedithighlighter.h 41 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditprivate.cpp 42 | # ${PROJECT_SOURCE_DIR}/QHexView/qhexeditprivate.h 43 | # ${PROJECT_SOURCE_DIR}/QHexView/sparserangemap.cpp 44 | # ${PROJECT_SOURCE_DIR}/QHexView/sparserangemap.h 45 | #) 46 | 47 | set(SOURCES 48 | main.cpp 49 | mainwindow.cpp 50 | mainwindow.hpp 51 | hexview.cpp 52 | hexview.hpp 53 | selectdialog.cpp 54 | selectdialog.hpp 55 | searchdialog.cpp 56 | searchdialog.hpp 57 | searchthread.cpp 58 | searchthread.hpp 59 | QDiskDevice.cpp 60 | QDiskDevice.hpp 61 | QFileDiskStream.cpp 62 | QFileDiskStream.hpp 63 | QDiskBuffer.cpp 64 | QDiskBuffer.hpp 65 | ) 66 | 67 | add_executable(hex-viewer ${SOURCES}) 68 | 69 | target_link_libraries(hex-viewer 70 | core 71 | qhexview-lib 72 | Qt5::Widgets 73 | Qt5::Core 74 | Qt5::Gui 75 | ) 76 | 77 | set_target_properties(hex-viewer PROPERTIES 78 | RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin/Debug" 79 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin/Release" 80 | RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin/RelWithDebInfo" 81 | RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${PROJECT_SOURCE_DIR}/bin/MinSizeRel" 82 | ) -------------------------------------------------------------------------------- /hex-viewer/QDiskBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "QDiskBuffer.hpp" 2 | 3 | #include 4 | 5 | QDiskBuffer::QDiskBuffer(QObject* parent) : QHexBuffer(parent) { } 6 | 7 | QDiskBuffer::~QDiskBuffer() 8 | {} 9 | 10 | uchar QDiskBuffer::at(qint64 idx) 11 | { 12 | dataProvider->seek(idx); 13 | char c; 14 | bool res = dataProvider->read(&c, 1); 15 | return static_cast(c); 16 | } 17 | 18 | qint64 QDiskBuffer::length() const 19 | { 20 | return dataProvider->size(); 21 | } 22 | 23 | void QDiskBuffer::insert(qint64 offset, const QByteArray& data) 24 | { 25 | // Not implemented 26 | } 27 | 28 | void QDiskBuffer::remove(qint64 offset, int length) 29 | { 30 | // Not implemented 31 | } 32 | 33 | QByteArray QDiskBuffer::read(qint64 offset, int length) 34 | { 35 | QByteArray* byteArray = new QByteArray; 36 | byteArray->resize(length); 37 | dataProvider->seek(offset); 38 | dataProvider->readData(byteArray->data(), length); 39 | return *byteArray; 40 | } 41 | 42 | bool QDiskBuffer::read(QIODevice* device) 43 | { 44 | this->dataProvider = static_cast(device); 45 | // Not implemented 46 | return true; 47 | } 48 | 49 | void QDiskBuffer::write(QIODevice* device) 50 | { 51 | // Not implemented 52 | } 53 | 54 | qint64 QDiskBuffer::indexOf(const QByteArray& ba, qint64 from) 55 | { 56 | return 0; 57 | } 58 | 59 | qint64 QDiskBuffer::lastIndexOf(const QByteArray& ba, qint64 from) 60 | { 61 | return 0; 62 | } -------------------------------------------------------------------------------- /hex-viewer/QDiskBuffer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef QDISKBUFFER_HPP 2 | #define QDISKBUFFER_HPP 3 | 4 | #include 5 | 6 | #include "QDiskDevice.hpp" 7 | 8 | #include 9 | 10 | class QDiskBuffer : public QHexBuffer 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit QDiskBuffer(QObject* parent = nullptr); 16 | ~QDiskBuffer(); 17 | 18 | uchar at(qint64 idx) override; 19 | qint64 length() const override; 20 | void insert(qint64 offset, const QByteArray& data) override; 21 | void remove(qint64 offset, int length) override; 22 | QByteArray read(qint64 offset, int length) override; 23 | bool read(QIODevice* device) override; 24 | void write(QIODevice* device) override; 25 | 26 | qint64 indexOf(const QByteArray& ba, qint64 from) override; 27 | qint64 lastIndexOf(const QByteArray& ba, qint64 from) override; 28 | 29 | private: 30 | QDiskDevice* dataProvider; 31 | }; 32 | 33 | #endif -------------------------------------------------------------------------------- /hex-viewer/QDiskDevice.cpp: -------------------------------------------------------------------------------- 1 | #include "QDiskDevice.hpp" 2 | 3 | QDiskDevice::QDiskDevice(io::data::DataProvider* dataProvider) 4 | { 5 | this->dataProvider = dataProvider; 6 | } 7 | 8 | qint64 QDiskDevice::readData(char* data, qint64 maxSize) 9 | { 10 | // TODO: fix me 11 | //return this->dataProvider->read(data, maxSize); 12 | this->dataProvider->read(data, maxSize); 13 | return maxSize; 14 | } 15 | 16 | qint64 QDiskDevice::writeData(const char* data, qint64 maxSize) 17 | { 18 | return 0; 19 | } 20 | 21 | bool QDiskDevice::seek(qint64 pos) 22 | { 23 | this->dataProvider->seek(pos); 24 | return this->dataProvider->tell() == pos; 25 | } 26 | 27 | qint64 QDiskDevice::size() const 28 | { 29 | return this->dataProvider->getLength(); 30 | } -------------------------------------------------------------------------------- /hex-viewer/QDiskDevice.hpp: -------------------------------------------------------------------------------- 1 | #ifndef QDISKDEVICE_HPP 2 | #define QDISKDEVICE_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | /// A QIODevice adapter for DataProvider 9 | class QDiskDevice : public QIODevice 10 | { 11 | io::data::DataProvider* dataProvider; 12 | public: 13 | explicit QDiskDevice(io::data::DataProvider*); 14 | qint64 readData(char* data, qint64 maxSize) override; 15 | qint64 writeData(const char* data, qint64 maxSize) override; 16 | bool seek(qint64 pos) override; 17 | qint64 size() const override; 18 | }; 19 | 20 | #endif -------------------------------------------------------------------------------- /hex-viewer/QFileDiskStream.cpp: -------------------------------------------------------------------------------- 1 | #include "QFileDiskStream.hpp" 2 | #include 3 | 4 | QFileDiskStream::QFileDiskStream(QFile* file) 5 | : file(file) 6 | {} 7 | 8 | uint64_t QFileDiskStream::read(char* data, uint32_t length) 9 | { 10 | //qInfo() << "Reading at: " << this->file->pos() << " of length " << length; 11 | return this->file->read(data, length); 12 | } 13 | 14 | uint64_t QFileDiskStream::seek(int64_t offset, uint32_t whence) 15 | { 16 | // TODO: handle whence 17 | this->file->seek(offset); 18 | return this->file->pos(); 19 | } 20 | 21 | uint64_t QFileDiskStream::tell() 22 | { 23 | return this->file->pos(); 24 | } 25 | 26 | uint64_t QFileDiskStream::getLength() const 27 | { 28 | return this->file->size(); 29 | } -------------------------------------------------------------------------------- /hex-viewer/QFileDiskStream.hpp: -------------------------------------------------------------------------------- 1 | #ifndef QDISKSTREAM_HPP 2 | #define QDISKSTREAM_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | /// A DiskStream adapter for QFile 9 | class QFileDiskStream : public io::stream::DiskStream 10 | { 11 | QFile* file; 12 | public: 13 | explicit QFileDiskStream(QFile*); 14 | uint64_t read(char*, uint32_t) override; 15 | uint64_t seek(int64_t, uint32_t) override; 16 | uint64_t tell() override; 17 | uint64_t getLength() const override; 18 | }; 19 | 20 | #endif -------------------------------------------------------------------------------- /hex-viewer/hexview.cpp: -------------------------------------------------------------------------------- 1 | #include "hexview.hpp" 2 | #include "selectdialog.hpp" 3 | #include "QDiskDevice.hpp" 4 | #include "QDiskBuffer.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | HexView::HexView(disk::Partition* partition, QWidget* parent) 12 | : m_partition(partition), QWidget(parent) 13 | { 14 | m_searchDialog = nullptr; 15 | 16 | m_hexView = new QHexView(); 17 | m_hexView->setReadOnly(true); 18 | 19 | QDiskDevice* device = new QDiskDevice(m_partition->getDataProvider()); 20 | device->open(QIODevice::ReadOnly); 21 | QHexDocument* hexEditData = QHexDocument::fromDevice(device); 22 | m_hexView->setDocument(hexEditData); 23 | 24 | m_hexCursor = m_hexView->document()->cursor(); 25 | 26 | QVBoxLayout* mainLayout = new QVBoxLayout; 27 | mainLayout->addWidget(m_hexView); 28 | setLayout(mainLayout); 29 | 30 | // Add go-to action 31 | QAction* gotoAction = new QAction(this); 32 | gotoAction->setShortcut(Qt::CTRL | Qt::Key_G); 33 | connect(gotoAction, SIGNAL(triggered()), this, SLOT(slotGotoOffset())); 34 | this->addAction(gotoAction); 35 | 36 | // Add copy action 37 | // TODO: For some reason QHexView doesn't allow copying when in read-only mode 38 | QAction* copyAction = new QAction(this); 39 | copyAction->setShortcut(Qt::CTRL | Qt::Key_C); 40 | connect(copyAction, SIGNAL(triggered()), this, SLOT(slotCopyData())); 41 | this->addAction(copyAction); 42 | 43 | // Add find action 44 | QAction* findAction = new QAction(this); 45 | findAction->setShortcut(Qt::CTRL | Qt::Key_F); 46 | connect(findAction, SIGNAL(triggered()), this, SLOT(slotFindData())); 47 | this->addAction(findAction); 48 | 49 | QAction* selectAction = new QAction(this); 50 | selectAction->setShortcut(Qt::CTRL | Qt::Key_S); 51 | connect(selectAction, SIGNAL(triggered()), this, SLOT(slotSelectData())); 52 | this->addAction(selectAction); 53 | 54 | // Selection changed update 55 | connect(m_hexCursor, SIGNAL(positionChanged()), this, SLOT(positionChanged())); 56 | } 57 | 58 | void HexView::positionChanged() 59 | { 60 | emit positionUpdate(); 61 | } 62 | 63 | void HexView::slotSelectData() 64 | { 65 | auto dialog = new SelectDialog(m_hexCursor); 66 | dialog->exec(); 67 | } 68 | 69 | void HexView::slotGotoOffset() 70 | { 71 | bool ok; 72 | QString offsetText = QInputDialog::getText(this, tr("Go to offset"), tr("Enter Offset"), 73 | QLineEdit::Normal, QString("0x%1").arg(m_hexCursor->position().offset(), 0, 16), &ok); 74 | if (ok && !offsetText.isEmpty()) { 75 | quint64 offset; 76 | if (offsetText.startsWith("0x")) 77 | offset = offsetText.toULongLong(&ok, 16); 78 | else 79 | offset = offsetText.toULongLong(&ok, 10); 80 | if (ok) { 81 | auto cursor = m_hexView->document()->cursor(); 82 | cursor->moveTo(offset); 83 | } 84 | } 85 | } 86 | 87 | void HexView::slotCopyData() 88 | { 89 | auto document = m_hexView->document(); 90 | if (!document->cursor()->hasSelection()) 91 | return; 92 | auto bytes = document->selectedBytes().toHex(' ').toUpper(); 93 | qApp->clipboard()->setText(bytes); 94 | } 95 | 96 | void HexView::slotFindData() 97 | { 98 | // Make sure a partition is loaded 99 | if (!m_partition) 100 | return; 101 | // Check if the search dialog is already open 102 | if (m_searchDialog && m_searchDialog->isVisible()) { 103 | m_searchDialog->activateWindow(); 104 | return; 105 | } 106 | m_searchDialog = new SearchDialog(m_partition->getDataProvider(), 107 | m_hexView->document()->cursor()); 108 | m_searchDialog->exec(); 109 | m_searchDialog = nullptr; 110 | } 111 | -------------------------------------------------------------------------------- /hex-viewer/hexview.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HEXVIEW_HPP 2 | #define HEXVIEW_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include "searchdialog.hpp" 11 | 12 | class HexView : public QWidget 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit HexView(disk::Partition* partition, QWidget* parent = nullptr); 18 | 19 | QHexView* getHexView() { return m_hexView; } 20 | 21 | QHexView* m_hexView; 22 | QHexCursor* m_hexCursor; 23 | disk::Partition* m_partition; 24 | 25 | bool m_searchDialogOpen; 26 | SearchDialog* m_searchDialog; 27 | 28 | signals: 29 | void positionUpdate(); 30 | 31 | private slots: 32 | void positionChanged(); 33 | void slotSelectData(); 34 | void slotGotoOffset(); 35 | void slotCopyData(); 36 | void slotFindData(); 37 | }; 38 | 39 | #endif /* HEXVIEW_HPP */ 40 | -------------------------------------------------------------------------------- /hex-viewer/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.hpp" 2 | #include 3 | 4 | int main(int argc, char* argv[]) 5 | { 6 | QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 7 | 8 | QApplication app(argc, argv); 9 | MainWindow w; 10 | w.show(); 11 | return app.exec(); 12 | } -------------------------------------------------------------------------------- /hex-viewer/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.hpp" 2 | #include "hexview.hpp" 3 | #include "QFileDiskStream.hpp" 4 | #include "document/qhexcursor.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | MainWindow::MainWindow(QWidget* parent) 17 | : QMainWindow(parent) 18 | { 19 | m_tabWidget = new QTabWidget; 20 | 21 | m_imageFile = nullptr; 22 | m_disk = nullptr; 23 | 24 | m_offsetLabel = new QLabel(this); 25 | m_selectedRangeLabel = new QLabel(this); 26 | m_selectedLengthLabel = new QLabel(this); 27 | 28 | statusBar()->setLayoutDirection(Qt::LayoutDirection::RightToLeft); 29 | statusBar()->addPermanentWidget(m_selectedLengthLabel, 1); 30 | statusBar()->addPermanentWidget(m_selectedRangeLabel, 1); 31 | statusBar()->addPermanentWidget(m_offsetLabel, 1); 32 | 33 | setCentralWidget(m_tabWidget); 34 | 35 | addToolBar("File"); 36 | QMenu* fileMenu = menuBar()->addMenu("&File"); 37 | fileMenu->addAction("Open..", this, SLOT(slotOpenFile())); 38 | 39 | connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(updateStatusBar())); 40 | } 41 | 42 | void MainWindow::updateStatusBar() 43 | { 44 | auto index = m_tabWidget->currentIndex(); 45 | HexView* widget = qobject_cast(m_tabWidget->widget(index)); 46 | QHexView* hexView = widget->getHexView(); 47 | 48 | auto* cursor = hexView->document()->cursor(); 49 | auto offset = cursor->position().offset(); 50 | auto start = cursor->selectionStart().offset(); 51 | auto end = cursor->selectionEnd().offset(); 52 | auto length = cursor->selectionLength(); 53 | 54 | m_offsetLabel->setText(QString("Current Offset: %1").arg(QString("%1").arg(offset, 0, 16).toUpper())); 55 | if (length) { 56 | m_selectedRangeLabel->setText(QString("Range: %1-%2").arg( 57 | QString("%1").arg(start, 0, 16).toUpper(), 58 | QString("%1").arg(end, 0, 16).toUpper() 59 | )); 60 | m_selectedLengthLabel->setText(QString("Length: %1").arg( 61 | QString("%1").arg(length, 0, 16).toUpper())); 62 | } 63 | else { 64 | m_selectedRangeLabel->setText(""); 65 | m_selectedLengthLabel->setText(""); 66 | } 67 | } 68 | 69 | void MainWindow::slotOpenFile() 70 | { 71 | auto fileName = QFileDialog::getOpenFileName(this, tr("Open Image File")); 72 | if (fileName.isNull()) 73 | return; 74 | 75 | auto keyFileName = QFileDialog::getOpenFileName(this, tr("Open Key File")); 76 | if (keyFileName.isNull()) 77 | return; 78 | 79 | if (m_imageFile && m_imageFile->isOpen()) 80 | m_imageFile->close(); 81 | m_imageFile = new QFile(fileName); 82 | 83 | if (m_disk) 84 | delete m_disk; 85 | 86 | QFile* keyFile = new QFile(keyFileName); 87 | m_imageFile->open(QIODevice::ReadOnly); 88 | QFileDiskStream* stream = new QFileDiskStream(m_imageFile); 89 | 90 | keyFile->open(QIODevice::ReadOnly); 91 | auto keyLen = keyFile->size(); 92 | 93 | disk::DiskConfig config; 94 | config.setStream(stream); 95 | config.setKeys(keyFile->readAll().data(), keyLen); 96 | keyFile->close(); 97 | 98 | m_disk = formats::DiskFormatFactory::getInstance()->detectFormat(&config); 99 | if (!m_disk) { 100 | QMessageBox::warning(this, tr("Cannot load disk"), 101 | tr("Disk format not recognized.")); 102 | return; 103 | } 104 | 105 | if (!m_disk->getPartitions().size()) { 106 | QMessageBox::warning(this, tr("Cannot load disk"), 107 | tr("No partitions were found.")); 108 | return; 109 | } 110 | 111 | m_tabWidget->clear(); 112 | for (auto partition : m_disk->getPartitions()) { 113 | auto hexView = new HexView(partition); 114 | m_tabWidget->addTab(hexView, tr(partition->getName().c_str())); 115 | connect(hexView, SIGNAL(positionUpdate()), this, SLOT(updateStatusBar())); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /hex-viewer/mainwindow.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_HPP 2 | #define MAINWINDOW_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include "searchdialog.hpp" 12 | 13 | class MainWindow : public QMainWindow 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | MainWindow(QWidget* parent = Q_NULLPTR); 19 | 20 | private: 21 | QTabWidget* m_tabWidget; 22 | QFile* m_imageFile; 23 | disk::Disk* m_disk; 24 | QLabel* m_offsetLabel; 25 | QLabel* m_selectedRangeLabel; 26 | QLabel* m_selectedLengthLabel; 27 | 28 | private slots: 29 | void updateStatusBar(); 30 | void slotOpenFile(); 31 | }; 32 | 33 | #endif /* MAINWINDOW_HPP */ -------------------------------------------------------------------------------- /hex-viewer/searchdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "searchdialog.hpp" 2 | #include "searchthread.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | SearchDialog::SearchDialog(io::data::DataProvider* dataProvider, 12 | QHexCursor* hexCursor, QWidget* parent) : QDialog(parent) 13 | { 14 | m_dataProvider = dataProvider; 15 | m_hexCursor = hexCursor; 16 | m_progressDialog = nullptr; 17 | 18 | m_layout = new QGridLayout; 19 | m_lineEdit = new QLineEdit; 20 | //m_comboBox = new QComboBox; 21 | m_findButton = new QPushButton("&Find", this); 22 | m_cancelButton = new QPushButton("&Cancel", this); 23 | 24 | m_lineEdit->setPlaceholderText("Hex string"); 25 | 26 | m_layout->addWidget(m_lineEdit, 0,0,1,3); 27 | //m_layout->addWidget(m_comboBox, 1,0,1,3); 28 | m_layout->addWidget(m_findButton, 2,1,1,1); 29 | m_layout->addWidget(m_cancelButton, 2,2,1,1); 30 | 31 | setLayout(m_layout); 32 | setFixedSize(this->sizeHint()); 33 | setWindowTitle(tr("Find Bytes")); 34 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 35 | setAttribute(Qt::WA_DeleteOnClose); 36 | 37 | m_lineEdit->setFocus(); 38 | 39 | connect(m_findButton, SIGNAL(clicked()), this, SLOT(slotFind())); 40 | connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(slotCancel())); 41 | } 42 | 43 | void SearchDialog::slotCancel() 44 | { 45 | this->reject(); 46 | } 47 | 48 | void SearchDialog::setProgress(quint64 pos) 49 | { 50 | auto value = ((double)pos / (double)m_dataProvider->getLength()) * INT_MAX; 51 | // QString label; 52 | // label.sprintf("%llu / %llu", pos, m_dataProvider->getLength()); 53 | // m_progressDialog->setLabelText(label); 54 | m_progressDialog->setValue(value); 55 | } 56 | 57 | void SearchDialog::slotFind() 58 | { 59 | auto text = m_lineEdit->text(); 60 | QRegularExpression hexMatcher( 61 | "^([0-9A-F]{2}\\s*)+$", 62 | QRegularExpression::CaseInsensitiveOption 63 | ); 64 | QRegularExpressionMatch match = hexMatcher.match(text); 65 | if (!match.hasMatch()) { 66 | QMessageBox::warning(this, tr("Error"), 67 | tr("The pattern has an invalid hex string.")); 68 | return; 69 | } 70 | 71 | auto bytes = QByteArray::fromHex(text.toUtf8()); 72 | 73 | if (!m_progressDialog) 74 | m_progressDialog = new QProgressDialog(this); 75 | m_progressDialog->setWindowTitle("Finding bytes"); 76 | m_progressDialog->setWindowModality(Qt::WindowModal); 77 | auto currentValue = ((double)m_hexCursor->position().offset() / (double)m_dataProvider->getLength()) * INT_MAX; 78 | m_progressDialog->setValue(currentValue); 79 | m_progressDialog->setMinimum(0); 80 | m_progressDialog->setMaximum(INT_MAX); // Maximum resolution 81 | m_progressDialog->setLabelText("Searching..."); 82 | 83 | 84 | SearchThread* thread = new SearchThread(bytes, m_dataProvider, m_hexCursor->position().offset(), this); 85 | connect(thread, SIGNAL(progress(quint64)), this, SLOT(setProgress(quint64))); 86 | connect(thread, SIGNAL(onFound(quint64,quint32)), this, SLOT(onSearchCompleted(quint64, quint32))); 87 | connect(m_progressDialog, SIGNAL(canceled()), thread, SLOT(cancel())); 88 | m_progressDialog->show(); 89 | thread->start(); 90 | } 91 | 92 | void SearchDialog::onSearchCompleted(quint64 offset, quint32 size) 93 | { 94 | m_hexCursor->moveTo(offset); 95 | m_hexCursor->select(size); 96 | m_progressDialog->close(); 97 | auto parent = parentWidget(); 98 | if (parent) 99 | parent->activateWindow(); 100 | } -------------------------------------------------------------------------------- /hex-viewer/searchdialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SEARCHDIALOG_HPP 2 | #define SEARCHDIALOG_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | class SearchDialog : public QDialog 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit SearchDialog(io::data::DataProvider* dataProvider, 21 | QHexCursor* hexCursor, QWidget* parent = nullptr); 22 | 23 | private: 24 | QGridLayout* m_layout; 25 | QLineEdit* m_lineEdit; 26 | QComboBox* m_comboBox; 27 | QPushButton* m_findButton; 28 | QPushButton* m_cancelButton; 29 | 30 | io::data::DataProvider* m_dataProvider; 31 | QHexCursor* m_hexCursor; 32 | 33 | QProgressDialog* m_progressDialog; 34 | 35 | private slots: 36 | void slotCancel(); 37 | void slotFind(); 38 | void setProgress(quint64); 39 | void onSearchCompleted(quint64, quint32); 40 | }; 41 | 42 | #endif -------------------------------------------------------------------------------- /hex-viewer/searchthread.cpp: -------------------------------------------------------------------------------- 1 | #include "searchthread.hpp" 2 | 3 | #include 4 | 5 | SearchThread::SearchThread(QByteArray needle, io::data::DataProvider* hayStack, 6 | quint64 from, QObject* parent) : QThread(parent) 7 | { 8 | this->m_needle = needle; 9 | this->m_hayStack = hayStack; 10 | this->m_from = from; 11 | } 12 | 13 | void SearchThread::run() 14 | { 15 | const quint32 kBufSize = 0x400000; 16 | quint64 pos = static_cast(m_from) + 1; 17 | QByteArray buffer(kBufSize, 0); 18 | 19 | qDebug() << m_needle.toHex(); 20 | for (; pos < m_hayStack->getLength(); 21 | pos += (kBufSize - m_needle.size())) { 22 | qDebug() << hex << pos; 23 | if (m_canceled) 24 | return; 25 | emit progress(pos); 26 | m_hayStack->seek(pos); 27 | m_hayStack->read(buffer.data(), kBufSize); 28 | auto idx = buffer.indexOf(m_needle); 29 | if (idx != -1) { 30 | emit onFound(pos + idx, m_needle.size()); 31 | return; 32 | } 33 | } 34 | } 35 | 36 | void SearchThread::cancel() 37 | { 38 | this->m_canceled = true; 39 | } -------------------------------------------------------------------------------- /hex-viewer/searchthread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SEARCHTHREAD_HPP 2 | #define SEARCHTHREAD_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | class SearchThread : public QThread 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | SearchThread(QByteArray needle, 16 | io::data::DataProvider* hayStack, quint64 from, QObject* parent = nullptr); 17 | 18 | private: 19 | void run() override; 20 | 21 | public slots: 22 | void cancel(); 23 | 24 | signals: 25 | void progress(quint64 value); 26 | void onFound(quint64 offset, quint32 size); 27 | 28 | private: 29 | bool m_canceled = false; 30 | quint64 m_from; 31 | QByteArray m_needle; 32 | io::data::DataProvider* m_hayStack; 33 | }; 34 | 35 | #endif -------------------------------------------------------------------------------- /hex-viewer/selectdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "selectdialog.hpp" 2 | 3 | #include 4 | #include 5 | 6 | SelectDialog::SelectDialog(QHexCursor* hexCursor, QWidget* parent) 7 | : QDialog(parent) 8 | { 9 | m_hexCursor = hexCursor; 10 | 11 | auto start = m_hexCursor->selectionStart().offset(); 12 | auto end = m_hexCursor->selectionEnd().offset(); 13 | auto length = m_hexCursor->selectionLength(); 14 | 15 | QVBoxLayout* main = new QVBoxLayout; 16 | QDialogButtonBox* buttonBox = new QDialogButtonBox; 17 | m_formLayout = new QFormLayout; 18 | 19 | m_startText = new QLineEdit; 20 | m_endText = new QLineEdit; 21 | m_lengthText = new QLineEdit; 22 | 23 | m_startText->setText(QString("0x%1").arg(start, 0, 16)); 24 | m_endText->setText(QString("0x%1").arg(end, 0, 16)); 25 | m_lengthText->setText(QString("0x%1").arg(length, 0, 16)); 26 | 27 | m_formLayout->addRow(tr("&Start:"), m_startText); 28 | m_formLayout->addRow(tr("&End:"), m_endText); 29 | m_formLayout->addRow(tr("&Length:"), m_lengthText); 30 | 31 | buttonBox->addButton(tr("Ok"), QDialogButtonBox::AcceptRole); 32 | buttonBox->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); 33 | 34 | main->addLayout(m_formLayout); 35 | main->addWidget(buttonBox); 36 | 37 | connect(buttonBox, SIGNAL(accepted()), this, SLOT(selectBytes())); 38 | connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); 39 | 40 | setLayout(main); 41 | setFixedSize(this->sizeHint()); 42 | setWindowTitle(tr("Select Bytes")); 43 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 44 | setAttribute(Qt::WA_DeleteOnClose); 45 | 46 | connect(m_startText, &QLineEdit::textChanged, [this]() { 47 | // length = end - start 48 | if (m_endText->text().size()) { 49 | bool ok; 50 | quint64 start, end; 51 | if (m_startText->text().startsWith("0x")) 52 | start = m_startText->text().mid(2).toULongLong(&ok, 16); 53 | else 54 | start = m_startText->text().toULongLong(&ok); 55 | if (!ok) 56 | return; 57 | if (m_endText->text().startsWith("0x")) 58 | end = m_endText->text().mid(2).toULongLong(&ok,16); 59 | else 60 | end = m_endText->text().toULongLong(&ok); 61 | if (!ok) 62 | return; 63 | if (start > end) 64 | return; 65 | auto length = (end - start) + 1; 66 | m_lengthText->setText(QString("0x%1").arg(length, 0, 16)); 67 | } 68 | }); 69 | 70 | connect(m_endText, &QLineEdit::textEdited, [this]() { 71 | // length = end - start 72 | if (m_startText->text().size()) { 73 | bool ok; 74 | quint64 start, end; 75 | if (m_startText->text().startsWith("0x")) 76 | start = m_startText->text().mid(2).toULongLong(&ok, 16); 77 | else 78 | start = m_startText->text().toULongLong(&ok); 79 | if (!ok) 80 | return; 81 | if (m_endText->text().startsWith("0x")) 82 | end = m_endText->text().mid(2).toULongLong(&ok,16); 83 | else 84 | end = m_endText->text().toULongLong(&ok); 85 | if (!ok) 86 | return; 87 | if (start > end) 88 | return; 89 | auto length = (end - start) + 1; 90 | m_lengthText->setText(QString("0x%1").arg(length, 0, 16)); 91 | } 92 | }); 93 | 94 | connect(m_lengthText, &QLineEdit::textEdited, [this]() { 95 | // end = start + length 96 | if (!m_lengthText->text().isEmpty() && !m_startText->text().isEmpty()) { 97 | bool ok; 98 | quint64 start, length; 99 | if (m_startText->text().startsWith("0x")) 100 | start = m_startText->text().mid(2).toULongLong(&ok, 16); 101 | else 102 | start = m_startText->text().toULongLong(&ok); 103 | if (!ok) 104 | return; 105 | if (m_lengthText->text().startsWith("0x")) 106 | length = m_lengthText->text().mid(2).toULongLong(&ok,16); 107 | else 108 | length = m_lengthText->text().toULongLong(&ok); 109 | if (!ok) 110 | return; 111 | if (length == 0) 112 | return; 113 | auto end = (start + length) - 1; 114 | m_endText->setText(QString("0x%1").arg(end, 0, 16)); 115 | } 116 | }); 117 | } 118 | 119 | void SelectDialog::slotCancel() 120 | { 121 | // Unused 122 | } 123 | 124 | void SelectDialog::selectBytes() 125 | { 126 | bool ok; 127 | quint64 start, end; 128 | if (m_startText->text().startsWith("0x")) 129 | start = m_startText->text().mid(2).toULongLong(&ok, 16); 130 | else 131 | start = m_startText->text().toULongLong(&ok); 132 | if (!ok) { 133 | QMessageBox::warning(this, 134 | "Invalid input", "Invalid start offset!"); 135 | return; 136 | } 137 | if (m_endText->text().startsWith("0x")) 138 | end = m_endText->text().mid(2).toULongLong(&ok, 16); 139 | else 140 | end = m_endText->text().toULongLong(&ok); 141 | if (!ok) { 142 | QMessageBox::warning(this, 143 | "Invalid input", "Invalid end offset!"); 144 | return; 145 | } 146 | // TODO: There appears to be a bug in selectOffset where it only selects a single line 147 | // That, or I'm using it incorrectly. 148 | // m_hexCursor->selectOffset(start, (end - start) + 1); 149 | m_hexCursor->moveTo(start / 0x10, start % 0x10); 150 | m_hexCursor->select(end / 0x10, end % 0x10); 151 | accept(); 152 | } 153 | -------------------------------------------------------------------------------- /hex-viewer/selectdialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SELECTDIALOG_HPP 2 | #define SELECTDIALOG_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | class SelectDialog : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit SelectDialog(QHexCursor* hexCursor, QWidget* parent = nullptr); 16 | 17 | private: 18 | QFormLayout* m_formLayout; 19 | QLineEdit* m_startText; 20 | QLineEdit* m_endText; 21 | QLineEdit* m_lengthText; 22 | 23 | QPushButton* m_okButton; 24 | QPushButton* m_cancelButton; 25 | 26 | QHexCursor* m_hexCursor; 27 | 28 | private slots: 29 | void slotCancel(); 30 | void selectBytes(); 31 | }; 32 | 33 | #endif /* SELECTDIALOG_HPP */ 34 | -------------------------------------------------------------------------------- /mount-tool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}/core) 2 | #add_definitions("-Wall" "-g") 3 | 4 | add_executable(mount-tool main.cpp) 5 | 6 | target_link_libraries(mount-tool core) 7 | 8 | set_target_properties(mount-tool PROPERTIES 9 | RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_SOURCE_DIR}/bin/Debug" 10 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_SOURCE_DIR}/bin/Release" 11 | RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_SOURCE_DIR}/bin/RelWithDebInfo" 12 | RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${PROJECT_SOURCE_DIR}/bin/MinSizeRel" 13 | ) -------------------------------------------------------------------------------- /mount-tool/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void printDirectory(vfs::VfsDirectory* dir, int level) 18 | { 19 | for (auto child : dir->getChildren()) { 20 | for (auto i = 0; i < level; i++) 21 | std::cout << " "; 22 | std::cout << child->getName().c_str() << std::endl; 23 | if (child->getType() == vfs::VfsNodeType::DIRECTORY) { 24 | auto childDir = static_cast(child); 25 | printDirectory(childDir, level+1); 26 | } 27 | } 28 | } 29 | 30 | void listPartitions(disk::Disk* disk) 31 | { 32 | std::cout << std::setw(26) << std::left << "Partition Name" 33 | << std::setw(16) << std::left << "Start" 34 | << std::setw(16) << std::left << "End" 35 | << std::setw(16) << std::left << "Length" 36 | << std::endl; 37 | 38 | for (auto partition : disk->getPartitions()) { 39 | std::cout << std::setw(26) << std::left << partition->getName() 40 | << std::setw(16) << std::left << std::hex << partition->getStart() 41 | << std::setw(16) << std::left << std::hex << partition->getEnd() 42 | << std::setw(16) << std::left << std::hex << partition->getLength() 43 | << std::endl; 44 | } 45 | } 46 | 47 | void buildConfig(disk::DiskConfig* config, std::string& imageFile, std::ifstream& keyFile) 48 | { 49 | keyFile.seekg(0, std::ios::end); 50 | auto keyLen = keyFile.tellg(); 51 | keyFile.seekg(0, std::ios::beg); 52 | 53 | std::vector keyData(keyLen); 54 | keyFile.read(keyData.data(), keyLen); 55 | 56 | config->setKeys(keyData.data(), keyLen); 57 | 58 | auto *stream = io::stream::DiskStreamFactory::getStream(imageFile); 59 | config->setStream(stream); 60 | } 61 | 62 | void printUsage(int argc, char** argv) 63 | { 64 | std::cout << "Usage: " << argv[0] << " [command] [arguments...]" << std::endl 65 | << std::endl 66 | << "Commands:" << std::endl 67 | << " list List available partitions." << std::endl 68 | << " display Display file system contents." << std::endl 69 | << std::endl; 70 | } 71 | 72 | int main(int argc, char** argv) 73 | { 74 | if (argc < 4) { 75 | printUsage(argc, argv); 76 | return 1; 77 | } 78 | 79 | auto logger = new logging::StdOutLogHandler; 80 | #ifndef NDEBUG 81 | logger->setLogLevel(logging::LogLevel::Debug); 82 | #endif 83 | rAddHandler("stdout", logger); 84 | 85 | std::string command(argv[1]); 86 | std::string input(argv[2]); 87 | 88 | std::ifstream keyFile; 89 | keyFile.open(argv[3], std::ios::binary); 90 | if (!keyFile.is_open()) { 91 | rError("Failed to open key file: " + std::string(argv[3])); 92 | return 1; 93 | } 94 | 95 | disk::DiskConfig config; 96 | buildConfig(&config, input, keyFile); 97 | 98 | disk::Disk* disk = formats::DiskFormatFactory::getInstance()->detectFormat(&config); 99 | if (!disk) { 100 | rError("Could not detect disk format"); 101 | return 1; 102 | } 103 | 104 | if (!disk->getPartitions().size()) { 105 | rError("Could not find any partitions in this disk"); 106 | return 1; 107 | } 108 | 109 | if (command == "list") { 110 | listPartitions(disk); 111 | } 112 | else if (command == "display") { 113 | std::string partitionName(argv[4]); 114 | 115 | auto partition = disk->getPartitionByName(partitionName); 116 | if (partition == nullptr) { 117 | rError("No partition named: " + partitionName); 118 | return 1; 119 | } 120 | 121 | partition->mount(); 122 | auto vfs = partition->getVfs(); 123 | 124 | if (!vfs->isMounted()) { 125 | rError("Could not mount the file system."); 126 | return 1; 127 | } 128 | 129 | auto root = vfs->getRoot(); 130 | printDirectory(root, 0); 131 | } 132 | 133 | return 0; 134 | } --------------------------------------------------------------------------------