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.
gui/initial setup: Add button of download Preinstalled Firmware.
  • Loading branch information
Zangetsu38 committed Oct 29, 2024
1 parent 52b82dd commit 582fbd0
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 8 deletions.
19 changes: 11 additions & 8 deletions vita3k/gui/src/initial_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,16 @@ void draw_initial_setup(GuiState &gui, EmuEnvState &emuenv) {
const auto WINDOW_SIZE = ImVec2(756.f * SCALE.x, 418.f * SCALE.y);
const auto SELECT_SIZE = 72.f * SCALE.y;
const auto BUTTON_SIZE = ImVec2(154.f * SCALE.x, 52.f * SCALE.y);
const auto BIG_BUTTON_SIZE = ImVec2(324.f * SCALE.x, 48.f * SCALE.y);
const auto BIG_BUTTON_SIZE = ImVec2(344.f * SCALE.x, 48.f * SCALE.y);
const auto BIG_BUTTON_POS = ImVec2((WINDOW_SIZE.x / 2.f) - (BIG_BUTTON_SIZE.x / 2.f), WINDOW_SIZE.y - BIG_BUTTON_SIZE.y - (20.f * SCALE.y));

auto &lang = gui.lang.initial_setup;
auto &common = emuenv.common_dialog.lang.common;
const auto completed_setup = lang["completed_setup"].c_str();

const auto is_default_path = emuenv.cfg.pref_path == emuenv.default_path;
const auto FW_PREINST_PATH{ emuenv.pref_path / "pd0" };
const auto FW_PREINST_INSTALLED = fs::exists(FW_PREINST_PATH) && !fs::is_empty(FW_PREINST_PATH);
const auto FW_PATH{ emuenv.pref_path / "vs0" };
const auto FW_INSTALLED = fs::exists(FW_PATH) && !fs::is_empty(FW_PATH);
const auto FW_FONT_PATH{ emuenv.pref_path / "sa0" };
Expand Down Expand Up @@ -170,27 +172,28 @@ void draw_initial_setup(GuiState &gui, EmuEnvState &emuenv) {
break;
case INSTALL_FIRMWARE:
title_str = lang["install_firmware"];
ImGui::SetCursorPos(ImVec2((WINDOW_SIZE.x / 2.f) - (ImGui::CalcTextSize(lang["install_highly_recommended"].c_str()).x / 2.f), (WINDOW_SIZE.y / 2.f) - (ImGui::GetFontSize() * 2.f)));
ImGui::SetCursorPos(ImVec2((WINDOW_SIZE.x / 2.f) - (ImGui::CalcTextSize(lang["install_highly_recommended"].c_str()).x / 2.f), (WINDOW_SIZE.y / 2.f) - (ImGui::GetFontSize() * 3.5f)));
ImGui::TextColored(GUI_COLOR_TEXT_TITLE, "%s", lang["install_highly_recommended"].c_str());
ImGui::Spacing();
if (ImGui::Button("Download Preinst Firmware", BIG_BUTTON_SIZE))
open_path("http://bit.ly/4hlePsX");
ImGui::SameLine(0, 20.f * SCALE.x);
ImGui::Text("%s %s", lang["installed"].c_str(), FW_PREINST_INSTALLED ? "V" : "X");
ImGui::Spacing();
if (ImGui::Button(lang["download_firmware"].c_str(), BIG_BUTTON_SIZE))
get_firmware_file(emuenv);
ImGui::SameLine(0, 20.f * SCALE.x);
ImGui::Text("%s %s", lang["installed"].c_str(), FW_INSTALLED ? "V" : "X");
ImGui::Spacing();
if (ImGui::Button(lang["download_font_package"].c_str(), BIG_BUTTON_SIZE))
open_path("https://bit.ly/2P2rb0r");
open_path("https://bit.ly/48ouDaa");
ImGui::SameLine(0, 20.f * SCALE.x);
ImGui::Text("%s %s", lang["installed"].c_str(), FW_FONT_INSTALLED ? "V" : "X");
ImGui::SetCursorPos(BIG_BUTTON_POS);
if (ImGui::Button(lang["install_firmware_file"].c_str(), BIG_BUTTON_SIZE))
gui.file_menu.firmware_install_dialog = true;
if (gui.file_menu.firmware_install_dialog) {
ImGui::PushFont(gui.vita_font);
ImGui::SetWindowFontScale(RES_SCALE.x);
if (gui.file_menu.firmware_install_dialog)
draw_firmware_install_dialog(gui, emuenv);
ImGui::PopFont();
}
break;
case SELECT_INTERFACE_SETTINGS:
title_str = lang["select_interface_settings"];
Expand Down
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
Loading

0 comments on commit 582fbd0

Please sign in to comment.