forked from Vita3K/Vita3K
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
package: Add support installation of Preinstalled firmware.
- Add support of exfat partition for installing it.
- Loading branch information
1 parent
52b82dd
commit fe98929
Showing
6 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Vita3K emulator project | ||
// Copyright (C) 2024 Vita3K team | ||
// | ||
// This program is free software; you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation; either version 2 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License along | ||
// with this program; if not, write to the Free Software Foundation, Inc., | ||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
|
||
#pragma once | ||
|
||
#include <util/log.h> | ||
|
||
// Credits to relan for their original work on this https://github.com/relan/exfat | ||
|
||
#define EXFAT_ENAME_MAX 15 | ||
|
||
struct ExFATSuperBlock { | ||
uint8_t jump[3]; /* 0x00 jmp and nop instructions */ | ||
uint8_t oem_name[8]; /* 0x03 "EXFAT " */ | ||
uint8_t __unused1[53]; /* 0x0B always 0 */ | ||
uint64_t sector_start; /* 0x40 partition first sector */ | ||
uint64_t sector_count; /* 0x48 partition sectors count */ | ||
uint32_t fat_sector_start; /* 0x50 FAT first sector */ | ||
uint32_t fat_sector_count; /* 0x54 FAT sectors count */ | ||
uint32_t cluster_sector_start; /* 0x58 first cluster sector */ | ||
uint32_t cluster_count; /* 0x5C total clusters count */ | ||
uint32_t rootdir_cluster; /* 0x60 first cluster of the root dir */ | ||
uint32_t volume_serial; /* 0x64 volume serial number */ | ||
|
||
struct { /* 0x68 FS version */ | ||
uint8_t minor; /* Minor version */ | ||
uint8_t major; /* Major version */ | ||
} version; | ||
|
||
uint16_t volume_state; /* 0x6A volume state flags */ | ||
uint8_t sector_bits; /* 0x6C sector size as (1 << n) */ | ||
uint8_t spc_bits; /* 0x6D sectors per cluster as (1 << n) */ | ||
uint8_t fat_count; /* 0x6E always 1 */ | ||
uint8_t drive_no; /* 0x6F always 0x80 */ | ||
uint8_t allocated_percent; /* 0x70 percentage of allocated space */ | ||
uint8_t __unused2[397]; /* 0x71 always 0 */ | ||
uint16_t boot_signature; /* 0xAA55 */ | ||
}; | ||
|
||
struct ExFATFileEntry { | ||
uint8_t type; /* EXFAT_ENTRY_FILE */ | ||
uint8_t continuations; | ||
uint16_t checksum; | ||
uint16_t attrib; /* combination of EXFAT_ATTRIB_xxx */ | ||
uint16_t __unknown1; | ||
uint16_t crtime, crdate; /* creation date and time */ | ||
uint16_t mtime, mdate; /* latest modification date and time */ | ||
uint16_t atime, adate; /* latest access date and time */ | ||
uint8_t crtime_cs; /* creation time in cs (centiseconds) */ | ||
uint8_t mtime_cs; /* latest modification time in cs */ | ||
uint8_t crtime_tzo, mtime_tzo, atime_tzo; /* timezone offset encoded */ | ||
uint8_t __unknown2[7]; | ||
}; | ||
|
||
struct ExFATFileEntryInfo { | ||
uint8_t type; /* EXFAT_ENTRY_FILE_INFO */ | ||
uint8_t flags; /* combination of EXFAT_FLAG_xxx */ | ||
uint8_t __unknown1; | ||
uint8_t name_length; | ||
uint16_t name_hash; | ||
uint16_t __unknown2; | ||
uint64_t valid_size; /* in bytes, less or equal to size */ | ||
uint8_t __unknown3[4]; | ||
uint32_t start_cluster; | ||
uint64_t size; /* in bytes */ | ||
}; | ||
|
||
struct ExFATEntryName { | ||
uint8_t type; /* EXFAT_ENTRY_FILE_NAME */ | ||
uint8_t __unknown; | ||
uint16_t name[EXFAT_ENAME_MAX]; /* in UTF-16LE */ | ||
}; | ||
|
||
enum ExFATAttrib { | ||
EXFAT_ATTRIB_RO = 0x01, | ||
EXFAT_ATTRIB_HIDDEN = 0x02, | ||
EXFAT_ATTRIB_SYSTEM = 0x04, | ||
EXFAT_ATTRIB_VOLUME = 0x08, | ||
EXFAT_ATTRIB_DIR = 0x10, | ||
EXFAT_ATTRIB_ARCH = 0x20 | ||
}; | ||
|
||
enum ExFATEntryType { | ||
EXFAT_ENTRY_VALID = 0x80, | ||
EXFAT_ENTRY_CONTINUED = 0x40, | ||
EXFAT_ENTRY_OPTIONAL = 0x20, | ||
|
||
EXFAT_ENTRY_BITMAP = (0x01 | EXFAT_ENTRY_VALID), | ||
EXFAT_ENTRY_UPCASE = (0x02 | EXFAT_ENTRY_VALID), | ||
EXFAT_ENTRY_LABEL = (0x03 | EXFAT_ENTRY_VALID), | ||
EXFAT_ENTRY_FILE = (0x05 | EXFAT_ENTRY_VALID), | ||
EXFAT_ENTRY_FILE_INFO = (0x00 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED), | ||
EXFAT_ENTRY_FILE_NAME = (0x01 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED), | ||
EXFAT_ENTRY_FILE_TAIL = (0x00 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED | EXFAT_ENTRY_OPTIONAL) | ||
}; | ||
|
||
namespace exfat { | ||
void extract_exfat(fs::ifstream &img, const fs::path &output_path); | ||
} // namespace exfat |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// Vita3K emulator project | ||
// Copyright (C) 2024 Vita3K team | ||
// | ||
// This program is free software; you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation; either version 2 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License along | ||
// with this program; if not, write to the Free Software Foundation, Inc., | ||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
|
||
#include <packages/exfat.h> | ||
|
||
namespace exfat { | ||
|
||
static std::string get_exfat_file_name(fs::ifstream &img, uint8_t continuations) { | ||
std::vector<uint16_t> utf16Data; | ||
|
||
// Loop depending on the number of continuations, subtract 1 because continuations start to 2 | ||
for (int cont = 0; cont < (continuations - 1); ++cont) { | ||
// read the name entry | ||
ExFATEntryName nameEntry; | ||
img.read(reinterpret_cast<char *>(&nameEntry), sizeof(ExFATEntryName)); | ||
|
||
// Check if the entry is of type `0xC1` | ||
if (nameEntry.type == 0xC1) { | ||
// Adding the name characters to `utf16Data` | ||
for (int i = 0; i < EXFAT_ENAME_MAX; ++i) | ||
utf16Data.push_back(nameEntry.name[i]); | ||
} else { | ||
LOG_ERROR("Error: Unexpected type of continuation entry (expected 0xC1, found: 0x{:X}, on offset: {})", nameEntry.type, static_cast<int>(img.tellg())); | ||
break; | ||
} | ||
} | ||
|
||
// Adding null-terminator | ||
utf16Data.push_back(0); | ||
|
||
// Convert name to UTF-8 | ||
std::string utf8String; | ||
for (uint16_t ch : utf16Data) { | ||
if (ch == 0) { | ||
break; | ||
} | ||
if (ch < 0x80) { | ||
utf8String.push_back(static_cast<char>(ch)); | ||
} else if (ch < 0x800) { | ||
utf8String.push_back(0xC0 | (ch >> 6)); | ||
utf8String.push_back(0x80 | (ch & 0x3F)); | ||
} else { | ||
utf8String.push_back(0xE0 | (ch >> 12)); | ||
utf8String.push_back(0x80 | ((ch >> 6) & 0x3F)); | ||
utf8String.push_back(0x80 | (ch & 0x3F)); | ||
} | ||
} | ||
|
||
return utf8String; | ||
} | ||
|
||
static uint64_t get_cluster_offset(const ExFATSuperBlock &super_block, uint32_t cluster) { | ||
const uint64_t sector_size = static_cast<uint64_t>(1 << super_block.sector_bits); | ||
const uint64_t sectors_per_cluster = static_cast<uint64_t>(1 << super_block.spc_bits); | ||
const uint64_t cluster_size = sector_size * sectors_per_cluster; | ||
|
||
// The cluster number is 0-based, so we need to add 1 | ||
return static_cast<uint64_t>(cluster + 1) * cluster_size; | ||
} | ||
|
||
static void traverse_directory(fs::ifstream &img, const uint64_t img_size, std::vector<std::streampos> &offset_stack, const ExFATSuperBlock &super_block, | ||
const uint32_t cluster, const fs::path &output_path, fs::path current_dir) { | ||
// Get the offset of the cluster | ||
const auto cluster_offset = get_cluster_offset(super_block, cluster); | ||
img.seekg(cluster_offset); | ||
|
||
// Loop through the entries in the cluster | ||
while (img.tellg() < img_size) { | ||
// Read the file entry | ||
ExFATFileEntry file_entry; | ||
img.read(reinterpret_cast<char *>(&file_entry), sizeof(ExFATFileEntry)); | ||
|
||
switch (file_entry.type) { | ||
case EXFAT_ENTRY_BITMAP: | ||
case EXFAT_ENTRY_UPCASE: | ||
case EXFAT_ENTRY_LABEL: | ||
// Skip these entries | ||
break; | ||
case EXFAT_ENTRY_FILE: { | ||
// Read the file info | ||
ExFATFileEntryInfo file_info; | ||
img.read(reinterpret_cast<char *>(&file_info), sizeof(ExFATFileEntryInfo)); | ||
|
||
// Get the name of file or directory | ||
std::string name = get_exfat_file_name(img, file_entry.continuations); | ||
|
||
// Set path of the current file or directory | ||
const auto subdir = current_dir / name; | ||
const auto current_output_path = output_path / subdir; | ||
|
||
// Get the current offset | ||
const uint64_t current_offset = img.tellg(); | ||
|
||
if (file_entry.attrib & EXFAT_ATTRIB_DIR) { | ||
fs::create_directories(current_output_path); | ||
|
||
// Save the current offset before entering | ||
offset_stack.push_back(current_offset); | ||
|
||
// Traverse the directory recursively | ||
traverse_directory(img, img_size, offset_stack, super_block, file_info.start_cluster, output_path, subdir); | ||
} else if (file_entry.attrib & EXFAT_ATTRIB_ARCH) { | ||
// Get offset of current file | ||
const auto file_offset = get_cluster_offset(super_block, file_info.start_cluster); | ||
|
||
// Seek to the file offset | ||
img.seekg(file_offset); | ||
|
||
// Create the file and write the data | ||
fs::ofstream output_file(current_output_path, std::ios::binary); | ||
const auto file_size = file_info.size; | ||
std::vector<char> buffer(file_size); | ||
img.read(buffer.data(), file_size); | ||
output_file.write(buffer.data(), file_size); | ||
output_file.close(); | ||
|
||
// Go back to the current offset for the next entry | ||
img.seekg(current_offset); | ||
} | ||
break; | ||
} | ||
default: | ||
// End of directory, return to parent if entries remain, otherwise seek to file end. | ||
if (!offset_stack.empty()) { | ||
img.seekg(offset_stack.back()); | ||
offset_stack.pop_back(); | ||
current_dir = current_dir.parent_path(); | ||
} else { | ||
img.seekg(img_size); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
|
||
void extract_exfat(fs::ifstream &img, const fs::path &output_path) { | ||
// Get the size of the image | ||
const auto img_size = img.tellg(); | ||
|
||
// Go back to the beginning | ||
img.seekg(0, std::ios::beg); | ||
|
||
// Read the super block | ||
ExFATSuperBlock super_block; | ||
img.read(reinterpret_cast<char *>(&super_block), sizeof(ExFATSuperBlock)); | ||
|
||
// Stack to keep track of the offsets | ||
std::vector<std::streampos> offset_stack; | ||
|
||
// Current directory | ||
fs::path current_dir; | ||
|
||
// Traverse the root directory | ||
traverse_directory(img, img_size, offset_stack, super_block, super_block.rootdir_cluster, output_path, current_dir); | ||
} | ||
|
||
} // namespace exfat |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters