Skip to content

Commit

Permalink
package: Add support installation of Preinstalled firmware.
Browse files Browse the repository at this point in the history
- Add support of exfat partition for installing it.
  • Loading branch information
Zangetsu38 committed Oct 28, 2024
1 parent 52b82dd commit fe98929
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 0 deletions.
2 changes: 2 additions & 0 deletions vita3k/packages/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
add_library(packages STATIC
src/exfat.cpp
src/license.cpp
src/pkg.cpp
src/pup.cpp
src/sce_utils.cpp
src/sfo.cpp
include/packages/exfat.h
include/packages/functions.h
include/packages/pkg.h
include/packages/sce_types.h
Expand Down
113 changes: 113 additions & 0 deletions vita3k/packages/include/packages/exfat.h
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
1 change: 1 addition & 0 deletions vita3k/packages/include/packages/sce_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ class SceRIF {
};

void register_keys(KeyStore &SCE_KEYS, int type);
void extract_exfat(const fs::path &partition_path, const std::string &partition, const fs::path &pref_path);
void extract_fat(const fs::path &partition_path, const std::string &partition, const fs::path &pref_path);
std::string decompress_segments(const std::vector<uint8_t> &decrypted_data, const uint64_t &size);
std::tuple<uint64_t, SelfType> get_key_type(std::ifstream &file, const SceHeader &sce_hdr);
Expand Down
171 changes: 171 additions & 0 deletions vita3k/packages/src/exfat.cpp
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
3 changes: 3 additions & 0 deletions vita3k/packages/src/pup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ static void decrypt_pup_packages(const fs::path &src, const fs::path &dest, KeyS
}

join_files(dest, "os0-", dest / "os0.img");
join_files(dest, "pd0-", dest / "pd0.img");
join_files(dest, "vs0-", dest / "vs0.img");
join_files(dest, "sa0-", dest / "sa0.img");
}
Expand Down Expand Up @@ -282,6 +283,8 @@ void install_pup(const fs::path &pref_path, const fs::path &pup_path, const std:
}
}
}
if (fs::file_size(pup_dec / "pd0.img") > 0)
extract_exfat(pup_dec, "pd0.img", pref_path);
if (fs::file_size(pup_dec / "sa0.img") > 0)
extract_fat(pup_dec, "sa0.img", pref_path);
if (fs::file_size(pup_dec / "vs0.img") > 0) {
Expand Down
13 changes: 13 additions & 0 deletions vita3k/packages/src/sce_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <fat16/fat16.h>
#include <miniz.h>
#include <openssl/evp.h>
#include <packages/exfat.h>
#include <packages/sce_types.h>
#include <util/string_utils.h>

Expand Down Expand Up @@ -652,6 +653,18 @@ static void traverse_directory(Fat16::Image &img, Fat16::Entry mee, const fs::pa
}
}

void extract_exfat(const fs::path &partition_path, const std::string &partition, const fs::path &pref_path) {
// Open the partition file for reading in binary mode and seek to the end
fs::ifstream img(partition_path / partition, std::ios::binary | std::ios::ate);
if (!img.is_open()) {
LOG_ERROR("Failed to open partition file");
return;
}

exfat::extract_exfat(img, pref_path / partition.substr(0, 3));
img.close();
}

void extract_fat(const fs::path &partition_path, const std::string &partition, const fs::path &pref_path) {
FILE *f = FOPEN((partition_path / partition).native().c_str(), "rb");
Fat16::Image img(
Expand Down

0 comments on commit fe98929

Please sign in to comment.