112 Commits
v1.0 ... utf8

Author SHA1 Message Date
2288e71605 Fix OSUTF8to32 2021-07-10 19:40:39 -07:00
0396cf467b Restore C++17 compatibility 2021-06-30 17:00:42 -04:00
0985c63958 Fix compile errors/typos; update logvisor 2021-06-30 16:22:50 -04:00
c1635245b8 Use UTF-8 exclusively internally; replace iconv with OSUTF
Filenames are now unconditionally encoded/decoded as
Shift-JIS, which provides a reasonable fallback to
7-bit ASCII.
2021-06-30 13:47:10 -04:00
a525f60775 Assume CP_USA_ASCII by default in Node as well 2021-06-27 10:56:43 -07:00
8127bddb97 Merge branch 'Minty-Meeo-Shift-JIS-fixes' 2021-06-27 10:46:57 -07:00
Minty-Meeo
3c25647b6e The Encoding Update
While Nintendo's own documents claim GameCube and Wii disc file symbol tables only support 7-bit ASCII, this is far from the truth.  Indeed, even some first-party Nintendo games shipped with Shift-JIS encoded file symbol tables.  My guess?  The locale of whatever Windows machine mastered a GameCube or Wii disc influenced how wide character strings (UCS-2) were converted to narrow character strings.  To account for all possibilites, this update adds extensible multi-byte character set options to NOD-Tool.

A rundown of notable changes:
 - "-c XXXXX" option added to set the encoding of the GameCube / Wii ISO(s) being processed.
 - "SystemStringConv" renamed to "DiscLocToSystemConv"
 - "SystemUTF8Conv" renamed to "SystemToDiscLocConv"
 - Help message updated with new info.
 - Bugfix: AddBuildName had a logic error wherein the length of the SystemString was being used instead of length of the disc locale string.  This would corrupt the File Symbol Table if the disc locale string's length was greater than the SystemString's length.
 - Bugfix: recursiveMergeFST was not keeping track of parent indexes at all, meaning nested folders and their contents would be corrupted.  I simply copied the way recursiveBuildFST did things to fix this.
 - Bugfix (Windows): On Windows, for some reason, Sstat was a typedef for _stat (32-bit) instead of _stat64 (64-bit).  This is confounding, because untrimmed Wii ISOs will always be larger than the unsigned 32-bit integer limit (4,699,979,776 bytes vs 4,294,967,295 bytes), meaning the MergeWii errand has never worked for untrimmed ISOs on Windows.  Was this never tested??
 - Bugfix (Windows): Did you know Windows Command Prompt fully supports Unicode?  Stdio streams are now in _O_U16TEXT mode for Windows only.  Previously, attempting to print any character that could not be narrowed to your locale's encoding would either silently fail (std functions), or throw an exception (fmt functions).  As a minor drawback, narrow character print functions can no longer be used when stdio is in _O_U16TEXT mode, necessitating my PR for Logvisor here: (AxioDL/logvisor#7)
 - ExtractionContext::progressCB now uses SystemStringView because widechar printing works correctly on Windows now.
 - progFunc lambda no longer throws exceptions when printing unicode because widechar printing works correctly on Windows now.
 - Top-level constructors and functions with a Codepage_t parameter have also signatures that default to the US-ASCII codepage.
    - DiscGCN constructor
    - DiscBuilderGCN constructor
    - DiscBuilderGCN::CalculateTotalSizeRequired
    - DiscMergerGCN constructor
    - DiscMergerGCN::CalculateTotalSizeRequired
    - DiscWii constructor
    - DiscBuilderWii constructor
    - DiscBuilderWii::CalculateTotalSizeRequired
    - DiscMergerWii constructor
    - DiscMergerWii::CalculateTotalSizeRequired
    - OpenDiscFromImage
 - Conversion between system encoding and disc locale encoding has checks in place to warn the user if string conversion goes awry.
2021-06-27 03:26:20 -05:00
3eccde013d Update logvisor 2021-06-19 14:44:24 -07:00
Henrique Gemignani Passos Lima
ab8f4c3990 Update logvisor 2021-06-19 13:53:45 +03:00
6a231004b1 Update logvisor 2021-05-07 23:21:10 -07:00
d14b798b5f Don't install within add_subdirectory; update logvisor 2021-04-13 10:34:57 -04:00
1f110f3549 Update logvisor 2021-04-12 15:54:07 -04:00
311d20532e Update logvisor 2021-04-06 17:57:07 -04:00
8fdc893c86 Update logvisor; set submodule tracking branch 2021-04-06 12:48:38 -04:00
fdc8be487d Update logvisor 2021-04-05 16:31:21 -04:00
3a21961a4e Update logvisor 2021-04-05 13:26:39 -04:00
da399b5b67 Update logvisor 2021-04-05 12:06:30 -04:00
11c734be47 Update logvisor 2021-04-04 18:23:33 -04:00
364787604d Update logivisor 2021-04-03 10:28:23 -07:00
02c188497a Fixes for Apple Silicon
- Only include cpuid.h for x86
- Only set -maes flag for x86
2021-01-06 20:32:21 -05:00
d53d677038 Merge commit '5b1b6f6' 2020-12-31 13:59:27 -08:00
5b1b6f6f80 Update logvisor 2020-12-31 13:58:57 -08:00
393a11ffb5 Update CMakeLists for individual build 2020-05-31 17:23:01 -04:00
Jack Andersen
2783337c36 Update fmtlib 2020-04-11 12:45:06 -10:00
Jack Andersen
dffcac50c5 Exclude CMake CXX standard from MSVC 2020-04-10 19:00:19 -10:00
Jack Andersen
f147e12356 Windows build fixes 2019-11-24 15:17:57 -10:00
Jack Andersen
48a2981a93 Shared NFS data class not necessary 2019-11-24 13:47:48 -10:00
Jack Andersen
19604b2a3b Use correct slash finding function 2019-11-23 20:25:29 -10:00
Jack Andersen
c1a1d1abc8 Use logical block for iv 2019-11-23 20:00:48 -10:00
Jack Andersen
6bf4f07129 Consistent variable names for blocks 2019-11-23 17:29:57 -10:00
Jack Andersen
75fc574f81 Support for Wii U VC NFS images 2019-11-23 17:24:33 -10:00
Jack Andersen
11a0351d1c Merge branch 'master' of ssh://git.axiodl.com:6431/AxioDL/nod 2019-09-30 21:30:57 -10:00
Jack Andersen
4ec6c6697b Update logvisor 2019-09-30 21:30:42 -10:00
ba0c2b7843 Merge pull request #14 from lioncash/athena
General: Be explicit about athena's SeekOrigin type
2019-09-08 17:51:25 -07:00
Lioncash
221bc7c7f2 General: Be explicit about athena's SeekOrigin type
Allows this code to function if the enum is turned into an enum class.
2019-09-08 17:29:38 -04:00
acdadaf963 Merge pull request #5 from henriquegemignani/optional
Use std::optional for CalculateTotalSizeBuild
2019-09-07 01:18:43 -07:00
d658909948 Update athena 2019-09-06 23:31:44 -07:00
Henrique Gemignani Passos Lima
091262ace1 Use std::optional for CalculateTotalSizeBuild 2019-09-06 22:17:06 +02:00
97cfcea14e Merge pull request #13 from lioncash/overrun
DiscBase: Prevent potential off-by-one case within getPartitonNodeCount()
2019-09-05 19:35:01 -07:00
Lioncash
f5c3cbdcd7 DiscBase: Amend typo within getPartitonNodeCount() name
Adds an extra 'i' to correct a typo.
2019-09-05 21:57:44 -04:00
Lioncash
7ddff919c1 DiscBase: Prevent potential off-by-one case within getPartitonNodeCount()
We should be comparing with >= instead of >.
2019-09-05 21:56:49 -04:00
Lioncash
4bba7af2c2 DiscBase: std::move std::function instance
std::function isn't a trivial type (it's allowed to heap allocate to
store any necessary captures), so we can move it in the constructor to
avoid any unnecessary allocations
2019-09-05 21:52:43 -04:00
Lioncash
f443b60bde DiscBase: Mark member functions as const where applicable
These don't modify instance state, so they can be marked as const.
2019-09-05 21:51:20 -04:00
Lioncash
998d6a77c3 General: Remove redundant inline keyword
Functions defined within the class declaration are already inline by
default, so we don't need to specify the keyword here.
2019-09-05 21:48:07 -04:00
55301dd505 Merge pull request #11 from lioncash/make
General: Use std::make_unique where applicable
2019-08-30 16:28:53 -07:00
793413540d Merge pull request #12 from lioncash/fmt
FileIOWin32: Amend fmt format strings
2019-08-30 16:28:45 -07:00
Lioncash
f11eb728bf FileIOWin32: Amend fmt format strings
Amends a few stragglers from the transition to fmtlib.
2019-08-30 05:40:00 -04:00
Lioncash
a8753e273f General: Use std::make_unique where applicable
Makes for a little less reading in certain cases.
2019-08-30 05:35:12 -04:00
63264695b0 Merge pull request #9 from lioncash/include
General: Include headers directly where necessary
2019-08-30 02:13:24 -07:00
edc31b2107 Merge pull request #10 from lioncash/cmake
CMakeLists: Minor cleanup
2019-08-30 02:13:13 -07:00
Lioncash
1b3bb7815d CMakeLists: Move lib-specific CMake commands into the lib folder
Keeps a clear division between the lib and the driver CMake code.
2019-08-30 04:53:05 -04:00
Lioncash
89df98ee96 CMakeLists: Eliminate glob statement
Avoids a top-level variable from being defined and adds the source files
explicitly to the target like our other targets.
2019-08-30 04:43:56 -04:00
Lioncash
5935e84dab General: Include headers directly where necessary
Ensures includes are explicitly included where necessary to avoid
indirect inclusions.
2019-08-30 04:34:49 -04:00
df1e450728 Update logvisor 2019-08-26 15:56:48 -07:00
c9bf821285 Update logvisor 2019-08-17 23:00:24 -07:00
Jack Andersen
18b297e312 Merge pull request #8 from lioncash/cmake
CMakeLists: Migrate off directly modifying CMAKE_CXX_FLAGS
2019-08-10 09:30:26 -07:00
255a37216f Merge branch 'override' of git://github.com/lioncash/nod into lioncash-override 2019-08-09 23:14:32 -07:00
7da7761afb Merge branch 'lioncash-null-term' 2019-08-09 23:14:09 -07:00
Lioncash
876a2ccf81 CMakeLists: Migrate off directly modifying CMAKE_CXX_FLAGS
We can simply apply the compilation options directly to the target. We
can perform equivalent behavior for conditionally adding sources to the
target instead of assigning to a variable as well.
2019-08-10 01:52:27 -04:00
Lioncash
2171388b9d General: Make use of override where applicable
Makes it explicit where functions are being overridden in derived
classes/structs.
2019-08-10 01:41:44 -04:00
Lioncash
a572439967 DirectoryEnumerator: Handle non-null-terminated strings in CaseInsensitiveCompare
std::string_view instances aren't required to be null terminated. Given
this, we can make the functions a little safer by performing an explicit
bounded comparison on the range of characters, making the code more
generic with regards to handling the underlying string data.
2019-08-10 01:16:21 -04:00
Jack Andersen
ac6f2a1ed2 update logvisor 2019-07-27 15:20:43 -10:00
Jack Andersen
37792ba116 Massive libfmt refactor 2019-07-19 18:21:57 -10:00
d9b6be8446 Compile fixes 2019-06-20 15:45:35 -07:00
Jack Andersen
47322b9496 Refactor of CMake for cleaner dependency handling 2019-06-11 15:54:20 -10:00
Jack Andersen
77013bbd9f Merge branch 'master' of https://github.com/AxioDL/nod 2019-06-09 16:51:00 -10:00
Jack Andersen
34b943c40f Solve various signing warnings reported by GCC 2019-06-09 16:49:17 -10:00
Jack Andersen
a1284ae065 Update logvisor 2019-05-25 00:15:37 -10:00
Jack Andersen
4dd0375cae Adjusted install commands for better CMake compatibility 2019-05-23 14:17:45 -10:00
Jack Andersen
01237372e1 Update logvisor 2019-05-22 18:09:21 -10:00
Jack Andersen
d9638cc60d Update logvisor 2019-05-22 17:59:34 -10:00
Jack Andersen
0ac7140542 Minor .gitmodules change 2019-05-22 16:04:33 -10:00
Jack Andersen
a5e9166194 Merge branch 'cmake' 2019-05-22 16:03:03 -10:00
Jack Andersen
ca2aeecc64 Ensure exported include dir is correct 2019-05-12 13:16:34 -10:00
Jack Andersen
cd782047c8 Update .gitmodules 2019-05-12 12:48:02 -10:00
Jack Andersen
2b7ea07cae Remove redundant include path 2019-05-12 12:41:14 -10:00
Jack Andersen
ed28576b99 Update logvisor 2019-05-09 18:06:48 -10:00
Jack Andersen
95ed2ae7dc Implicit switch fallthrough refactor 2019-02-17 19:46:42 -10:00
Sam Fuller
f1c76a475d Cmake config support 2019-01-27 21:35:03 -08:00
Jack Andersen
be8409681f New code style refactor 2018-12-07 19:21:47 -10:00
Jack Andersen
3d380fdc3b macOS build fixes 2018-10-15 17:16:08 -10:00
Jack Andersen
f87b286ff3 Windows build fixes 2018-10-14 10:11:28 -10:00
Jack Andersen
e964a013fe Convert to pragma once 2018-10-06 17:39:24 -10:00
Jack Andersen
eb6aa30563 NX build fixes 2018-10-06 16:56:47 -10:00
1ad101897c Merge branch 'arukibree-master' closes #4 2018-07-06 16:03:00 -07:00
Aruki
6f777ebb48 Fixes for a couple warnings/errors whene compiling on windows 2018-07-06 01:02:23 -06:00
Jack Andersen
42589c3604 Update logvisor 2018-06-01 14:02:48 -10:00
Jack Andersen
4d9071bad7 Update logvisor 2018-05-26 18:22:14 -10:00
Jack Andersen
d5f5db440c Windows build fixes and warning avoidance 2018-05-24 20:38:06 -10:00
Jack Andersen
51a15e474e Update logvisor 2018-05-05 15:31:24 -10:00
Jack Andersen
274a63bb30 Update logvisor 2018-03-23 12:05:04 -10:00
Jack Andersen
bab7aab6fa Update logvisor 2018-01-22 19:00:12 -10:00
Jack Andersen
5197abc131 Update logvisor 2018-01-13 20:41:28 -10:00
Jack Andersen
648c015383 Update logvisor 2018-01-09 20:17:41 -10:00
Jack Andersen
bf00fcd10f Driver fix 2017-12-28 22:38:55 -10:00
Jack Andersen
a557f86974 Huge compile performance refactor 2017-12-28 21:57:54 -10:00
Jack Andersen
34de6276b0 UWP fix 2017-12-06 18:11:03 -10:00
Jack Andersen
3d70a568dc UWP support 2017-12-05 17:23:58 -10:00
63ae60a967 Update .gitmodules 2017-12-02 13:23:44 -08:00
Jack Andersen
e20fce1e6f Update logvisor 2017-11-18 21:10:10 -10:00
Jack Andersen
b5916af702 Require CMake 3.10 2017-11-13 18:51:39 -10:00
Jack Andersen
58ceb47b25 Windows fixes 2017-11-13 17:35:34 -10:00
Jack Andersen
69e96e3b3c Linux build fixes 2017-11-12 21:21:29 -10:00
Jack Andersen
27a2cb5998 string_view refactor 2017-11-12 20:18:53 -10:00
Jack Andersen
c374038103 Minor indent fix 2017-10-30 17:51:49 -10:00
Jack Andersen
db1a6f13a2 Fix missing variable initialization 2017-07-30 17:43:51 -10:00
Jack Andersen
fb2a5c91d2 Explicitly zero-initialize new image files 2017-07-30 16:03:20 -10:00
42ef3a7958 Fix return type derp 2017-07-25 05:06:35 -07:00
Jack Andersen
d597400f4a Fix FST parent directory index 2017-07-11 19:13:44 -10:00
e99290e3c3 Remove unreferenced local 2017-07-09 02:11:19 -07:00
72169e8e77 Added writeSysFiles for convience of integration 2017-07-09 01:25:43 -07:00
a7c19799e1 Add MSVC definitions 2017-07-08 22:25:19 -07:00
32 changed files with 8633 additions and 5480 deletions

4
.gitignore vendored
View File

@@ -4,3 +4,7 @@ version.h
.DS_Store .DS_Store
*.autosave *.autosave
docs/* docs/*
.idea/
cmake-build-*
build/
out/

3
.gitmodules vendored
View File

@@ -1,3 +1,4 @@
[submodule "logvisor"] [submodule "logvisor"]
path = logvisor path = logvisor
url = https://github.com/AxioDL/logvisor.git url = ../logvisor.git
branch = master

View File

@@ -1,18 +1,99 @@
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) # because of CMAKE_CXX_STANDARD cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
project(nod) project(nod VERSION 0.1)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) if (MSVC)
# Shaddup MSVC
add_compile_definitions(UNICODE=1 _UNICODE=1 __SSE__=1
_CRT_SECURE_NO_WARNINGS=1 D_SCL_SECURE_NO_WARNINGS=1
_SCL_SECURE_NO_DEPRECATE=1 _CRT_NONSTDC_NO_WARNINGS=1
_ENABLE_EXTENDED_ALIGNED_STORAGE=1 NOMINMAX=1)
add_compile_options(/IGNORE:4221 /wd4018 /wd4800 /wd4005 /wd4311 /wd4068
/wd4267 /wd4244 /wd4200 /wd4305 /wd4067 /wd4146 /wd4309 /wd4805 /utf-8 ${VS_OPTIONS})
add_compile_options(
# Disable exceptions
$<$<COMPILE_LANGUAGE:CXX>:/EHsc->
# Disable RTTI
$<$<COMPILE_LANGUAGE:CXX>:/GR->
# Enforce various standards compliant behavior.
$<$<COMPILE_LANGUAGE:CXX>:/permissive->
# Enable standard volatile semantics.
$<$<COMPILE_LANGUAGE:CXX>:/volatile:iso>
# Reports the proper value for the __cplusplus preprocessor macro.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
# Use latest C++ standard.
$<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>
)
add_compile_definitions(FMT_EXCEPTIONS=0 _HAS_EXCEPTIONS=0)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Flags for MSVC (not clang-cl)
add_compile_options(
# Allow constexpr variables to have explicit external linkage.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>
# Assume that new throws exceptions, allowing better code generation.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:throwingNew>
# Link-time Code Generation for Release builds
$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:/GL>
)
# Link-time Code Generation for Release builds
set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "/LTCG")
set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "/LTCG")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO /DEBUGTYPE:cv,fixup")
endif()
else()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
endif() endif()
include (CMakePackageConfigHelpers)
if (NOT TARGET logvisor) if (NOT TARGET logvisor)
add_subdirectory(logvisor) add_subdirectory(logvisor)
set(LOGVISOR_INCLUDE_DIR logvisor/include)
endif() endif()
include_directories(include ${LOGVISOR_INCLUDE_DIR})
file(GLOB NOD_HEADERS include/nod/*.h*)
add_subdirectory(lib) add_subdirectory(lib)
add_subdirectory(driver) add_subdirectory(driver)
install(DIRECTORY include/nod DESTINATION include/nod)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(version_config_file "${PROJECT_BINARY_DIR}/nodConfigVersion.cmake")
set(config_file "${PROJECT_BINARY_DIR}/nodConfig.cmake")
set(config_install_dir "lib/cmake/nod")
# Install the target config files
install(
EXPORT nodTargets
NAMESPACE "nod::"
DESTINATION "${config_install_dir}"
)
# Generate version config file
write_basic_package_version_file(
"${version_config_file}"
COMPATIBILITY SameMajorVersion
)
# Generate config file
configure_package_config_file(
"Config.cmake.in"
"${config_file}"
INSTALL_DESTINATION "lib/cmake/nod"
)
# Install the config files
install(
FILES "${config_file}" "${version_config_file}"
DESTINATION ${config_install_dir}
)
endif()

4
Config.cmake.in Normal file
View File

@@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/nodTargets.cmake")
check_required_components(nod)

View File

@@ -34,19 +34,19 @@ a content pipeline using the `nod::DiscBuilderBase` interface.
```cpp ```cpp
/* Sample logging lambda for progress feedback */ /* Sample logging lambda for progress feedback */
size_t lastIdx = -1; size_t lastIdx = -1;
auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes) auto progFunc = [&](size_t idx, const std::string& name, size_t bytes)
{ {
if (idx != lastIdx) if (idx != lastIdx)
{ {
lastIdx = idx; lastIdx = idx;
/* NOD provides I/O wrappers using wchar_t on Windows; /* NOD provides I/O wrappers using wchar_t on Windows;
* _S() conditionally makes string-literals wide */ * _S() conditionally makes string-literals wide */
nod::Printf(_S("\n")); fmt::print(_S("\n"));
} }
if (bytes != -1) if (bytes != -1)
nod::Printf(_S("\r%s %" PRISize " B"), name.c_str(), bytes); fmt::print(_S("\r{} {} B"), name, bytes);
else else
nod::Printf(_S("\r%s"), name.c_str()); fmt::print(_S("\r{}"), name);
fflush(stdout); fflush(stdout);
}; };

View File

@@ -3,9 +3,11 @@ add_executable(nodtool main.cpp)
target_link_libraries(nodtool nod logvisor) target_link_libraries(nodtool nod logvisor)
if (NOT WIN32) if (NOT WIN32)
target_link_libraries(nodtool pthread) target_link_libraries(nodtool pthread)
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
target_link_libraries(nodtool execinfo) target_link_libraries(nodtool execinfo)
else() else ()
target_link_libraries(nodtool dl) target_link_libraries(nodtool dl)
endif() endif ()
endif() else ()
target_sources(nodtool PRIVATE app.manifest)
endif ()

9
driver/app.manifest Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity type="win32" name="..." version="6.0.0.0"/>
<application>
<windowsSettings>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>
</assembly>

View File

@@ -1,265 +1,319 @@
#include <stdio.h> #include <array>
#include <string.h> #include <cstddef>
#include "logvisor/logvisor.hpp" #include <cstdint>
#include "nod/nod.hpp" #include <cstdio>
#include <cstring>
#if _WIN32
#include <fcntl.h>
#include <io.h>
#endif
static void printHelp() #include <logvisor/logvisor.hpp>
{
fprintf(stderr, "Usage:\n" #include <nod/DiscBase.hpp>
" nodtool extract [-f] <image-in> [<dir-out>]\n" #include <nod/DiscGCN.hpp>
" nodtool makegcn <fsroot-in> [<image-out>]\n" #include <nod/DiscWii.hpp>
" nodtool makewii <fsroot-in> [<image-out>]\n" #include <nod/nod.hpp>
" nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n"
" nodtool mergewii <fsroot-in> <image-in> [<image-out>]\n"); static void printHelp() {
fmt::print(stderr, FMT_STRING(
"Usage:\n"
" nodtool extract [options] <image-in> [<dir-out>]\n"
" nodtool makegcn [options] <fsroot-in> [<image-out>]\n"
" nodtool makewii [options] <fsroot-in> [<image-out>]\n"
" nodtool mergegcn [options] <fsroot-in> <image-in> [<image-out>]\n"
" nodtool mergewii [options] <fsroot-in> <image-in> [<image-out>]\n"
"Options:\n"
" -f Force (extract only)\n"
" -v Verbose details (extract only).\n"));
} }
#if NOD_UCS2 #if _MSC_VER
#ifdef strcasecmp #include <nowide/args.hpp>
#undef strcasecmp
#endif
#define strcasecmp _wcsicmp
#define PRISize "Iu" #define PRISize "Iu"
int wmain(int argc, wchar_t* argv[]) int main(int argc, char* argv[]) {
nowide::args _(argc, argv);
#else #else
#define PRISize "zu" #define PRISize "zu"
int main(int argc, char* argv[]) int main(int argc, char* argv[]) {
#endif #endif
{ /* Enable logging to console */
if (argc < 3 || logvisor::RegisterStandardExceptions();
(!strcasecmp(argv[1], _S("makegcn")) && argc < 3) || logvisor::RegisterConsoleLogger();
(!strcasecmp(argv[1], _S("makewii")) && argc < 3) ||
(!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) || int argidx = 1;
(!strcasecmp(argv[1], _S("mergewii")) && argc < 4)) std::string errand;
{ bool verbose = false;
printHelp(); nod::ExtractionContext ctx = {true, [&](std::string_view str, float c) {
return 1; if (verbose)
fmt::print(stderr, FMT_STRING("Current node: {}, Extraction {:g}% Complete\n"),
str, c * 100.f);
}};
while (argidx < argc) {
if (!nod::StrCaseCmp(argv[argidx], "-f")) {
ctx.force = true;
++argidx;
continue;
} else if (!nod::StrCaseCmp(argv[argidx], "-v")) {
verbose = true;
++argidx;
continue;
} else if (errand.empty()) {
errand = argv[argidx];
++argidx;
continue;
} else {
break;
} }
}
/* Enable logging to console */ if (errand.empty()) {
logvisor::RegisterStandardExceptions(); printHelp();
logvisor::RegisterConsoleLogger(); return 1;
}
bool verbose = false; auto progFunc = [&](float prog, std::string_view name, size_t bytes) {
nod::ExtractionContext ctx = {true, fmt::print(FMT_STRING("\r "));
[&](const std::string& str, float c) { if (bytes != SIZE_MAX)
if (verbose) fmt::print(FMT_STRING("\r{:g}% {} {} B"), prog * 100.f, name, bytes);
fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.c_str(), c * 100.f);
}};
const nod::SystemChar* inDir = nullptr;
const nod::SystemChar* outDir = _S(".");
for (int a=2 ; a<argc ; ++a)
{
if (argv[a][0] == '-' && argv[a][1] == 'f')
ctx.force = true;
else if (argv[a][0] == '-' && argv[a][1] == 'v')
verbose = true;
else if (!inDir)
inDir = argv[a];
else
outDir = argv[a];
}
auto progFunc = [&](float prog, const nod::SystemString& name, size_t bytes)
{
nod::Printf(_S("\r "));
if (bytes != -1)
nod::Printf(_S("\r%g%% %s %" PRISize " B"), prog * 100.f, name.c_str(), bytes);
else
nod::Printf(_S("\r%g%% %s"), prog * 100.f, name.c_str());
fflush(stdout);
};
if (!strcasecmp(argv[1], _S("extract")))
{
bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(inDir, isWii);
if (!disc)
return 1;
nod::Mkdir(outDir, 0755);
nod::Partition* dataPart = disc->getDataPartition();
if (!dataPart)
return 1;
if (!dataPart->extractToDirectory(outDir, ctx))
return 1;
}
else if (!strcasecmp(argv[1], _S("makegcn")))
{
/* Pre-validate path */
nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
return 1;
}
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2]) == -1)
return 1;
nod::EBuildResult ret;
if (argc < 4)
{
nod::SystemString outPath(argv[2]);
outPath.append(_S(".iso"));
nod::DiscBuilderGCN b(outPath.c_str(), progFunc);
ret = b.buildFromDirectory(argv[2]);
}
else
{
nod::DiscBuilderGCN b(argv[3], progFunc);
ret = b.buildFromDirectory(argv[2]);
}
printf("\n");
if (ret != nod::EBuildResult::Success)
return 1;
}
else if (!strcasecmp(argv[1], _S("makewii")))
{
/* Pre-validate path */
nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[4]);
return 1;
}
bool dual = false;
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual) == -1)
return 1;
nod::EBuildResult ret;
if (argc < 4)
{
nod::SystemString outPath(argv[2]);
outPath.append(_S(".iso"));
nod::DiscBuilderWii b(outPath.c_str(), dual, progFunc);
ret = b.buildFromDirectory(argv[2]);
}
else
{
nod::DiscBuilderWii b(argv[3], dual, progFunc);
ret = b.buildFromDirectory(argv[2]);
}
printf("\n");
if (ret != nod::EBuildResult::Success)
return 1;
}
else if (!strcasecmp(argv[1], _S("mergegcn")))
{
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
return 1;
}
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[3]);
return 1;
}
bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
if (!disc)
{
nod::LogModule.report(logvisor::Error, _S("unable to open image %s"), argv[3]);
return 1;
}
if (isWii)
{
nod::LogModule.report(logvisor::Error, _S("Wii images should be merged with 'mergewii'"));
return 1;
}
if (nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), argv[2]) == -1)
return 1;
nod::EBuildResult ret;
if (argc < 5)
{
nod::SystemString outPath(argv[2]);
outPath.append(_S(".iso"));
nod::DiscMergerGCN b(outPath.c_str(), static_cast<nod::DiscGCN&>(*disc), progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
else
{
nod::DiscMergerGCN b(argv[4], static_cast<nod::DiscGCN&>(*disc), progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
printf("\n");
if (ret != nod::EBuildResult::Success)
return 1;
}
else if (!strcasecmp(argv[1], _S("mergewii")))
{
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
return 1;
}
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[3]);
return 1;
}
bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
if (!disc)
{
nod::LogModule.report(logvisor::Error, _S("unable to open image %s"), argv[3]);
return 1;
}
if (!isWii)
{
nod::LogModule.report(logvisor::Error, _S("GameCube images should be merged with 'mergegcn'"));
return 1;
}
bool dual = false;
if (nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), argv[2], dual) == -1)
return 1;
nod::EBuildResult ret;
if (argc < 5)
{
nod::SystemString outPath(argv[2]);
outPath.append(_S(".iso"));
nod::DiscMergerWii b(outPath.c_str(), static_cast<nod::DiscWii&>(*disc), dual, progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
else
{
nod::DiscMergerWii b(argv[4], static_cast<nod::DiscWii&>(*disc), dual, progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
printf("\n");
if (ret != nod::EBuildResult::Success)
return 1;
}
else else
{ fmt::print(FMT_STRING("\r{:g}% {}"), prog * 100.f, name);
fflush(stdout);
};
if (errand == "extract") {
std::string imageIn;
std::string dirOut;
while (argidx < argc) {
if (imageIn.empty()) {
imageIn = argv[argidx];
++argidx;
continue;
} else if (dirOut.empty()) {
dirOut = argv[argidx];
++argidx;
continue;
} else {
printHelp(); printHelp();
return 1; return 1;
}
}
if (dirOut.empty())
dirOut = ".";
bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
if (!disc)
return 1;
nod::Mkdir(dirOut.c_str(), 0755);
nod::IPartition* dataPart = disc->getDataPartition();
if (!dataPart)
return 1;
if (!dataPart->extractToDirectory(dirOut, ctx))
return 1;
} else if (errand == "makegcn") {
std::string fsrootIn;
std::string imageOut;
while (argidx < argc) {
if (fsrootIn.empty()) {
fsrootIn = argv[argidx];
++argidx;
continue;
} else if (imageOut.empty()) {
imageOut = argv[argidx];
++argidx;
continue;
} else {
printHelp();
return 1;
}
}
if (imageOut.empty())
imageOut = fsrootIn + ".gcm";
/* Pre-validate path */
nod::Sstat theStat;
if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as directory"), fsrootIn);
return 1;
} }
nod::LogModule.report(logvisor::Info, _S("Success!")); if (!nod::DiscBuilderGCN::CalculateTotalSizeRequired(fsrootIn))
return 0; return 1;
}
nod::EBuildResult ret;
nod::DiscBuilderGCN b(imageOut, progFunc);
ret = b.buildFromDirectory(fsrootIn);
fmt::print(FMT_STRING("\n"));
if (ret != nod::EBuildResult::Success)
return 1;
} else if (errand == "makewii") {
std::string fsrootIn;
std::string imageOut;
while (argidx < argc) {
if (fsrootIn.empty()) {
fsrootIn = argv[argidx];
++argidx;
continue;
} else if (imageOut.empty()) {
imageOut = argv[argidx];
++argidx;
continue;
} else {
printHelp();
return 1;
}
}
if (imageOut.empty())
imageOut = fsrootIn + ".iso";
/* Pre-validate path */
nod::Sstat theStat;
if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as directory"), fsrootIn);
return 1;
}
bool dual = false;
if (!nod::DiscBuilderWii::CalculateTotalSizeRequired(fsrootIn, dual))
return 1;
nod::EBuildResult ret;
nod::DiscBuilderWii b(imageOut, dual, progFunc);
ret = b.buildFromDirectory(fsrootIn);
fmt::print(FMT_STRING("\n"));
if (ret != nod::EBuildResult::Success)
return 1;
} else if (errand == "mergegcn") {
std::string fsrootIn;
std::string imageIn;
std::string imageOut;
while (argidx < argc) {
if (fsrootIn.empty()) {
fsrootIn = argv[argidx];
++argidx;
continue;
} else if (imageIn.empty()) {
imageIn = argv[argidx];
++argidx;
continue;
} else if (imageOut.empty()) {
imageOut = argv[argidx];
++argidx;
continue;
} else {
printHelp();
return 1;
}
}
if (imageOut.empty())
imageOut = fsrootIn + ".gcm";
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as directory"), fsrootIn);
return 1;
}
if (nod::Stat(imageIn.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) {
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as file"), imageIn);
return 1;
}
bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
if (!disc) {
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to open image {}"), imageIn);
return 1;
}
if (isWii) {
nod::LogModule.report(logvisor::Error, FMT_STRING("Wii images should be merged with 'mergewii'"));
return 1;
}
if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), fsrootIn))
return 1;
nod::EBuildResult ret;
nod::DiscMergerGCN b(imageOut, static_cast<nod::DiscGCN&>(*disc), progFunc);
ret = b.mergeFromDirectory(fsrootIn);
fmt::print(FMT_STRING("\n"));
if (ret != nod::EBuildResult::Success)
return 1;
} else if (errand == "mergewii") {
std::string fsrootIn;
std::string imageIn;
std::string imageOut;
while (argidx < argc) {
if (fsrootIn.empty()) {
fsrootIn = argv[argidx];
++argidx;
continue;
} else if (imageIn.empty()) {
imageIn = argv[argidx];
++argidx;
continue;
} else if (imageOut.empty()) {
imageOut = argv[argidx];
++argidx;
continue;
} else {
printHelp();
return 1;
}
}
if (imageOut.empty())
imageOut = fsrootIn + ".iso";
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as directory"), fsrootIn);
return 1;
}
if (nod::Stat(imageIn.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) {
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as file"), imageIn);
return 1;
}
bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
if (!disc) {
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to open image {}"), argv[3]);
return 1;
}
if (!isWii) {
nod::LogModule.report(logvisor::Error, FMT_STRING("GameCube images should be merged with 'mergegcn'"));
return 1;
}
bool dual = false;
if (!nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), fsrootIn, dual))
return 1;
nod::EBuildResult ret;
nod::DiscMergerWii b(imageOut, static_cast<nod::DiscWii&>(*disc), dual, progFunc);
ret = b.mergeFromDirectory(fsrootIn);
fmt::print(FMT_STRING("\n"));
if (ret != nod::EBuildResult::Success)
return 1;
} else {
printHelp();
return 1;
}
nod::LogModule.report(logvisor::Info, FMT_STRING("Success!"));
return 0;
}

View File

@@ -1,73 +1,52 @@
#ifndef __NOD_DIRECTORY_ENUMERATOR__ #pragma once
#define __NOD_DIRECTORY_ENUMERATOR__
#include "Util.hpp" #include <algorithm>
#include <cctype>
#include <cstddef>
#include <vector>
namespace nod #include "nod/Util.hpp"
{
struct CaseInsensitiveCompare namespace nod {
{
bool operator()(const std::string& lhs, const std::string& rhs) const
{
#if _WIN32
if (_stricmp(lhs.c_str(), rhs.c_str()) < 0)
#else
if (strcasecmp(lhs.c_str(), rhs.c_str()) < 0)
#endif
return true;
return false;
}
#if _WIN32 /**
bool operator()(const std::wstring& lhs, const std::wstring& rhs) const * @brief Case-insensitive comparator for std::map sorting
{ */
if (_wcsicmp(lhs.c_str(), rhs.c_str()) < 0) struct CaseInsensitiveCompare {
return true; // Allow heterogeneous lookup with maps that use this comparator.
return false; using is_transparent = void;
}
#endif bool operator()(std::string_view lhs, std::string_view rhs) const {
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](char lhs, char rhs) {
return std::tolower(static_cast<unsigned char>(lhs)) < std::tolower(static_cast<unsigned char>(rhs));
});
}
}; };
class DirectoryEnumerator class DirectoryEnumerator {
{
public: public:
enum class Mode enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted };
{ struct Entry {
Native, std::string m_path;
DirsSorted, std::string m_name;
FilesSorted, size_t m_fileSz;
DirsThenFilesSorted bool m_isDir;
};
struct Entry
{
SystemString m_path;
SystemString m_name;
size_t m_fileSz;
bool m_isDir;
private: Entry(std::string path, std::string name, size_t sz, bool isDir)
friend class DirectoryEnumerator; : m_path(std::move(path)), m_name(std::move(name)), m_fileSz(sz), m_isDir(isDir) {}
Entry(SystemString&& path, const SystemChar* name, size_t sz, bool isDir) };
: m_path(std::move(path)), m_name(name), m_fileSz(sz), m_isDir(isDir) {}
};
private: private:
std::vector<Entry> m_entries; std::vector<Entry> m_entries;
public: public:
DirectoryEnumerator(const SystemString& path, Mode mode=Mode::DirsThenFilesSorted, DirectoryEnumerator(std::string_view path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
bool sizeSort=false, bool reverse=false, bool noHidden=false) bool reverse = false, bool noHidden = false);
: DirectoryEnumerator(path.c_str(), mode, sizeSort, reverse, noHidden) {}
DirectoryEnumerator(const SystemChar* path, Mode mode=Mode::DirsThenFilesSorted,
bool sizeSort=false, bool reverse=false, bool noHidden=false);
operator bool() const {return m_entries.size() != 0;} explicit operator bool() const { return !m_entries.empty(); }
size_t size() const {return m_entries.size();} [[nodiscard]] size_t size() const { return m_entries.size(); }
std::vector<Entry>::const_iterator begin() const {return m_entries.cbegin();} [[nodiscard]] std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
std::vector<Entry>::const_iterator end() const {return m_entries.cend();} [[nodiscard]] std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
}; };
} } // namespace nod
#endif // __NOD_DIRECTORY_ENUMERATOR__

View File

@@ -1,529 +1,470 @@
#ifndef __NOD_DISC_BASE__ #pragma once
#define __NOD_DISC_BASE__
#include <vector> #include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <stdio.h> #include <vector>
#include <stdint.h>
#include <functional>
#include "Util.hpp"
#include "IDiscIO.hpp"
#include "IFileIO.hpp"
namespace nod #include "nod/IDiscIO.hpp"
{ #include "nod/IFileIO.hpp"
#include "nod/OSUTF.h"
#include "nod/Util.hpp"
using FProgress = std::function<void(float totalProg, const SystemString& fileName, size_t fileBytesXfered)>; namespace nod {
enum class EBuildResult using FProgress = std::function<void(float totalProg, std::string_view fileName, size_t fileBytesXfered)>;
{
Success,
Failed,
DiskFull
};
enum class PartitionKind : uint32_t enum class EBuildResult { Success, Failed, DiskFull };
{
Data, enum class PartitionKind : uint32_t { Data, Update, Channel };
Update, const char* getKindString(PartitionKind kind);
Channel
}; class FSTNode {
const SystemChar* getKindString(PartitionKind kind); uint32_t typeAndNameOffset;
uint32_t offset;
uint32_t length;
class FSTNode
{
uint32_t typeAndNameOffset;
uint32_t offset;
uint32_t length;
public: public:
FSTNode(bool isDir, uint32_t nameOff, uint32_t off, uint32_t len) FSTNode(bool isDir, uint32_t nameOff, uint32_t off, uint32_t len) {
{ typeAndNameOffset = nameOff & 0xffffff;
typeAndNameOffset = nameOff & 0xffffff; typeAndNameOffset |= isDir << 24;
typeAndNameOffset |= isDir << 24; typeAndNameOffset = SBig(typeAndNameOffset);
typeAndNameOffset = SBig(typeAndNameOffset); offset = SBig(off);
offset = SBig(off); length = SBig(len);
length = SBig(len); }
} bool isDir() const { return ((SBig(typeAndNameOffset) >> 24) != 0); }
inline bool isDir() const {return ((SBig(typeAndNameOffset) >> 24) != 0);} uint32_t getNameOffset() const { return SBig(typeAndNameOffset) & 0xffffff; }
inline uint32_t getNameOffset() const {return SBig(typeAndNameOffset) & 0xffffff;} uint32_t getOffset() const { return SBig(offset); }
inline uint32_t getOffset() const {return SBig(offset);} uint32_t getLength() const { return SBig(length); }
inline uint32_t getLength() const {return SBig(length);} void incrementLength() {
void incrementLength() uint32_t orig = SBig(length);
{ ++orig;
uint32_t orig = SBig(length); length = SBig(orig);
++orig; }
length = SBig(orig);
}
}; };
struct Header struct Header {
{ char m_gameID[6];
char m_gameID[6]; char m_discNum;
char m_discNum; char m_discVersion;
char m_discVersion; char m_audioStreaming;
char m_audioStreaming; char m_streamBufSz;
char m_streamBufSz; char m_unk1[14];
char m_unk1[14]; uint32_t m_wiiMagic;
uint32_t m_wiiMagic; uint32_t m_gcnMagic;
uint32_t m_gcnMagic; char m_gameTitle[64];
char m_gameTitle[64]; char m_disableHashVerification;
char m_disableHashVerification; char m_disableDiscEnc;
char m_disableDiscEnc; char m_unk2[0x39e];
char m_unk2[0x39e]; uint32_t m_debugMonOff;
uint32_t m_debugMonOff; uint32_t m_debugLoadAddr;
uint32_t m_debugLoadAddr; char m_unk3[0x18];
char m_unk3[0x18]; uint32_t m_dolOff;
uint32_t m_dolOff; uint32_t m_fstOff;
uint32_t m_fstOff; uint32_t m_fstSz;
uint32_t m_fstSz; uint32_t m_fstMaxSz;
uint32_t m_fstMaxSz; uint32_t m_fstMemoryAddress;
uint32_t m_fstMemoryAddress; uint32_t m_userPosition;
uint32_t m_userPosition; uint32_t m_userSz;
uint32_t m_userSz; uint8_t padding1[4];
uint8_t padding1[4];
Header() = default; Header() = default;
Header(IDiscIO& dio, bool& err) Header(IDiscIO& dio, bool& err) {
{ auto rs = dio.beginReadStream();
auto rs = dio.beginReadStream(); if (!rs) {
if (!rs) err = true;
{ return;
err = true;
return;
}
read(*rs);
} }
read(*rs);
}
void read(IReadStream& s) void read(IReadStream& s) {
{ memset(this, 0, sizeof(*this));
memset(this, 0, sizeof(*this)); s.read(this, sizeof(*this));
s.read(this, sizeof(*this)); m_wiiMagic = SBig(m_wiiMagic);
m_wiiMagic = SBig(m_wiiMagic); m_gcnMagic = SBig(m_gcnMagic);
m_gcnMagic = SBig(m_gcnMagic); m_debugMonOff = SBig(m_debugMonOff);
m_debugMonOff = SBig(m_debugMonOff); m_debugLoadAddr = SBig(m_debugLoadAddr);
m_debugLoadAddr = SBig(m_debugLoadAddr); m_dolOff = SBig(m_dolOff);
m_dolOff = SBig(m_dolOff); m_fstOff = SBig(m_fstOff);
m_fstOff = SBig(m_fstOff); m_fstSz = SBig(m_fstSz);
m_fstSz = SBig(m_fstSz); m_fstMaxSz = SBig(m_fstMaxSz);
m_fstMaxSz = SBig(m_fstMaxSz); m_fstMemoryAddress = SBig(m_fstMemoryAddress);
m_fstMemoryAddress = SBig(m_fstMemoryAddress); m_userPosition = SBig(m_userPosition);
m_userPosition = SBig(m_userPosition); m_userSz = SBig(m_userSz);
m_userSz = SBig(m_userSz); }
}
void write(IWriteStream& ws) const void write(IWriteStream& ws) const {
{ Header hs(*this);
Header hs(*this); hs.m_wiiMagic = SBig(hs.m_wiiMagic);
hs.m_wiiMagic = SBig(hs.m_wiiMagic); hs.m_gcnMagic = SBig(hs.m_gcnMagic);
hs.m_gcnMagic = SBig(hs.m_gcnMagic); hs.m_debugMonOff = SBig(hs.m_debugMonOff);
hs.m_debugMonOff = SBig(hs.m_debugMonOff); hs.m_debugLoadAddr = SBig(hs.m_debugLoadAddr);
hs.m_debugLoadAddr = SBig(hs.m_debugLoadAddr); hs.m_dolOff = SBig(hs.m_dolOff);
hs.m_dolOff = SBig(hs.m_dolOff); hs.m_fstOff = SBig(hs.m_fstOff);
hs.m_fstOff = SBig(hs.m_fstOff); hs.m_fstSz = SBig(hs.m_fstSz);
hs.m_fstSz = SBig(hs.m_fstSz); hs.m_fstMaxSz = SBig(hs.m_fstMaxSz);
hs.m_fstMaxSz = SBig(hs.m_fstMaxSz); hs.m_fstMemoryAddress = SBig(hs.m_fstMemoryAddress);
hs.m_fstMemoryAddress = SBig(hs.m_fstMemoryAddress); hs.m_userPosition = SBig(hs.m_userPosition);
hs.m_userPosition = SBig(hs.m_userPosition); hs.m_userSz = SBig(hs.m_userSz);
hs.m_userSz = SBig(hs.m_userSz); ws.write(&hs, sizeof(hs));
ws.write(&hs, sizeof(hs)); }
}
}; };
/* Currently only kept for dolphin compatibility */ /* Currently only kept for dolphin compatibility */
struct BI2Header struct BI2Header {
{ int32_t m_debugMonitorSize;
int32_t m_debugMonitorSize; int32_t m_simMemSize;
int32_t m_simMemSize; uint32_t m_argOffset;
uint32_t m_argOffset; uint32_t m_debugFlag;
uint32_t m_debugFlag; uint32_t m_trkAddress;
uint32_t m_trkAddress; uint32_t m_trkSz;
uint32_t m_trkSz; uint32_t m_countryCode;
uint32_t m_countryCode; uint32_t m_unk1;
uint32_t m_unk1; uint32_t m_unk2;
uint32_t m_unk2; uint32_t m_unk3;
uint32_t m_unk3; uint32_t m_dolLimit;
uint32_t m_dolLimit; uint32_t m_unk4;
uint32_t m_unk4; uint8_t padding2[0x1FD0];
uint8_t padding2[0x1FD0];
void read(IReadStream& rs) void read(IReadStream& rs) {
{ memset(this, 0, sizeof(*this));
memset(this, 0, sizeof(*this)); rs.read(this, sizeof(*this));
rs.read(this, sizeof(*this)); m_debugMonitorSize = SBig(m_debugMonitorSize);
m_debugMonitorSize = SBig(m_debugMonitorSize); m_simMemSize = SBig(m_simMemSize);
m_simMemSize = SBig(m_simMemSize); m_argOffset = SBig(m_argOffset);
m_argOffset = SBig(m_argOffset); m_debugFlag = SBig(m_debugFlag);
m_debugFlag = SBig(m_debugFlag); m_trkAddress = SBig(m_trkAddress);
m_trkAddress = SBig(m_trkAddress); m_trkSz = SBig(m_trkSz);
m_trkSz = SBig(m_trkSz); m_countryCode = SBig(m_countryCode);
m_countryCode = SBig(m_countryCode); m_unk1 = SBig(m_unk1);
m_unk1 = SBig(m_unk1); m_unk2 = SBig(m_unk2);
m_unk2 = SBig(m_unk2); m_unk3 = SBig(m_unk3);
m_unk3 = SBig(m_unk3); m_dolLimit = SBig(m_dolLimit);
m_dolLimit = SBig(m_dolLimit); m_unk4 = SBig(m_unk4);
m_unk4 = SBig(m_unk4); }
}
void write(IWriteStream& ws) const void write(IWriteStream& ws) const {
{ BI2Header h = *this;
BI2Header h = *this; h.m_debugMonitorSize = SBig(h.m_debugMonitorSize);
h.m_debugMonitorSize = SBig(h.m_debugMonitorSize); h.m_simMemSize = SBig(h.m_simMemSize);
h.m_simMemSize = SBig(h.m_simMemSize); h.m_argOffset = SBig(h.m_argOffset);
h.m_argOffset = SBig(h.m_argOffset); h.m_debugFlag = SBig(h.m_debugFlag);
h.m_debugFlag = SBig(h.m_debugFlag); h.m_trkAddress = SBig(h.m_trkAddress);
h.m_trkAddress = SBig(h.m_trkAddress); h.m_trkSz = SBig(h.m_trkSz);
h.m_trkSz = SBig(h.m_trkSz); h.m_countryCode = SBig(h.m_countryCode);
h.m_countryCode = SBig(h.m_countryCode); h.m_unk1 = SBig(h.m_unk1);
h.m_unk1 = SBig(h.m_unk1); h.m_unk2 = SBig(h.m_unk2);
h.m_unk2 = SBig(h.m_unk2); h.m_unk3 = SBig(h.m_unk3);
h.m_unk3 = SBig(h.m_unk3); h.m_dolLimit = SBig(h.m_dolLimit);
h.m_dolLimit = SBig(h.m_dolLimit); h.m_unk4 = SBig(h.m_unk4);
h.m_unk4 = SBig(h.m_unk4); ws.write(&h, sizeof(h));
ws.write(&h, sizeof(h)); }
}
}; };
struct ExtractionContext; struct ExtractionContext;
class DiscBase class IPartition;
{ class DiscBase;
class Node {
public: public:
virtual ~DiscBase() = default; enum class Kind { File, Directory };
class IPartition private:
{ friend class IPartition;
public: const IPartition& m_parent;
virtual ~IPartition() = default; Kind m_kind;
struct DOLHeader
{
uint32_t textOff[7];
uint32_t dataOff[11];
uint32_t textStarts[7];
uint32_t dataStarts[11];
uint32_t textSizes[7];
uint32_t dataSizes[11];
uint32_t bssStart;
uint32_t bssSize;
uint32_t entryPoint;
};
class Node uint64_t m_discOffset;
{ uint64_t m_discLength;
public: std::string m_name;
enum class Kind
{
File,
Directory
};
private:
friend class IPartition;
const IPartition& m_parent;
Kind m_kind;
uint64_t m_discOffset; std::vector<Node>::iterator m_childrenBegin;
uint64_t m_discLength; std::vector<Node>::iterator m_childrenEnd;
std::string m_name;
std::vector<Node>::iterator m_childrenBegin;
std::vector<Node>::iterator m_childrenEnd;
public:
Node(const IPartition& parent, const FSTNode& node, const char* name)
: m_parent(parent),
m_kind(node.isDir() ? Kind::Directory : Kind::File),
m_discOffset(parent.normalizeOffset(node.getOffset())),
m_discLength(node.getLength()),
m_name(name) {}
inline Kind getKind() const {return m_kind;}
inline const std::string& getName() const {return m_name;}
inline uint64_t size() const {return m_discLength;}
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset=0) const
{
if (m_kind != Kind::File)
{
LogModule.report(logvisor::Error, "unable to stream a non-file %s", m_name.c_str());
return std::unique_ptr<IPartReadStream>();
}
return m_parent.beginReadStream(m_discOffset + offset);
}
std::unique_ptr<uint8_t[]> getBuf() const
{
if (m_kind != Kind::File)
{
LogModule.report(logvisor::Error, "unable to buffer a non-file %s", m_name.c_str());
return std::unique_ptr<uint8_t[]>();
}
uint8_t* buf = new uint8_t[m_discLength];
beginReadStream()->read(buf, m_discLength);
return std::unique_ptr<uint8_t[]>(buf);
}
inline std::vector<Node>::iterator rawBegin() const {return m_childrenBegin;}
inline std::vector<Node>::iterator rawEnd() const {return m_childrenEnd;}
class DirectoryIterator : std::iterator<std::forward_iterator_tag, Node>
{
friend class Node;
std::vector<Node>::iterator m_it;
DirectoryIterator(const std::vector<Node>::iterator& it)
: m_it(it) {}
public:
inline bool operator!=(const DirectoryIterator& other) {return m_it != other.m_it;}
inline bool operator==(const DirectoryIterator& other) {return m_it == other.m_it;}
inline DirectoryIterator& operator++()
{
if (m_it->m_kind == Kind::Directory)
m_it = m_it->rawEnd();
else
++m_it;
return *this;
}
inline Node& operator*() {return *m_it;}
inline Node* operator->() {return &*m_it;}
};
inline DirectoryIterator begin() const {return DirectoryIterator(m_childrenBegin);}
inline DirectoryIterator end() const {return DirectoryIterator(m_childrenEnd);}
inline DirectoryIterator find(const std::string& name) const
{
if (m_kind == Kind::Directory)
{
DirectoryIterator it=begin();
for (; it != end() ; ++it)
{
if (!it->getName().compare(name))
return it;
}
return it;
}
return end();
}
bool extractToDirectory(const SystemString& basePath, const ExtractionContext& ctx) const;
};
protected:
Header m_header;
BI2Header m_bi2Header;
uint64_t m_dolOff;
uint64_t m_fstOff;
uint64_t m_fstSz;
uint64_t m_apploaderSz;
std::vector<Node> m_nodes;
void parseFST(IPartReadStream& s);
std::vector<FSTNode> m_buildNodes;
std::vector<std::string> m_buildNames;
size_t m_buildNameOff = 0;
void recursiveBuildNodes(const SystemChar* dirIn, std::function<void(void)> incParents);
uint64_t m_dolSz;
void parseDOL(IPartReadStream& s);
const DiscBase& m_parent;
PartitionKind m_kind;
uint64_t m_offset;
bool m_isWii;
public:
mutable size_t m_curNodeIdx = 0;
float getProgressFactor() const { return getNodeCount() ? m_curNodeIdx / float(getNodeCount()) : 0.f; }
float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const
{
if (!getNodeCount())
return 0.f;
if (totalBytes)
return (m_curNodeIdx + (curByte / float(totalBytes))) / float(getNodeCount());
else
return m_curNodeIdx / float(getNodeCount());
}
IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset)
: m_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii) {}
virtual uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset;}
inline PartitionKind getKind() const {return m_kind;}
inline bool isWii() const {return m_isWii;}
inline uint64_t getDiscOffset() const {return m_offset;}
virtual std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset=0) const=0;
inline std::unique_ptr<IPartReadStream> beginDOLReadStream(uint64_t offset=0) const
{return beginReadStream(m_dolOff + offset);}
inline std::unique_ptr<IPartReadStream> beginFSTReadStream(uint64_t offset=0) const
{return beginReadStream(m_fstOff + offset);}
inline std::unique_ptr<IPartReadStream> beginApploaderReadStream(uint64_t offset=0) const
{return beginReadStream(0x2440 + offset);}
inline const Node& getFSTRoot() const {return m_nodes[0];}
inline Node& getFSTRoot() {return m_nodes[0];}
bool extractToDirectory(const SystemString& path, const ExtractionContext& ctx);
inline uint64_t getDOLSize() const {return m_dolSz;}
inline std::unique_ptr<uint8_t[]> getDOLBuf() const
{
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_dolSz]);
beginDOLReadStream()->read(buf.get(), m_dolSz);
return buf;
}
inline uint64_t getFSTSize() const {return m_fstSz;}
inline std::unique_ptr<uint8_t[]> getFSTBuf() const
{
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_fstSz]);
beginFSTReadStream()->read(buf.get(), m_fstSz);
return buf;
}
inline uint64_t getApploaderSize() const {return m_apploaderSz;}
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const
{
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_apploaderSz]);
beginApploaderReadStream()->read(buf.get(), m_apploaderSz);
return buf;
}
inline size_t getNodeCount() const { return m_nodes.size(); }
inline const Header& getHeader() const { return m_header; }
inline const BI2Header& getBI2() const { return m_bi2Header; }
virtual bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const { return true; }
};
protected:
std::unique_ptr<IDiscIO> m_discIO;
Header m_header;
std::vector<std::unique_ptr<IPartition>> m_partitions;
public: public:
DiscBase(std::unique_ptr<IDiscIO>&& dio, bool& err) Node(const IPartition& parent, const FSTNode& node, std::string_view name);
: m_discIO(std::move(dio)), m_header(*m_discIO, err) {} Kind getKind() const { return m_kind; }
std::string_view getName() const { return m_name; }
uint64_t size() const { return m_discLength; }
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset = 0) const;
std::unique_ptr<uint8_t[]> getBuf() const;
std::vector<Node>::iterator rawBegin() const { return m_childrenBegin; }
std::vector<Node>::iterator rawEnd() const { return m_childrenEnd; }
inline const Header& getHeader() const {return m_header;} class DirectoryIterator {
inline const IDiscIO& getDiscIO() const {return *m_discIO;} friend class Node;
inline size_t getPartitonNodeCount(size_t partition = 0) const std::vector<Node>::iterator m_it;
{ DirectoryIterator(const std::vector<Node>::iterator& it) : m_it(it) {}
if (partition > m_partitions.size())
return -1; public:
return m_partitions[partition]->getNodeCount(); using iterator_category = std::forward_iterator_tag;
using value_type = Node;
using difference_type = std::ptrdiff_t;
using pointer = Node*;
using reference = Node&;
bool operator==(const DirectoryIterator& other) const { return m_it == other.m_it; }
bool operator!=(const DirectoryIterator& other) const { return !operator==(other); }
DirectoryIterator& operator++() {
if (m_it->m_kind == Kind::Directory)
m_it = m_it->rawEnd();
else
++m_it;
return *this;
} }
Node& operator*() { return *m_it; }
inline IPartition* getDataPartition() const Node& operator*() const { return *m_it; }
{ Node* operator->() { return &*m_it; }
for (const std::unique_ptr<IPartition>& part : m_partitions) const Node* operator->() const { return &*m_it; }
if (part->getKind() == PartitionKind::Data) };
return part.get(); DirectoryIterator begin() const { return DirectoryIterator(m_childrenBegin); }
return nullptr; DirectoryIterator end() const { return DirectoryIterator(m_childrenEnd); }
DirectoryIterator find(std::string_view name) const {
if (m_kind == Kind::Directory) {
DirectoryIterator it = begin();
for (; it != end(); ++it) {
if (it->getName() == name)
return it;
}
return it;
} }
return end();
}
inline IPartition* getUpdatePartition() bool extractToDirectory(std::string_view basePath, const ExtractionContext& ctx) const;
{
for (const std::unique_ptr<IPartition>& part : m_partitions)
if (part->getKind() == PartitionKind::Update)
return part.get();
return nullptr;
}
inline void extractToDirectory(const SystemString& path, const ExtractionContext& ctx)
{
for (std::unique_ptr<IPartition>& part : m_partitions)
part->extractToDirectory(path, ctx);
}
virtual bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const=0;
}; };
class DiscBuilderBase class IPartition {
{
friend class DiscMergerWii;
public: public:
class PartitionBuilderBase virtual ~IPartition() = default;
{ struct DOLHeader {
public: uint32_t textOff[7];
virtual ~PartitionBuilderBase() = default; uint32_t dataOff[11];
protected: uint32_t textStarts[7];
std::unordered_map<SystemString, std::pair<uint64_t,uint64_t>> m_fileOffsetsSizes; uint32_t dataStarts[11];
std::vector<FSTNode> m_buildNodes; uint32_t textSizes[7];
std::vector<std::string> m_buildNames; uint32_t dataSizes[11];
size_t m_buildNameOff = 0; uint32_t bssStart;
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0; uint32_t bssSize;
virtual uint32_t packOffset(uint64_t offset) const=0; uint32_t entryPoint;
};
void recursiveBuildNodesPre(const SystemChar* dirIn);
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn);
bool recursiveBuildFST(const SystemChar* dirIn,
std::function<void(void)> incParents);
void recursiveMergeNodesPre(const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn);
bool recursiveMergeNodes(IPartWriteStream& ws, bool system,
const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn,
const SystemString& keyPath);
bool recursiveMergeFST(const DiscBase::IPartition::Node* nodeIn,
const SystemChar* dirIn, std::function<void(void)> incParents,
const SystemString& keyPath);
static bool RecursiveCalculateTotalSize(uint64_t& totalSz,
const DiscBase::IPartition::Node* nodeIn,
const SystemChar* dirIn);
void addBuildName(const SystemString& str)
{
SystemUTF8View utf8View(str);
m_buildNames.push_back(utf8View.utf8_str());
m_buildNameOff += str.size() + 1;
}
DiscBuilderBase& m_parent;
PartitionKind m_kind;
uint64_t m_dolOffset = 0;
uint64_t m_dolSize = 0;
bool m_isWii;
public:
PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii)
: m_parent(parent), m_kind(kind), m_isWii(isWii)
{}
virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)=0;
bool buildFromDirectory(IPartWriteStream& ws,
const SystemChar* dirIn);
static uint64_t CalculateTotalSizeBuild(const SystemChar* dirIn,
PartitionKind kind, bool isWii);
bool mergeFromDirectory(IPartWriteStream& ws,
const DiscBase::IPartition* partIn,
const SystemChar* dirIn);
static uint64_t CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
const SystemChar* dirIn);
};
protected: protected:
SystemString m_outPath; Header m_header;
std::unique_ptr<IFileIO> m_fileIO; BI2Header m_bi2Header;
std::vector<std::unique_ptr<PartitionBuilderBase>> m_partitions; uint64_t m_dolOff;
int64_t m_discCapacity; uint64_t m_fstOff;
uint64_t m_fstSz;
uint64_t m_apploaderSz;
std::vector<Node> m_nodes;
void parseFST(IPartReadStream& s);
std::vector<FSTNode> m_buildNodes;
std::vector<std::string> m_buildNames;
size_t m_buildNameOff = 0;
uint64_t m_dolSz;
void parseDOL(IPartReadStream& s);
const DiscBase& m_parent;
PartitionKind m_kind;
uint64_t m_offset;
bool m_isWii;
public: public:
FProgress m_progressCB; mutable size_t m_curNodeIdx = 0;
size_t m_progressIdx = 0; float getProgressFactor() const { return getNodeCount() ? m_curNodeIdx / float(getNodeCount()) : 0.f; }
size_t m_progressTotal = 0; float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const {
float getProgressFactor() const if (!getNodeCount())
{ return 0.f;
return m_progressTotal ? std::min(1.f, m_progressIdx / float(m_progressTotal)) : 0.f;
}
float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const
{
if (!m_progressTotal)
return 0.f;
if (totalBytes) if (totalBytes)
return (m_progressIdx + (curByte / float(totalBytes))) / float(m_progressTotal); return (m_curNodeIdx + (curByte / float(totalBytes))) / float(getNodeCount());
else else
return m_progressIdx / float(m_progressTotal); return m_curNodeIdx / float(getNodeCount());
} }
virtual ~DiscBuilderBase() = default; IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset)
DiscBuilderBase(const SystemChar* outPath, : m_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii) {}
int64_t discCapacity, FProgress progressCB) virtual uint64_t normalizeOffset(uint64_t anOffset) const { return anOffset; }
: m_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)), PartitionKind getKind() const { return m_kind; }
m_discCapacity(discCapacity), m_progressCB(progressCB) {} bool isWii() const { return m_isWii; }
DiscBuilderBase(DiscBuilderBase&&) = default; uint64_t getDiscOffset() const { return m_offset; }
DiscBuilderBase& operator=(DiscBuilderBase&&) = default; virtual std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset = 0) const = 0;
std::unique_ptr<IPartReadStream> beginDOLReadStream(uint64_t offset = 0) const {
return beginReadStream(m_dolOff + offset);
}
std::unique_ptr<IPartReadStream> beginFSTReadStream(uint64_t offset = 0) const {
return beginReadStream(m_fstOff + offset);
}
std::unique_ptr<IPartReadStream> beginApploaderReadStream(uint64_t offset = 0) const {
return beginReadStream(0x2440 + offset);
}
const Node& getFSTRoot() const { return m_nodes[0]; }
Node& getFSTRoot() { return m_nodes[0]; }
bool extractToDirectory(std::string_view path, const ExtractionContext& ctx);
IFileIO& getFileIO() { return *m_fileIO; } uint64_t getDOLSize() const { return m_dolSz; }
std::unique_ptr<uint8_t[]> getDOLBuf() const {
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_dolSz]);
beginDOLReadStream()->read(buf.get(), m_dolSz);
return buf;
}
uint64_t getFSTSize() const { return m_fstSz; }
std::unique_ptr<uint8_t[]> getFSTBuf() const {
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_fstSz]);
beginFSTReadStream()->read(buf.get(), m_fstSz);
return buf;
}
uint64_t getApploaderSize() const { return m_apploaderSz; }
std::unique_ptr<uint8_t[]> getApploaderBuf() const {
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_apploaderSz]);
beginApploaderReadStream()->read(buf.get(), m_apploaderSz);
return buf;
}
size_t getNodeCount() const { return m_nodes.size(); }
const Header& getHeader() const { return m_header; }
const BI2Header& getBI2() const { return m_bi2Header; }
virtual bool extractCryptoFiles(std::string_view path, const ExtractionContext& ctx) const { return true; }
bool extractSysFiles(std::string_view path, const ExtractionContext& ctx) const;
}; };
using Partition = DiscBase::IPartition; class DiscBase {
using Node = Partition::Node; public:
virtual ~DiscBase() = default;
} protected:
std::unique_ptr<IDiscIO> m_discIO;
Header m_header;
std::vector<std::unique_ptr<IPartition>> m_partitions;
#endif // __NOD_DISC_BASE__ public:
DiscBase(std::unique_ptr<IDiscIO>&& dio, bool& err) : m_discIO(std::move(dio)), m_header(*m_discIO, err) {}
const Header& getHeader() const { return m_header; }
const IDiscIO& getDiscIO() const { return *m_discIO; }
size_t getPartitionNodeCount(size_t partition = 0) const {
if (partition >= m_partitions.size()) {
return -1;
}
return m_partitions[partition]->getNodeCount();
}
IPartition* getDataPartition() {
for (const std::unique_ptr<IPartition>& part : m_partitions)
if (part->getKind() == PartitionKind::Data)
return part.get();
return nullptr;
}
IPartition* getUpdatePartition() {
for (const std::unique_ptr<IPartition>& part : m_partitions)
if (part->getKind() == PartitionKind::Update)
return part.get();
return nullptr;
}
void extractToDirectory(std::string_view path, const ExtractionContext& ctx) {
for (std::unique_ptr<IPartition>& part : m_partitions)
part->extractToDirectory(path, ctx);
}
virtual bool extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const = 0;
};
class DiscBuilderBase {
friend class DiscMergerWii;
public:
class PartitionBuilderBase {
public:
virtual ~PartitionBuilderBase() = default;
protected:
std::unordered_map<std::string, std::pair<uint64_t, uint64_t>> m_fileOffsetsSizes;
std::vector<FSTNode> m_buildNodes;
std::vector<std::string> m_buildNames;
size_t m_buildNameOff = 0;
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) = 0;
virtual uint32_t packOffset(uint64_t offset) const = 0;
void recursiveBuildNodesPre(std::string_view dirIn);
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, std::string_view dirIn);
bool recursiveBuildFST(std::string_view dirIn, std::function<void(void)> incParents, size_t parentDirIdx);
void recursiveMergeNodesPre(const Node* nodeIn, std::string_view dirIn);
bool recursiveMergeNodes(IPartWriteStream& ws, bool system, const Node* nodeIn, std::string_view dirIn,
std::string_view keyPath);
bool recursiveMergeFST(const Node* nodeIn, std::string_view dirIn, std::function<void(void)> incParents,
size_t parentDirIdx, std::string_view keyPath);
static bool RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, std::string_view dirIn);
void addBuildName(std::string_view str) {
UTF8ToSJIS nameView(str);
m_buildNames.emplace_back(nameView.str());
m_buildNameOff += nameView.str().size() + 1;
}
DiscBuilderBase& m_parent;
PartitionKind m_kind;
uint64_t m_dolOffset = 0;
uint64_t m_dolSize = 0;
bool m_isWii;
public:
PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii)
: m_parent(parent), m_kind(kind), m_isWii(isWii) {}
virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset) = 0;
bool buildFromDirectory(IPartWriteStream& ws, std::string_view dirIn);
static std::optional<uint64_t> CalculateTotalSizeBuild(std::string_view dirIn, PartitionKind kind, bool isWii);
bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, std::string_view dirIn);
static std::optional<uint64_t> CalculateTotalSizeMerge(const IPartition* partIn, std::string_view dirIn);
};
protected:
std::string m_outPath;
std::unique_ptr<IFileIO> m_fileIO;
std::vector<std::unique_ptr<PartitionBuilderBase>> m_partitions;
int64_t m_discCapacity;
public:
FProgress m_progressCB;
size_t m_progressIdx = 0;
size_t m_progressTotal = 0;
float getProgressFactor() const {
return m_progressTotal ? std::min(1.f, m_progressIdx / float(m_progressTotal)) : 0.f;
}
float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const {
if (!m_progressTotal)
return 0.f;
if (totalBytes)
return (m_progressIdx + (curByte / float(totalBytes))) / float(m_progressTotal);
else
return m_progressIdx / float(m_progressTotal);
}
virtual ~DiscBuilderBase() = default;
DiscBuilderBase(std::string_view outPath, int64_t discCapacity, FProgress progressCB)
: m_outPath(outPath)
, m_fileIO(NewFileIO(outPath, discCapacity))
, m_discCapacity(discCapacity)
, m_progressCB(std::move(progressCB)) {}
DiscBuilderBase(DiscBuilderBase&&) = default;
DiscBuilderBase& operator=(DiscBuilderBase&&) = default;
IFileIO& getFileIO() { return *m_fileIO; }
};
} // namespace nod

View File

@@ -1,41 +1,36 @@
#ifndef __NOD_DISC_GCN__ #pragma once
#define __NOD_DISC_GCN__
#include "DiscBase.hpp" #include "nod/DiscBase.hpp"
namespace nod namespace nod {
{
class DiscBuilderGCN; class DiscBuilderGCN;
class DiscGCN : public DiscBase class DiscGCN : public DiscBase {
{ friend class DiscMergerGCN;
friend class DiscMergerGCN; DiscBuilderGCN makeMergeBuilder(std::string_view outPath, FProgress progressCB);
DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB);
public: public:
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err); DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err);
bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const; bool extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const override;
}; };
class DiscBuilderGCN : public DiscBuilderBase class DiscBuilderGCN : public DiscBuilderBase {
{ friend class DiscMergerGCN;
friend class DiscMergerGCN;
public: public:
DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB); DiscBuilderGCN(std::string_view outPath, FProgress progressCB);
EBuildResult buildFromDirectory(const SystemChar* dirIn); EBuildResult buildFromDirectory(std::string_view dirIn);
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn); static std::optional<uint64_t> CalculateTotalSizeRequired(std::string_view dirIn);
}; };
class DiscMergerGCN class DiscMergerGCN {
{ DiscGCN& m_sourceDisc;
DiscGCN& m_sourceDisc; DiscBuilderGCN m_builder;
DiscBuilderGCN m_builder;
public: public:
DiscMergerGCN(const SystemChar* outPath, DiscGCN& sourceDisc, FProgress progressCB); DiscMergerGCN(std::string_view outPath, DiscGCN& sourceDisc, FProgress progressCB);
EBuildResult mergeFromDirectory(const SystemChar* dirIn); EBuildResult mergeFromDirectory(std::string_view dirIn);
static uint64_t CalculateTotalSizeRequired(DiscGCN& sourceDisc, const SystemChar* dirIn); static std::optional<uint64_t> CalculateTotalSizeRequired(DiscGCN& sourceDisc, std::string_view dirIn);
}; };
} } // namespace nod
#endif // __NOD_DISC_GCN__

View File

@@ -1,41 +1,32 @@
#ifndef __NOD_DISC_WII__ #pragma once
#define __NOD_DISC_WII__
#include "DiscBase.hpp" #include "nod/DiscBase.hpp"
namespace nod namespace nod {
{
class DiscBuilderWii; class DiscBuilderWii;
class DiscWii : public DiscBase class DiscWii : public DiscBase {
{
public: public:
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err); DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
DiscBuilderWii makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB); DiscBuilderWii makeMergeBuilder(std::string_view outPath, bool dualLayer, FProgress progressCB);
bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const; bool extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const override;
}; };
class DiscBuilderWii : public DiscBuilderBase class DiscBuilderWii : public DiscBuilderBase {
{
bool m_dualLayer;
public: public:
DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB); DiscBuilderWii(std::string_view outPath, bool dualLayer, FProgress progressCB);
EBuildResult buildFromDirectory(const SystemChar* dirIn); EBuildResult buildFromDirectory(std::string_view dirIn);
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer); static std::optional<uint64_t> CalculateTotalSizeRequired(std::string_view dirIn, bool& dualLayer);
}; };
class DiscMergerWii class DiscMergerWii {
{ DiscWii& m_sourceDisc;
DiscWii& m_sourceDisc; DiscBuilderWii m_builder;
DiscBuilderWii m_builder;
public: public:
DiscMergerWii(const SystemChar* outPath, DiscWii& sourceDisc, DiscMergerWii(std::string_view outPath, DiscWii& sourceDisc, bool dualLayer, FProgress progressCB);
bool dualLayer, FProgress progressCB); EBuildResult mergeFromDirectory(std::string_view dirIn);
EBuildResult mergeFromDirectory(const SystemChar* dirIn); static std::optional<uint64_t> CalculateTotalSizeRequired(DiscWii& sourceDisc, std::string_view dirIn, bool& dualLayer);
static uint64_t CalculateTotalSizeRequired(DiscWii& sourceDisc, const SystemChar* dirIn,
bool& dualLayer);
}; };
} } // namespace nod
#endif // __NOD_DISC_WII__

View File

@@ -1,75 +1,69 @@
#ifndef __NOD_IDISC_IO__ #pragma once
#define __NOD_IDISC_IO__
#include <cstdint>
#include <cstdio>
#include <memory> #include <memory>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#if NOD_ATHENA #if NOD_ATHENA
#include <athena/IStreamReader.hpp> #include <athena/IStreamReader.hpp>
#include <athena/IStreamWriter.hpp> #include <athena/IStreamWriter.hpp>
#endif #endif
namespace nod namespace nod {
{
struct IReadStream struct IReadStream {
{ virtual ~IReadStream() = default;
virtual ~IReadStream() = default; virtual uint64_t read(void* buf, uint64_t length) = 0;
virtual uint64_t read(void* buf, uint64_t length)=0; virtual void seek(int64_t offset, int whence = SEEK_SET) = 0;
virtual void seek(int64_t offset, int whence=SEEK_SET)=0; virtual uint64_t position() const = 0;
virtual uint64_t position() const=0;
}; };
struct IWriteStream struct IWriteStream {
{ virtual ~IWriteStream() = default;
virtual ~IWriteStream() = default; virtual uint64_t write(const void* buf, uint64_t length) = 0;
virtual uint64_t write(const void* buf, uint64_t length)=0;
}; };
class IDiscIO class IDiscIO {
{
public: public:
virtual ~IDiscIO() = default; virtual ~IDiscIO() = default;
virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset=0) const=0; virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset = 0) const = 0;
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset=0) const=0; virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset = 0) const = 0;
virtual bool hasWiiCrypto() const { return true; } /* NFS overrides this to false */
}; };
struct IPartReadStream : IReadStream struct IPartReadStream : IReadStream {
{ ~IPartReadStream() override = default;
virtual ~IPartReadStream() = default;
}; };
struct IPartWriteStream : IWriteStream struct IPartWriteStream : IWriteStream {
{ ~IPartWriteStream() override = default;
virtual ~IPartWriteStream() = default; virtual void close() = 0;
virtual void close()=0; virtual uint64_t position() const = 0;
virtual uint64_t position() const=0;
}; };
#if NOD_ATHENA #if NOD_ATHENA
class AthenaPartReadStream : public athena::io::IStreamReader class AthenaPartReadStream : public athena::io::IStreamReader {
{ std::unique_ptr<IPartReadStream> m_rs;
std::unique_ptr<IPartReadStream> m_rs;
public:
AthenaPartReadStream(std::unique_ptr<IPartReadStream>&& rs) : m_rs(std::move(rs)) {}
inline void seek(atInt64 off, athena::SeekOrigin origin) public:
{ AthenaPartReadStream(std::unique_ptr<IPartReadStream>&& rs) : m_rs(std::move(rs)) {}
if (origin == athena::Begin)
m_rs->seek(off, SEEK_SET); void seek(atInt64 off, athena::SeekOrigin origin) override {
else if (origin == athena::Current) if (origin == athena::SeekOrigin::Begin) {
m_rs->seek(off, SEEK_CUR); m_rs->seek(off, SEEK_SET);
} else if (origin == athena::SeekOrigin::Current) {
m_rs->seek(off, SEEK_CUR);
} }
inline atUint64 position() const {return m_rs->position();} }
inline atUint64 length() const {return 0;} atUint64 position() const override { return m_rs->position(); }
inline atUint64 readUBytesToBuf(void* buf, atUint64 sz) {m_rs->read(buf, sz); return sz;} atUint64 length() const override { return 0; }
atUint64 readUBytesToBuf(void* buf, atUint64 sz) override {
m_rs->read(buf, sz);
return sz;
}
}; };
#endif #endif
} } // namespace nod
#endif // __NOD_IDISC_IO__

View File

@@ -1,87 +1,74 @@
#ifndef __NOD_IFILE_IO__ #pragma once
#define __NOD_IFILE_IO__
#include <memory> #include <cstdint>
#include <functional> #include <functional>
#include <stdlib.h> #include <memory>
#include "IDiscIO.hpp"
#include "Util.hpp"
namespace nod #include "nod/IDiscIO.hpp"
{ #include "nod/Util.hpp"
class IFileIO #include <logvisor/logvisor.hpp>
{
namespace nod {
class IFileIO {
public: public:
virtual ~IFileIO() = default; virtual ~IFileIO() = default;
virtual bool exists()=0; virtual bool exists() = 0;
virtual uint64_t size()=0; virtual uint64_t size() = 0;
struct IWriteStream : nod::IWriteStream struct IWriteStream : nod::IWriteStream {
{ uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length) {
uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length) uint64_t read = 0;
{ uint8_t buf[0x7c00];
uint64_t read = 0; while (length) {
uint8_t buf[0x7c00]; uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
while (length) uint64_t readSz = discio.read(buf, thisSz);
{ if (thisSz != readSz) {
uint64_t thisSz = nod::min(uint64_t(0x7c00), length); LogModule.report(logvisor::Error, FMT_STRING("unable to read enough from disc"));
uint64_t readSz = discio.read(buf, thisSz); return read;
if (thisSz != readSz)
{
LogModule.report(logvisor::Error, "unable to read enough from disc");
return read;
}
if (write(buf, readSz) != readSz)
{
LogModule.report(logvisor::Error, "unable to write in file");
return read;
}
length -= thisSz;
read += thisSz;
}
return read;
} }
uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length, const std::function<void(float)>& prog) if (write(buf, readSz) != readSz) {
{ LogModule.report(logvisor::Error, FMT_STRING("unable to write in file"));
uint64_t read = 0; return read;
uint8_t buf[0x7c00];
uint64_t total = length;
while (length)
{
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
uint64_t readSz = discio.read(buf, thisSz);
if (thisSz != readSz)
{
LogModule.report(logvisor::Error, "unable to read enough from disc");
return read;
}
if (write(buf, readSz) != readSz)
{
LogModule.report(logvisor::Error, "unable to write in file");
return read;
}
length -= thisSz;
read += thisSz;
prog(read / float(total));
}
return read;
} }
}; length -= thisSz;
virtual std::unique_ptr<IWriteStream> beginWriteStream() const=0; read += thisSz;
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const=0; }
return read;
}
uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length, const std::function<void(float)>& prog) {
uint64_t read = 0;
uint8_t buf[0x7c00];
uint64_t total = length;
while (length) {
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
uint64_t readSz = discio.read(buf, thisSz);
if (thisSz != readSz) {
LogModule.report(logvisor::Error, FMT_STRING("unable to read enough from disc"));
return read;
}
if (write(buf, readSz) != readSz) {
LogModule.report(logvisor::Error, FMT_STRING("unable to write in file"));
return read;
}
length -= thisSz;
read += thisSz;
prog(read / float(total));
}
return read;
}
};
virtual std::unique_ptr<IWriteStream> beginWriteStream() const = 0;
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const = 0;
struct IReadStream : nod::IReadStream struct IReadStream : nod::IReadStream {
{ virtual uint64_t copyToDisc(struct IPartWriteStream& discio, uint64_t length) = 0;
virtual uint64_t copyToDisc(struct IPartWriteStream& discio, uint64_t length)=0; };
}; virtual std::unique_ptr<IReadStream> beginReadStream() const = 0;
virtual std::unique_ptr<IReadStream> beginReadStream() const=0; virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const = 0;
virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const=0;
}; };
std::unique_ptr<IFileIO> NewFileIO(const SystemString& path, int64_t maxWriteSize=-1); std::unique_ptr<IFileIO> NewFileIO(std::string_view path, int64_t maxWriteSize = -1);
std::unique_ptr<IFileIO> NewFileIO(const SystemChar* path, int64_t maxWriteSize=-1);
} } // namespace nod
#endif // __NOD_IFILE_IO__

113
include/nod/OSUTF.h Normal file
View File

@@ -0,0 +1,113 @@
#ifdef __cplusplus
#include <array>
#include <cstdint>
#if !__cpp_char8_t
using char8_t = uint8_t;
#endif
#define OS_CONSTEXPR constexpr
extern "C" {
#else
#include <stdint.h>
#include <stdbool.h>
#define OS_CONSTEXPR
typedef uint8_t char8_t;
typedef uint16_t char16_t;
typedef uint32_t char32_t;
#endif
OS_CONSTEXPR inline bool IsSjisLeadByte(char8_t c) { return (c > 0x80 && c < 0xa0) || (c > 0xdf && c < 0xfd); }
OS_CONSTEXPR inline bool IsSjisTrailByte(char8_t c) { return c > 0x3f && c < 0xfd && c != 0x7f; }
char32_t OSSJISToUTF32(char16_t sjis);
char16_t OSUTF32ToSJIS(char32_t utf32);
char8_t OSUTF32ToAnsi(char32_t utf32);
char16_t* OSUTF32To16(char32_t utf32, char16_t* utf16);
char8_t* OSUTF32To8(char32_t utf32, char8_t* utf8);
const char16_t* OSUTF16To32(const char16_t* utf16, char32_t* utf32);
const char8_t* OSUTF8To32(const char8_t* utf8, char32_t* utf32);
#ifdef __cplusplus
}
class SJISToUTF8 {
private:
std::string out;
public:
SJISToUTF8(std::string_view sv) {
const auto* in = reinterpret_cast<const char8_t*>(sv.data());
const auto* end = in + sv.size();
std::array<char8_t, 4> u8arr{};
while (in < end) {
if (IsSjisLeadByte(*in)) {
char16_t sjis = static_cast<char16_t>(*in) << 8 | *(in + 1);
in += 2;
char32_t utf32 = OSSJISToUTF32(sjis);
if (utf32 == 0) {
continue;
}
char8_t* u8out = u8arr.data();
char8_t* u8end = OSUTF32To8(utf32, u8out);
if (u8end == nullptr) {
continue;
}
auto length = static_cast<size_t>(u8end - u8out);
out.append(std::string_view{reinterpret_cast<char*>(u8out), length});
} else {
out.push_back(static_cast<char>(*in++));
}
}
}
[[nodiscard]] const std::string& str() const { return out; }
[[nodiscard]] std::string& str() { return out; }
[[nodiscard]] const char* c_str() const { return out.c_str(); }
};
class UTF8ToSJIS {
private:
std::string out;
public:
UTF8ToSJIS(std::string_view sv) {
const auto* in = reinterpret_cast<const char8_t*>(sv.data());
const auto* end = in + sv.size();
while (in < end) {
char32_t utf32 = 0;
const char8_t* next = OSUTF8To32(in, &utf32);
if (next == nullptr) {
utf32 = *in;
in++;
} else {
in = next;
}
char16_t sjis = OSUTF32ToSJIS(utf32);
char8_t lead = (sjis >> 8) & 0xFF;
if (IsSjisLeadByte(lead)) {
out.push_back(static_cast<char>(lead));
}
out.push_back(static_cast<char>(sjis & 0xFF));
}
}
[[nodiscard]] const std::string& str() const { return out; }
[[nodiscard]] std::string& str() { return out; }
[[nodiscard]] const char* c_str() const { return out.c_str(); }
};
#endif

View File

@@ -1,8 +1,8 @@
#ifndef __NOD_UTIL_HPP__ #pragma once
#define __NOD_UTIL_HPP__
#if _WIN32 && UNICODE #if _WIN32
#include <wctype.h> #include <array>
#include <cwctype>
#include <direct.h> #include <direct.h>
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1 #define WIN32_LEAN_AND_MEAN 1
@@ -10,33 +10,39 @@
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX #define NOMINMAX
#endif #endif
#include <nowide/stackstring.hpp>
#include <windows.h> #include <windows.h>
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
#define WINDOWS_STORE 1
#else #else
#include <ctype.h> #define WINDOWS_STORE 0
#endif
#else
#include <cctype>
#include <cerrno>
#include <sys/file.h> #include <sys/file.h>
#include <unistd.h>
#include <errno.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/statvfs.h> #include <sys/statvfs.h>
#include <unistd.h>
#endif #endif
#include <sys/stat.h> #include <sys/stat.h>
#include <string>
#include <cstring>
#include <algorithm> #include <algorithm>
#include "logvisor/logvisor.hpp" #include <cstring>
#include <string>
#include <string_view>
#include <logvisor/logvisor.hpp>
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(disable : 4996) #pragma warning(disable : 4996)
#include <sys/stat.h> #include <sys/stat.h>
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
#endif #endif
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) #if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
#endif #endif
#if !defined(S_ISLNK) #if !defined(S_ISLNK)
@@ -47,118 +53,53 @@
#undef min #undef min
#undef max #undef max
namespace nod namespace nod {
{
/* define our own min/max to avoid MSVC BS */ /* define our own min/max to avoid MSVC BS */
template<typename T> template <typename T>
inline T min(T a, T b) { return a < b ? a : b; } constexpr T min(T a, T b) {
template<typename T> return a < b ? a : b;
inline T max(T a, T b) { return a > b ? a : b; } }
template <typename T>
constexpr T max(T a, T b) {
return a > b ? a : b;
}
/* template-based div for flexible typing and avoiding a library call */
template <typename T>
constexpr auto div(T a, T b) {
struct DivTp {
T quot, rem;
};
return DivTp{a / b, a % b};
}
/* Log Module */ /* Log Module */
extern logvisor::Module LogModule; extern logvisor::Module LogModule;
/* filesystem char type */ /* filesystem char type */
#if _WIN32 && UNICODE #if _WIN32
#define NOD_UCS2 1 static inline int Mkdir(const char* path, int) {
typedef struct _stat Sstat; const nowide::wstackstring str(path);
static inline int Mkdir(const wchar_t* path, int) {return _wmkdir(path);} return _wmkdir(str.get());
static inline int Stat(const wchar_t* path, Sstat* statout) {return _wstat(path, statout);} }
using Sstat = struct ::_stat64;
static inline int Stat(const char* path, Sstat* statout) {
const nowide::wstackstring wpath(path);
return _wstat64(wpath.get(), statout);
}
#else #else
static inline int Mkdir(const char* path, mode_t mode) { return mkdir(path, mode); }
typedef struct stat Sstat; typedef struct stat Sstat;
static inline int Mkdir(const char* path, mode_t mode) {return mkdir(path, mode);} static inline int Stat(const char* path, Sstat* statout) { return stat(path, statout); }
static inline int Stat(const char* path, Sstat* statout) {return stat(path, statout);}
#endif #endif
/* String-converting views */ static inline int StrCaseCmp(const char* str1, const char* str2) {
#if NOD_UCS2 #ifdef _MSC_VER
typedef wchar_t SystemChar; return _stricmp(str1, str2);
typedef std::wstring SystemString;
static inline void ToLower(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), towlower);}
static inline void ToUpper(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), towupper);}
static inline size_t StrLen(const SystemChar* str) {return wcslen(str);}
class SystemUTF8View
{
std::string m_utf8;
public:
explicit SystemUTF8View(const SystemString& str)
{
int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.size(), nullptr, 0, nullptr, nullptr);
m_utf8.assign(len, '\0');
WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.size(), &m_utf8[0], len, nullptr, nullptr);
}
inline const std::string& utf8_str() {return m_utf8;}
};
class SystemStringView
{
std::wstring m_sys;
public:
explicit SystemStringView(const std::string& str)
{
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), nullptr, 0);
m_sys.assign(len, L'\0');
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), &m_sys[0], len);
}
inline const std::wstring& sys_str() {return m_sys;}
};
#ifndef _S
#define _S(val) L ## val
#endif
#else #else
typedef char SystemChar; return strcasecmp(str1, str2);
typedef std::string SystemString;
static inline void ToLower(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), tolower);}
static inline void ToUpper(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), toupper);}
static inline size_t StrLen(const SystemChar* str) {return strlen(str);}
class SystemUTF8View
{
const std::string& m_utf8;
public:
explicit SystemUTF8View(const SystemString& str)
: m_utf8(str) {}
inline const std::string& utf8_str() {return m_utf8;}
};
class SystemStringView
{
const std::string& m_sys;
public:
explicit SystemStringView(const std::string& str)
: m_sys(str) {}
inline const std::string& sys_str() {return m_sys;}
};
#ifndef _S
#define _S(val) val
#endif
#endif
static inline void Unlink(const SystemChar* file)
{
#if NOD_UCS2
_wunlink(file);
#else
unlink(file);
#endif
}
static inline int StrCmp(const SystemChar* str1, const SystemChar* str2)
{
#if NOD_UCS2
return wcscmp(str1, str2);
#else
return strcmp(str1, str2);
#endif
}
static inline int StrCaseCmp(const SystemChar* str1, const SystemChar* str2)
{
#if NOD_UCS2
return _wcsicmp(str1, str2);
#else
return strcasecmp(str1, str2);
#endif #endif
} }
@@ -167,79 +108,71 @@ static inline int StrCaseCmp(const SystemChar* str1, const SystemChar* str2)
#undef bswap64 #undef bswap64
/* Type-sensitive byte swappers */ /* Type-sensitive byte swappers */
template <typename T> template <typename T>
static inline T bswap16(T val) static inline T bswap16(T val) {
{
#if __GNUC__ #if __GNUC__
return __builtin_bswap16(val); return __builtin_bswap16(val);
#elif _WIN32 #elif _WIN32
return _byteswap_ushort(val); return _byteswap_ushort(val);
#else #else
return (val = (val << 8) | ((val >> 8) & 0xFF)); return (val = (val << 8) | ((val >> 8) & 0xFF));
#endif #endif
} }
template <typename T> template <typename T>
static inline T bswap32(T val) static inline T bswap32(T val) {
{
#if __GNUC__ #if __GNUC__
return __builtin_bswap32(val); return __builtin_bswap32(val);
#elif _WIN32 #elif _WIN32
return _byteswap_ulong(val); return _byteswap_ulong(val);
#else #else
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16; val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8; val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
return val; return val;
#endif #endif
} }
template <typename T> template <typename T>
static inline T bswap64(T val) static inline T bswap64(T val) {
{
#if __GNUC__ #if __GNUC__
return __builtin_bswap64(val); return __builtin_bswap64(val);
#elif _WIN32 #elif _WIN32
return _byteswap_uint64(val); return _byteswap_uint64(val);
#else #else
return ((val & 0xFF00000000000000ULL) >> 56) | return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
((val & 0x00FF000000000000ULL) >> 40) | ((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
((val & 0x000000FF00000000ULL) >> 8) | ((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
((val & 0x00000000FF000000ULL) << 8) |
((val & 0x0000000000FF0000ULL) << 24) |
((val & 0x000000000000FF00ULL) << 40) |
((val & 0x00000000000000FFULL) << 56);
#endif #endif
} }
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
static inline int16_t SBig(int16_t val) {return bswap16(val);} static inline int16_t SBig(int16_t val) { return bswap16(val); }
static inline uint16_t SBig(uint16_t val) {return bswap16(val);} static inline uint16_t SBig(uint16_t val) { return bswap16(val); }
static inline int32_t SBig(int32_t val) {return bswap32(val);} static inline int32_t SBig(int32_t val) { return bswap32(val); }
static inline uint32_t SBig(uint32_t val) {return bswap32(val);} static inline uint32_t SBig(uint32_t val) { return bswap32(val); }
static inline int64_t SBig(int64_t val) {return bswap64(val);} static inline int64_t SBig(int64_t val) { return bswap64(val); }
static inline uint64_t SBig(uint64_t val) {return bswap64(val);} static inline uint64_t SBig(uint64_t val) { return bswap64(val); }
static inline int16_t SLittle(int16_t val) {return val;} static inline int16_t SLittle(int16_t val) { return val; }
static inline uint16_t SLittle(uint16_t val) {return val;} static inline uint16_t SLittle(uint16_t val) { return val; }
static inline int32_t SLittle(int32_t val) {return val;} static inline int32_t SLittle(int32_t val) { return val; }
static inline uint32_t SLittle(uint32_t val) {return val;} static inline uint32_t SLittle(uint32_t val) { return val; }
static inline int64_t SLittle(int64_t val) {return val;} static inline int64_t SLittle(int64_t val) { return val; }
static inline uint64_t SLittle(uint64_t val) {return val;} static inline uint64_t SLittle(uint64_t val) { return val; }
#else #else
static inline int16_t SLittle(int16_t val) {return bswap16(val);} static inline int16_t SLittle(int16_t val) { return bswap16(val); }
static inline uint16_t SLittle(uint16_t val) {return bswap16(val);} static inline uint16_t SLittle(uint16_t val) { return bswap16(val); }
static inline int32_t SLittle(int32_t val) {return bswap32(val);} static inline int32_t SLittle(int32_t val) { return bswap32(val); }
static inline uint32_t SLittle(uint32_t val) {return bswap32(val);} static inline uint32_t SLittle(uint32_t val) { return bswap32(val); }
static inline int64_t SLittle(int64_t val) {return bswap64(val);} static inline int64_t SLittle(int64_t val) { return bswap64(val); }
static inline uint64_t SLittle(uint64_t val) {return bswap64(val);} static inline uint64_t SLittle(uint64_t val) { return bswap64(val); }
static inline int16_t SBig(int16_t val) {return val;} static inline int16_t SBig(int16_t val) { return val; }
static inline uint16_t SBig(uint16_t val) {return val;} static inline uint16_t SBig(uint16_t val) { return val; }
static inline int32_t SBig(int32_t val) {return val;} static inline int32_t SBig(int32_t val) { return val; }
static inline uint32_t SBig(uint32_t val) {return val;} static inline uint32_t SBig(uint32_t val) { return val; }
static inline int64_t SBig(int64_t val) {return val;} static inline int64_t SBig(int64_t val) { return val; }
static inline uint64_t SBig(uint64_t val) {return val;} static inline uint64_t SBig(uint64_t val) { return val; }
#endif #endif
#ifndef ROUND_UP_32 #ifndef ROUND_UP_32
@@ -247,106 +180,81 @@ static inline uint64_t SBig(uint64_t val) {return val;}
#define ROUND_UP_16(val) (((val) + 15) & ~15) #define ROUND_UP_16(val) (((val) + 15) & ~15)
#endif #endif
enum class FileLockType enum class FileLockType { None = 0, Read, Write };
{ static inline FILE* Fopen(const char* path, const char* mode, FileLockType lock = FileLockType::None) {
None = 0, #if _MSC_VER
Read, const nowide::wstackstring wpath(path);
Write const nowide::wshort_stackstring wmode(mode);
}; FILE* fp = _wfopen(wpath.get(), wmode.get());
static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock=FileLockType::None) if (!fp)
{ return nullptr;
#if NOD_UCS2
FILE* fp = _wfopen(path, mode);
if (!fp)
return nullptr;
#else #else
FILE* fp = fopen(path, mode); FILE* fp = fopen(path, mode);
if (!fp) if (!fp)
return nullptr; return nullptr;
#endif #endif
if (lock != FileLockType::None) if (lock != FileLockType::None) {
{
#if _WIN32 #if _WIN32
OVERLAPPED ov = {}; OVERLAPPED ov = {};
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov); LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1,
&ov);
#else #else
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB)) if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
LogModule.report(logvisor::Error, "flock %s: %s", path, strerror(errno)); LogModule.report(logvisor::Error, FMT_STRING("flock {}: {}"), path, strerror(errno));
#endif #endif
} }
return fp; return fp;
} }
static inline int FSeek(FILE* fp, int64_t offset, int whence) static inline int FSeek(FILE* fp, int64_t offset, int whence) {
{
#if _WIN32 #if _WIN32
return _fseeki64(fp, offset, whence); return _fseeki64(fp, offset, whence);
#elif __APPLE__ || __FreeBSD__ #elif __APPLE__ || __FreeBSD__
return fseeko(fp, offset, whence); return fseeko(fp, offset, whence);
#else #else
return fseeko64(fp, offset, whence); return fseeko64(fp, offset, whence);
#endif #endif
} }
static inline int64_t FTell(FILE* fp) static inline int64_t FTell(FILE* fp) {
{
#if _WIN32 #if _WIN32
return _ftelli64(fp); return _ftelli64(fp);
#elif __APPLE__ || __FreeBSD__ #elif __APPLE__ || __FreeBSD__
return ftello(fp); return ftello(fp);
#else #else
return ftello64(fp); return ftello64(fp);
#endif #endif
} }
static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz) static inline bool CheckFreeSpace(const char* path, size_t reqSz) {
{
#if _WIN32 #if _WIN32
ULARGE_INTEGER freeBytes; ULARGE_INTEGER freeBytes;
wchar_t buf[1024]; const nowide::wstackstring wpath(path);
wchar_t* end; std::array<wchar_t, 1024> buf{};
DWORD ret = GetFullPathNameW(path, 1024, buf, &end); wchar_t* end = nullptr;
if (!ret || ret > 1024) DWORD ret = GetFullPathNameW(wpath.get(), 1024, buf.data(), &end);
{ if (ret == 0 || ret > 1024) {
LogModule.report(logvisor::Error, _S("GetFullPathNameW %s"), path); LogModule.report(logvisor::Error, FMT_STRING("GetFullPathNameW {}"), path);
return false; return false;
} }
if (end) if (end != nullptr) {
end[0] = L'\0'; end[0] = L'\0';
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr)) }
{ if (!GetDiskFreeSpaceExW(buf.data(), &freeBytes, nullptr, nullptr)) {
LogModule.report(logvisor::Error, _S("GetDiskFreeSpaceExW %s: %d"), path, GetLastError()); LogModule.report(logvisor::Error, FMT_STRING("GetDiskFreeSpaceExW {}: {}"), path, GetLastError());
return false; return false;
} }
return reqSz < freeBytes.QuadPart; return reqSz < freeBytes.QuadPart;
#else #else
struct statvfs svfs; struct statvfs svfs;
if (statvfs(path, &svfs)) if (statvfs(path, &svfs)) {
{ LogModule.report(logvisor::Error, FMT_STRING("statvfs {}: {}"), path, strerror(errno));
LogModule.report(logvisor::Error, "statvfs %s: %s", path, strerror(errno)); return false;
return false; }
} return reqSz < svfs.f_frsize * svfs.f_bavail;
return reqSz < svfs.f_frsize * svfs.f_bavail;
#endif #endif
} }
#if __GNUC__ } // namespace nod
__attribute__((__format__ (__printf__, 1, 2)))
#endif
static inline void Printf(const SystemChar* fmt, ...)
{
va_list args;
va_start(args, fmt);
#if NOD_UCS2
vwprintf(fmt, args);
#else
vprintf(fmt, args);
#endif
va_end(args);
}
}
#endif // __NOD_UTIL_HPP__

View File

@@ -1,24 +1,19 @@
#ifndef __AES_HPP__ #pragma once
#define __AES_HPP__
#include <stdint.h> #include <cstdint>
#include <stdlib.h> #include <cstdlib>
#include <memory> #include <memory>
namespace nod namespace nod {
{
class IAES class IAES {
{
public: public:
virtual ~IAES() = default; virtual ~IAES() = default;
virtual void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0; virtual void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) = 0;
virtual void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0; virtual void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) = 0;
virtual void setKey(const uint8_t* key)=0; virtual void setKey(const uint8_t* key) = 0;
}; };
std::unique_ptr<IAES> NewAES(); std::unique_ptr<IAES> NewAES();
} } // namespace nod
#endif //__AES_HPP__

View File

@@ -1,29 +1,21 @@
#ifndef __NOD_LIB__ #pragma once
#define __NOD_LIB__
#include <memory>
#include <functional> #include <functional>
#include "logvisor/logvisor.hpp" #include <memory>
#include "Util.hpp" #include <string>
namespace nod #include "nod/Util.hpp"
{
namespace nod {
class DiscBase; class DiscBase;
struct ExtractionContext final struct ExtractionContext final {
{ bool force : 1;
bool force : 1; std::function<void(std::string_view, float)> progressCB;
std::function<void(const std::string&, float)> progressCB;
}; };
std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path); std::unique_ptr<DiscBase> OpenDiscFromImage(std::string_view path);
std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii); std::unique_ptr<DiscBase> OpenDiscFromImage(std::string_view path, bool& isWii);
} } // namespace nod
#include "DiscGCN.hpp"
#include "DiscWii.hpp"
#include "IDiscIO.hpp"
#endif // __NOD_LIB__

View File

@@ -1,21 +1,53 @@
if(NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-multichar")
set(PLAT_SRCS FileIOFILE.cpp)
else()
set(PLAT_SRCS FileIOWin32.cpp)
endif()
add_library(nod add_library(nod
aes.cpp aes.cpp
sha1.c sha1.c
DiscBase.cpp
DiscGCN.cpp DirectoryEnumerator.cpp
DiscIOISO.cpp DiscBase.cpp
DiscIOWBFS.cpp DiscGCN.cpp
DiscWii.cpp DiscIOISO.cpp
DirectoryEnumerator.cpp DiscIONFS.cpp
nod.cpp DiscIOWBFS.cpp
${PLAT_SRCS} DiscWii.cpp
${NOD_HEADERS}) nod.cpp
if(NOT MSVC) OSUTF.c
set_source_files_properties(aes.cpp PROPERTIES COMPILE_FLAGS -maes)
../include/nod/aes.hpp
../include/nod/DirectoryEnumerator.hpp
../include/nod/DiscBase.hpp
../include/nod/DiscGCN.hpp
../include/nod/DiscWii.hpp
../include/nod/IDiscIO.hpp
../include/nod/IFileIO.hpp
../include/nod/nod.hpp
../include/nod/sha1.h
../include/nod/Util.hpp
../include/nod/OSUTF.h
)
target_include_directories(nod PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
)
target_link_libraries(nod PUBLIC $<BUILD_INTERFACE:logvisor>)
if(WIN32)
target_sources(nod PRIVATE FileIOWin32.cpp)
else()
target_compile_options(nod PRIVATE -Wno-multichar)
target_sources(nod PRIVATE FileIOFILE.cpp)
endif() endif()
if(NOT MSVC AND ${CMAKE_SYSTEM_PROCESSOR} STREQUAL x86_64)
set_source_files_properties(aes.cpp PROPERTIES COMPILE_FLAGS -maes)
endif()
# Associate target with export
install(
TARGETS nod
EXPORT nodTargets
ARCHIVE DESTINATION "lib"
INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target.
)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../include/nod DESTINATION include)

View File

@@ -1,283 +1,297 @@
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/stat.h>
#include <dirent.h>
#endif
#include <map>
#include "nod/DirectoryEnumerator.hpp" #include "nod/DirectoryEnumerator.hpp"
namespace nod #ifdef _WIN32
{ #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#else
#include <dirent.h>
#include <sys/stat.h>
#endif
DirectoryEnumerator::DirectoryEnumerator(const SystemChar* path, Mode mode, #include <cstring>
bool sizeSort, bool reverse, bool noHidden) #include <map>
{ #ifdef __cpp_lib_ranges
Sstat theStat; #include <ranges>
if (Stat(path, &theStat) || !S_ISDIR(theStat.st_mode)) #endif
return;
namespace nod {
DirectoryEnumerator::DirectoryEnumerator(std::string_view path, Mode mode, bool sizeSort, bool reverse, bool noHidden) {
Sstat theStat;
if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode)) {
return;
}
#if _WIN32 #if _WIN32
SystemString wc(path); std::wstring wc = nowide::widen(path);
wc += _S("/*"); wc += L"/*";
WIN32_FIND_DATAW d; WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(wc.c_str(), &d); HANDLE dir = FindFirstFileW(wc.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE) if (dir == INVALID_HANDLE_VALUE) {
return; return;
switch (mode) }
{ switch (mode) {
case Mode::Native: case Mode::Native:
do do {
{ if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L"..")) {
if (!wcscmp(d.cFileName, _S(".")) || !wcscmp(d.cFileName, _S(".."))) continue;
continue; }
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) {
continue; continue;
SystemString fp(path); }
fp += _S('/'); std::string fileName = nowide::narrow(d.cFileName);
fp += d.cFileName; std::string fp(path);
Sstat st; fp += '/';
if (Stat(fp.c_str(), &st)) fp += fileName;
continue; Sstat st;
if (Stat(fp.c_str(), &st))
continue;
size_t sz = 0; size_t sz = 0;
bool isDir = false; bool isDir = false;
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode)) {
isDir = true; isDir = true;
else if (S_ISREG(st.st_mode)) } else if (S_ISREG(st.st_mode)) {
sz = st.st_size; sz = st.st_size;
else } else {
continue; continue;
}
m_entries.push_back(std::move(Entry(std::move(fp), d.cFileName, sz, isDir))); m_entries.emplace_back(fp, fileName, sz, isDir);
} while (FindNextFileW(dir, &d)); } while (FindNextFileW(dir, &d));
break; break;
case Mode::DirsThenFilesSorted: case Mode::DirsThenFilesSorted:
case Mode::DirsSorted: case Mode::DirsSorted: {
{ std::map<std::string, Entry, CaseInsensitiveCompare> sort;
std::map<SystemString, Entry, CaseInsensitiveCompare> sort; do {
do if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L"..")) {
{ continue;
if (!wcscmp(d.cFileName, _S(".")) || !wcscmp(d.cFileName, _S(".."))) }
continue; if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) {
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) continue;
continue; }
SystemString fp(path); std::string fileName = nowide::narrow(d.cFileName);
fp +=_S('/'); std::string fp(path);
fp += d.cFileName; fp += '/';
Sstat st; fp += fileName;
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) Sstat st;
continue; if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) {
sort.emplace(std::make_pair(d.cFileName, Entry(std::move(fp), d.cFileName, 0, true))); continue;
} while (FindNextFileW(dir, &d)); }
sort.emplace(fileName, Entry{fp, fileName, 0, true});
} while (FindNextFileW(dir, &d));
m_entries.reserve(sort.size()); m_entries.reserve(sort.size());
if (reverse) if (reverse) {
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it) for (auto& it : std::ranges::reverse_view(sort)) {
m_entries.push_back(std::move(it->second)); m_entries.emplace_back(std::move(it.second));
else }
for (auto& e : sort) } else {
m_entries.push_back(std::move(e.second)); for (auto& e : sort) {
m_entries.emplace_back(std::move(e.second));
if (mode == Mode::DirsSorted) }
break;
FindClose(dir);
dir = FindFirstFileW(wc.c_str(), &d);
} }
case Mode::FilesSorted:
{
if (mode == Mode::FilesSorted)
m_entries.clear();
if (sizeSort) if (mode == Mode::DirsSorted) {
{ break;
std::multimap<size_t, Entry> sort;
do
{
if (!wcscmp(d.cFileName, _S(".")) || !wcscmp(d.cFileName, _S("..")))
continue;
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
continue;
SystemString fp(path);
fp += _S('/');
fp += d.cFileName;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
continue;
sort.emplace(std::make_pair(st.st_size, Entry(std::move(fp), d.cFileName, st.st_size, false)));
} while (FindNextFileW(dir, &d));
m_entries.reserve(sort.size());
if (reverse)
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
m_entries.push_back(std::move(it->second));
else
for (auto& e : sort)
m_entries.push_back(std::move(e.second));
}
else
{
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
do
{
if (!wcscmp(d.cFileName, _S(".")) || !wcscmp(d.cFileName, _S("..")))
continue;
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
continue;
SystemString fp(path);
fp += _S('/');
fp += d.cFileName;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
continue;
sort.emplace(std::make_pair(d.cFileName, Entry(std::move(fp), d.cFileName, st.st_size, false)));
} while (FindNextFileW(dir, &d));
m_entries.reserve(sort.size());
if (reverse)
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
m_entries.push_back(std::move(it->second));
else
for (auto& e : sort)
m_entries.push_back(std::move(e.second));
}
break;
}
} }
FindClose(dir); FindClose(dir);
dir = FindFirstFileW(wc.c_str(), &d);
}
case Mode::FilesSorted: {
if (mode == Mode::FilesSorted) {
m_entries.clear();
}
if (sizeSort) {
std::multimap<size_t, Entry> sort;
do {
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L"..")) {
continue;
}
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) {
continue;
}
std::string fileName = nowide::narrow(d.cFileName);
std::string fp(path);
fp += '/';
fp += fileName;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) {
continue;
}
sort.emplace(st.st_size, Entry{fp, fileName, static_cast<size_t>(st.st_size), false});
} while (FindNextFileW(dir, &d));
m_entries.reserve(m_entries.size() + sort.size());
if (reverse) {
for (auto& it : std::ranges::reverse_view(sort)) {
m_entries.emplace_back(std::move(it.second));
}
} else {
for (auto& e : sort) {
m_entries.emplace_back(std::move(e.second));
}
}
} else {
std::map<std::string, Entry, CaseInsensitiveCompare> sort;
do {
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L"..")) {
continue;
}
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) {
continue;
}
std::string fileName = nowide::narrow(d.cFileName);
std::string fp(path);
fp += '/';
fp += fileName;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) {
continue;
}
sort.emplace(fileName, Entry{fp, fileName, static_cast<size_t>(st.st_size), false});
} while (FindNextFileW(dir, &d));
m_entries.reserve(m_entries.size() + sort.size());
if (reverse) {
for (auto& e : std::ranges::reverse_view(sort)) {
m_entries.emplace_back(std::move(e.second));
}
} else {
for (auto& e : sort) {
m_entries.emplace_back(std::move(e.second));
}
}
}
break;
}
}
FindClose(dir);
#else #else
DIR* dir = opendir(path); DIR* dir = opendir(path.data());
if (!dir) if (!dir)
return; return;
const dirent* d; const dirent* d;
switch (mode) switch (mode) {
{ case Mode::Native:
case Mode::Native: while ((d = readdir(dir))) {
while ((d = readdir(dir))) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
{ continue;
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) if (noHidden && d->d_name[0] == '.')
continue; continue;
if (noHidden && d->d_name[0] == '.') std::string fp(path);
continue; fp += '/';
SystemString fp(path); fp += d->d_name;
fp += '/'; Sstat st;
fp += d->d_name; if (Stat(fp.c_str(), &st))
Sstat st; continue;
if (Stat(fp.c_str(), &st))
continue;
size_t sz = 0; size_t sz = 0;
bool isDir = false; bool isDir = false;
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
isDir = true; isDir = true;
else if (S_ISREG(st.st_mode)) else if (S_ISREG(st.st_mode))
sz = st.st_size; sz = st.st_size;
else else
continue; continue;
m_entries.push_back(Entry(std::move(fp), d->d_name, sz, isDir)); m_entries.push_back(Entry(fp, d->d_name, sz, isDir));
}
break;
case Mode::DirsThenFilesSorted:
case Mode::DirsSorted:
{
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
while ((d = readdir(dir)))
{
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
if (noHidden && d->d_name[0] == '.')
continue;
SystemString fp(path);
fp += '/';
fp += d->d_name;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
continue;
sort.emplace(std::make_pair(d->d_name, Entry(std::move(fp), d->d_name, 0, true)));
}
m_entries.reserve(sort.size());
if (reverse)
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
m_entries.push_back(std::move(it->second));
else
for (auto& e : sort)
m_entries.push_back(std::move(e.second));
if (mode == Mode::DirsSorted)
break;
rewinddir(dir);
} }
case Mode::FilesSorted: break;
{ case Mode::DirsThenFilesSorted:
if (mode == Mode::FilesSorted) case Mode::DirsSorted: {
m_entries.clear(); std::map<std::string, Entry, CaseInsensitiveCompare> sort;
while ((d = readdir(dir))) {
if (sizeSort) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
{ continue;
std::multimap<size_t, Entry> sort; if (noHidden && d->d_name[0] == '.')
while ((d = readdir(dir))) continue;
{ std::string fp(path);
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) fp += '/';
continue; fp += d->d_name;
if (noHidden && d->d_name[0] == '.') Sstat st;
continue; if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
SystemString fp(path); continue;
fp += '/'; sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, 0, true)));
fp += d->d_name;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
continue;
sort.emplace(std::make_pair(st.st_size, Entry(std::move(fp), d->d_name, st.st_size, false)));
}
m_entries.reserve(sort.size());
if (reverse)
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
m_entries.push_back(std::move(it->second));
else
for (auto& e : sort)
m_entries.push_back(std::move(e.second));
}
else
{
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
while ((d = readdir(dir)))
{
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
if (noHidden && d->d_name[0] == '.')
continue;
SystemString fp(path);
fp += '/';
fp += d->d_name;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
continue;
sort.emplace(std::make_pair(d->d_name, Entry(std::move(fp), d->d_name, st.st_size, false)));
}
m_entries.reserve(sort.size());
if (reverse)
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it)
m_entries.push_back(std::move(it->second));
else
for (auto& e : sort)
m_entries.push_back(std::move(e.second));
}
break;
} }
m_entries.reserve(sort.size());
if (reverse)
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
m_entries.push_back(std::move(it->second));
else
for (auto& e : sort)
m_entries.push_back(std::move(e.second));
if (mode == Mode::DirsSorted)
break;
rewinddir(dir);
[[fallthrough]];
}
case Mode::FilesSorted: {
if (mode == Mode::FilesSorted)
m_entries.clear();
if (sizeSort) {
std::multimap<size_t, Entry> sort;
while ((d = readdir(dir))) {
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
if (noHidden && d->d_name[0] == '.')
continue;
std::string fp(path);
fp += '/';
fp += d->d_name;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
continue;
sort.emplace(std::make_pair(st.st_size, Entry(fp, d->d_name, st.st_size, false)));
}
m_entries.reserve(sort.size());
if (reverse)
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
m_entries.push_back(std::move(it->second));
else
for (auto& e : sort)
m_entries.push_back(std::move(e.second));
} else {
std::map<std::string, Entry, CaseInsensitiveCompare> sort;
while ((d = readdir(dir))) {
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
if (noHidden && d->d_name[0] == '.')
continue;
std::string fp(path);
fp += '/';
fp += d->d_name;
Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
continue;
sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, st.st_size, false)));
}
m_entries.reserve(sort.size());
if (reverse)
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
m_entries.push_back(std::move(it->second));
else
for (auto& e : sort)
m_entries.push_back(std::move(e.second));
} }
closedir(dir);
break;
}
}
closedir(dir);
#endif #endif
} }
} } // namespace nod

File diff suppressed because it is too large Load Diff

View File

@@ -1,468 +1,431 @@
#include "nod/DiscGCN.hpp" #include "nod/DiscGCN.hpp"
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include "nod/nod.hpp" #include "nod/nod.hpp"
#include <inttypes.h> #include "nod/Util.hpp"
#include <logvisor/logvisor.hpp>
#define BUFFER_SZ 0x8000 #define BUFFER_SZ 0x8000
namespace nod namespace nod {
{
class PartitionGCN : public DiscBase::IPartition class PartitionGCN : public IPartition {
{
public: public:
PartitionGCN(const DiscGCN& parent, uint64_t offset, bool& err) PartitionGCN(const DiscGCN& parent, uint64_t offset, bool& err)
: IPartition(parent, PartitionKind::Data, false, offset) : IPartition(parent, PartitionKind::Data, false, offset) {
{ /* GCN-specific header reads */
/* GCN-specific header reads */ std::unique_ptr<IPartReadStream> s = beginReadStream(0x0);
std::unique_ptr<IPartReadStream> s = beginReadStream(0x0); if (!s) {
if (!s) err = true;
{ return;
err = true;
return;
}
m_header.read(*s);
m_bi2Header.read(*s);
m_dolOff = m_header.m_dolOff;
m_fstOff = m_header.m_fstOff;
m_fstSz = m_header.m_fstSz;
uint32_t vals[2];
s->seek(0x2440 + 0x14);
s->read(vals, 8);
m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]);
/* Yay files!! */
parseFST(*s);
/* Also make DOL header and size handy */
s->seek(m_dolOff);
parseDOL(*s);
} }
m_header.read(*s);
m_bi2Header.read(*s);
m_dolOff = m_header.m_dolOff;
m_fstOff = m_header.m_fstOff;
m_fstSz = m_header.m_fstSz;
uint32_t vals[2];
s->seek(0x2440 + 0x14);
s->read(vals, 8);
m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]);
class PartReadStream : public IPartReadStream /* Yay files!! */
{ parseFST(*s);
const PartitionGCN& m_parent;
std::unique_ptr<IReadStream> m_dio;
uint64_t m_offset; /* Also make DOL header and size handy */
size_t m_curBlock = SIZE_MAX; s->seek(m_dolOff);
uint8_t m_buf[BUFFER_SZ]; parseDOL(*s);
}
public: class PartReadStream : public IPartReadStream {
PartReadStream(const PartitionGCN& parent, uint64_t offset, bool& err) const PartitionGCN& m_parent;
: m_parent(parent), m_offset(offset) std::unique_ptr<IReadStream> m_dio;
{
size_t block = m_offset / BUFFER_SZ;
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(block * BUFFER_SZ);
if (!m_dio)
{
err = true;
return;
}
m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block;
}
void seek(int64_t offset, int whence)
{
if (whence == SEEK_SET)
m_offset = offset;
else if (whence == SEEK_CUR)
m_offset += offset;
else
return;
size_t block = m_offset / BUFFER_SZ;
if (block != m_curBlock)
{
m_dio->seek(block * BUFFER_SZ);
m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block;
}
}
uint64_t position() const {return m_offset;}
uint64_t read(void* buf, uint64_t length)
{
size_t block = m_offset / BUFFER_SZ;
size_t cacheOffset = m_offset % BUFFER_SZ;
uint64_t cacheSize;
uint64_t rem = length;
uint8_t* dst = (uint8_t*)buf;
while (rem) uint64_t m_offset;
{ size_t m_curBlock = SIZE_MAX;
if (block != m_curBlock) uint8_t m_buf[BUFFER_SZ];
{
m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block;
}
cacheSize = rem; public:
if (cacheSize + cacheOffset > BUFFER_SZ) PartReadStream(const PartitionGCN& parent, uint64_t offset, bool& err) : m_parent(parent), m_offset(offset) {
cacheSize = BUFFER_SZ - cacheOffset; size_t block = m_offset / BUFFER_SZ;
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(block * BUFFER_SZ);
memmove(dst, m_buf + cacheOffset, cacheSize); if (!m_dio) {
dst += cacheSize; err = true;
rem -= cacheSize;
cacheOffset = 0;
++block;
}
m_offset += length;
return dst - (uint8_t*)buf;
}
};
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const
{
bool Err = false;
auto ret = std::unique_ptr<IPartReadStream>(new PartReadStream(*this, offset, Err));
if (Err)
return {};
return ret;
}
};
DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err)
: DiscBase(std::move(dio), err)
{
if (err)
return; return;
}
m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block;
}
void seek(int64_t offset, int whence) override {
if (whence == SEEK_SET)
m_offset = offset;
else if (whence == SEEK_CUR)
m_offset += offset;
else
return;
size_t block = m_offset / BUFFER_SZ;
if (block != m_curBlock) {
m_dio->seek(block * BUFFER_SZ);
m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block;
}
}
uint64_t position() const override { return m_offset; }
uint64_t read(void* buf, uint64_t length) override {
size_t block = m_offset / BUFFER_SZ;
size_t cacheOffset = m_offset % BUFFER_SZ;
uint64_t cacheSize;
uint64_t rem = length;
uint8_t* dst = (uint8_t*)buf;
/* One lone partition for GCN */ while (rem) {
m_partitions.emplace_back(new PartitionGCN(*this, 0, err)); if (block != m_curBlock) {
} m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block;
DiscBuilderGCN DiscGCN::makeMergeBuilder(const SystemChar* outPath, FProgress progressCB)
{
return DiscBuilderGCN(outPath, progressCB);
}
bool DiscGCN::extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const
{
return true;
}
class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase
{
uint64_t m_curUser = 0x57058000;
public:
class PartWriteStream : public IPartWriteStream
{
const PartitionBuilderGCN& m_parent;
uint64_t m_offset;
std::unique_ptr<IFileIO::IWriteStream> m_fio;
public:
PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset, bool& err)
: m_parent(parent), m_offset(offset)
{
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset);
if (!m_fio)
err = true;
} }
void close() {m_fio.reset();}
uint64_t position() const {return m_offset;}
uint64_t write(const void* buf, uint64_t length)
{
uint64_t len = m_fio->write(buf, length);
m_offset += len;
return len;
}
void seek(size_t off)
{
m_offset = off;
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(off);
}
};
PartitionBuilderGCN(DiscBuilderBase& parent) cacheSize = rem;
: DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false) {} if (cacheSize + cacheOffset > BUFFER_SZ)
cacheSize = BUFFER_SZ - cacheOffset;
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) memmove(dst, m_buf + cacheOffset, cacheSize);
{ dst += cacheSize;
m_curUser -= reqSz; rem -= cacheSize;
m_curUser &= 0xfffffffffffffff0; cacheOffset = 0;
if (m_curUser < 0x30000) ++block;
{ }
LogModule.report(logvisor::Error, "user area low mark reached");
return -1; m_offset += length;
} return dst - (uint8_t*)buf;
static_cast<PartWriteStream&>(ws).seek(m_curUser); }
return m_curUser; };
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const override {
bool err = false;
auto ret = std::make_unique<PartReadStream>(*this, offset, err);
if (err) {
return nullptr;
} }
uint32_t packOffset(uint64_t offset) const return ret;
{ }
return offset;
}
std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)
{
bool Err = false;
std::unique_ptr<IPartWriteStream> ret = std::make_unique<PartWriteStream>(*this, offset, Err);
if (Err)
return {};
return ret;
}
bool _build(const std::function<bool(IPartWriteStream&, uint32_t, uint32_t,
uint32_t, uint32_t, uint32_t)>& headerFunc,
const std::function<bool(IPartWriteStream&)>& bi2Func,
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc)
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0x2440);
if (!ws)
return false;
size_t xferSz = 0;
if (!apploaderFunc(*ws, xferSz))
return false;
size_t fstOff = ROUND_UP_32(xferSz);
size_t fstSz = sizeof(FSTNode) * m_buildNodes.size();
for (size_t i=0 ; i<fstOff-xferSz ; ++i)
ws->write("\xff", 1);
fstOff += 0x2440;
ws->write(m_buildNodes.data(), fstSz);
for (const std::string& str : m_buildNames)
ws->write(str.data(), str.size()+1);
fstSz += m_buildNameOff;
fstSz = ROUND_UP_32(fstSz);
if (fstOff + fstSz >= m_curUser)
{
LogModule.report(logvisor::Error,
"FST flows into user area (one or the other is too big)");
return false;
}
ws = beginWriteStream(0);
if (!ws)
return false;
if (!headerFunc(*ws, m_dolOffset, fstOff, fstSz, m_curUser, 0x57058000 - m_curUser))
return false;
if (!bi2Func(*ws))
return false;
return true;
}
bool buildFromDirectory(const SystemChar* dirIn)
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
if (!ws)
return false;
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn);
if (!result)
return false;
SystemString dirStr(dirIn);
/* Check Apploader */
SystemString apploaderIn = dirStr + _S("/sys/apploader.img");
Sstat apploaderStat;
if (Stat(apploaderIn.c_str(), &apploaderStat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn.c_str());
return -1;
}
/* Check Boot */
SystemString bootIn = dirStr + _S("/sys/boot.bin");
Sstat bootStat;
if (Stat(bootIn.c_str(), &bootStat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), bootIn.c_str());
return -1;
}
/* Check BI2 */
SystemString bi2In = dirStr + _S("/sys/bi2.bin");
Sstat bi2Stat;
if (Stat(bi2In.c_str(), &bi2Stat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), bi2In.c_str());
return -1;
}
return _build(
[this, &bootIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz,
uint32_t userOff, uint32_t userSz) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bootIn.c_str())->beginReadStream();
if (!rs)
return false;
Header header;
header.read(*rs);
header.m_dolOff = dolOff;
header.m_fstOff = fstOff;
header.m_fstSz = fstSz;
header.m_fstMaxSz = fstSz;
header.m_userPosition = userOff;
header.m_userSz = userSz;
header.write(ws);
return true;
},
[this, &bi2In](IPartWriteStream& ws) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bi2In.c_str())->beginReadStream();
if (!rs)
return false;
BI2Header bi2;
bi2.read(*rs);
bi2.write(ws);
return true;
},
[this, &apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn.c_str())->beginReadStream();
if (!rs)
return false;
char buf[8192];
while (true)
{
size_t rdSz = rs->read(buf, 8192);
if (!rdSz)
break;
ws.write(buf, rdSz);
xferSz += rdSz;
if (0x2440 + xferSz >= m_curUser)
{
LogModule.report(logvisor::Error,
"apploader flows into user area (one or the other is too big)");
return false;
}
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
}
++m_parent.m_progressIdx;
return true;
});
}
bool mergeFromDirectory(const PartitionGCN* partIn, const SystemChar* dirIn)
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
if (!ws)
return false;
bool result = DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(*ws, partIn, dirIn);
if (!result)
return false;
return _build(
[this, partIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz,
uint32_t userOff, uint32_t userSz) -> bool
{
Header header = partIn->getHeader();
header.m_dolOff = dolOff;
header.m_fstOff = fstOff;
header.m_fstSz = fstSz;
header.m_fstMaxSz = fstSz;
header.m_userPosition = userOff;
header.m_userSz = userSz;
header.write(ws);
return true;
},
[this, partIn](IPartWriteStream& ws) -> bool
{
partIn->getBI2().write(ws);
return true;
},
[this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool
{
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
size_t apploaderSz = partIn->getApploaderSize();
SystemString apploaderName(_S("<apploader>"));
ws.write(apploaderBuf.get(), apploaderSz);
xferSz += apploaderSz;
if (0x2440 + xferSz >= m_curUser)
{
LogModule.report(logvisor::Error,
"apploader flows into user area (one or the other is too big)");
return false;
}
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
++m_parent.m_progressIdx;
return true;
});
}
}; };
EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn) DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err) : DiscBase(std::move(dio), err) {
{ if (err)
if (!m_fileIO->beginWriteStream()) return;
return EBuildResult::Failed;
if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000)) /* One lone partition for GCN */
{ m_partitions.emplace_back(std::make_unique<PartitionGCN>(*this, 0, err));
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_outPath.c_str()); }
return EBuildResult::DiskFull;
DiscBuilderGCN DiscGCN::makeMergeBuilder(std::string_view outPath, FProgress progressCB) {
return DiscBuilderGCN(outPath, progressCB);
}
bool DiscGCN::extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const { return true; }
class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase {
uint64_t m_curUser = 0x57058000;
public:
class PartWriteStream : public IPartWriteStream {
const PartitionBuilderGCN& m_parent;
uint64_t m_offset;
std::unique_ptr<IFileIO::IWriteStream> m_fio;
public:
PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset, bool& err)
: m_parent(parent), m_offset(offset) {
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset);
if (!m_fio)
err = true;
} }
m_progressCB(getProgressFactor(), _S("Preallocating image"), -1); void close() override { m_fio.reset(); }
++m_progressIdx; uint64_t position() const override { return m_offset; }
auto ws = m_fileIO->beginWriteStream(0x57058000 - 1); uint64_t write(const void* buf, uint64_t length) override {
uint64_t len = m_fio->write(buf, length);
m_offset += len;
return len;
}
void seek(size_t off) {
m_offset = off;
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(off);
}
};
PartitionBuilderGCN(DiscBuilderBase& parent)
: DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false) {}
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) override {
m_curUser -= reqSz;
m_curUser &= 0xfffffffffffffff0;
if (m_curUser < 0x30000) {
LogModule.report(logvisor::Error, FMT_STRING("user area low mark reached"));
return -1;
}
static_cast<PartWriteStream&>(ws).seek(m_curUser);
return m_curUser;
}
uint32_t packOffset(uint64_t offset) const override { return offset; }
std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset) override {
bool err = false;
auto ret = std::make_unique<PartWriteStream>(*this, offset, err);
if (err) {
return nullptr;
}
return ret;
}
bool
_build(const std::function<bool(IPartWriteStream&, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)>& headerFunc,
const std::function<bool(IPartWriteStream&)>& bi2Func,
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc) {
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0x2440);
if (!ws) if (!ws)
return EBuildResult::Failed; return false;
ws->write("", 1); size_t xferSz = 0;
if (!apploaderFunc(*ws, xferSz))
return false;
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]); size_t fstOff = ROUND_UP_32(xferSz);
return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed; size_t fstSz = sizeof(FSTNode) * m_buildNodes.size();
} for (size_t i = 0; i < fstOff - xferSz; ++i)
ws->write("\xff", 1);
fstOff += 0x2440;
ws->write(m_buildNodes.data(), fstSz);
for (const std::string& str : m_buildNames)
ws->write(str.data(), str.size() + 1);
fstSz += m_buildNameOff;
fstSz = ROUND_UP_32(fstSz);
uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn) if (fstOff + fstSz >= m_curUser) {
{ LogModule.report(logvisor::Error, FMT_STRING("FST flows into user area (one or the other is too big)"));
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false); return false;
if (sz == -1)
return -1;
sz += 0x30000;
if (sz > 0x57058000)
{
LogModule.report(logvisor::Error, _S("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000);
return -1;
} }
return sz;
}
DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB) ws = beginWriteStream(0);
: DiscBuilderBase(outPath, 0x57058000, progressCB)
{
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this);
m_partitions.emplace_back(partBuilder);
}
DiscMergerGCN::DiscMergerGCN(const SystemChar* outPath, DiscGCN& sourceDisc, FProgress progressCB)
: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB))
{}
EBuildResult DiscMergerGCN::mergeFromDirectory(const SystemChar* dirIn)
{
if (!m_builder.getFileIO().beginWriteStream())
return EBuildResult::Failed;
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000))
{
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_builder.m_outPath.c_str());
return EBuildResult::DiskFull;
}
m_builder.m_progressCB(m_builder.getProgressFactor(), _S("Preallocating image"), -1);
++m_builder.m_progressIdx;
auto ws = m_builder.m_fileIO->beginWriteStream(0x57058000 - 1);
if (!ws) if (!ws)
return EBuildResult::Failed; return false;
ws->write("", 1); if (!headerFunc(*ws, m_dolOffset, fstOff, fstSz, m_curUser, 0x57058000 - m_curUser))
return false;
if (!bi2Func(*ws))
return false;
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_builder.m_partitions[0]); return true;
return pb.mergeFromDirectory(static_cast<PartitionGCN*>(m_sourceDisc.getDataPartition()), dirIn) ? }
EBuildResult::Success : EBuildResult::Failed;
}
uint64_t DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, const SystemChar* dirIn) bool buildFromDirectory(std::string_view dirIn) {
{ std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge( if (!ws)
sourceDisc.getDataPartition(), dirIn); return false;
if (sz == -1) bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn);
return -1; if (!result)
sz += 0x30000; return false;
if (sz > 0x57058000)
{ std::string dirStr(dirIn);
LogModule.report(logvisor::Error, _S("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000);
return -1; /* Check Apploader */
std::string apploaderIn = dirStr + "/sys/apploader.img";
Sstat apploaderStat;
if (Stat(apploaderIn.c_str(), &apploaderStat)) {
LogModule.report(logvisor::Error, FMT_STRING("unable to stat {}"), apploaderIn);
return false;
} }
return sz;
/* Check Boot */
std::string bootIn = dirStr + "/sys/boot.bin";
Sstat bootStat;
if (Stat(bootIn.c_str(), &bootStat)) {
LogModule.report(logvisor::Error, FMT_STRING("unable to stat {}"), bootIn);
return false;
}
/* Check BI2 */
std::string bi2In = dirStr + "/sys/bi2.bin";
Sstat bi2Stat;
if (Stat(bi2In.c_str(), &bi2Stat)) {
LogModule.report(logvisor::Error, FMT_STRING("unable to stat {}"), bi2In);
return false;
}
return _build(
[&bootIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz, uint32_t userOff,
uint32_t userSz) -> bool {
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bootIn.c_str())->beginReadStream();
if (!rs)
return false;
Header header;
header.read(*rs);
header.m_dolOff = dolOff;
header.m_fstOff = fstOff;
header.m_fstSz = fstSz;
header.m_fstMaxSz = fstSz;
header.m_userPosition = userOff;
header.m_userSz = userSz;
header.write(ws);
return true;
},
[&bi2In](IPartWriteStream& ws) -> bool {
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bi2In.c_str())->beginReadStream();
if (!rs)
return false;
BI2Header bi2;
bi2.read(*rs);
bi2.write(ws);
return true;
},
[this, &apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool {
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn.c_str())->beginReadStream();
if (!rs)
return false;
char buf[8192];
while (true) {
size_t rdSz = rs->read(buf, 8192);
if (!rdSz)
break;
ws.write(buf, rdSz);
xferSz += rdSz;
if (0x2440 + xferSz >= m_curUser) {
LogModule.report(logvisor::Error, FMT_STRING("apploader flows into user area (one or the other is too big)"));
return false;
}
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
}
++m_parent.m_progressIdx;
return true;
});
}
bool mergeFromDirectory(const PartitionGCN* partIn, std::string_view dirIn) {
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
if (!ws)
return false;
bool result = DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(*ws, partIn, dirIn);
if (!result)
return false;
return _build(
[partIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz, uint32_t userOff,
uint32_t userSz) -> bool {
Header header = partIn->getHeader();
header.m_dolOff = dolOff;
header.m_fstOff = fstOff;
header.m_fstSz = fstSz;
header.m_fstMaxSz = fstSz;
header.m_userPosition = userOff;
header.m_userSz = userSz;
header.write(ws);
return true;
},
[partIn](IPartWriteStream& ws) -> bool {
partIn->getBI2().write(ws);
return true;
},
[this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool {
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
size_t apploaderSz = partIn->getApploaderSize();
std::string apploaderName("<apploader>");
ws.write(apploaderBuf.get(), apploaderSz);
xferSz += apploaderSz;
if (0x2440 + xferSz >= m_curUser) {
LogModule.report(logvisor::Error, FMT_STRING("apploader flows into user area (one or the other is too big)"));
return false;
}
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
++m_parent.m_progressIdx;
return true;
});
}
};
EBuildResult DiscBuilderGCN::buildFromDirectory(std::string_view dirIn) {
if (!m_fileIO->beginWriteStream())
return EBuildResult::Failed;
if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000)) {
LogModule.report(logvisor::Error, FMT_STRING("not enough free disk space for {}"), m_outPath);
return EBuildResult::DiskFull;
}
m_progressCB(getProgressFactor(), "Preallocating image", -1);
++m_progressIdx;
{
auto ws = m_fileIO->beginWriteStream(0);
if (!ws)
return EBuildResult::Failed;
char zeroBytes[1024] = {};
for (uint64_t i = 0; i < 0x57058000; i += 1024)
ws->write(zeroBytes, 1024);
}
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]);
return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed;
} }
std::optional<uint64_t> DiscBuilderGCN::CalculateTotalSizeRequired(std::string_view dirIn) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false);
if (!sz)
return sz;
*sz += 0x30000;
if (sz > 0x57058000) {
LogModule.report(logvisor::Error, FMT_STRING("disc capacity exceeded [{} / {}]"), *sz, 0x57058000);
return std::nullopt;
}
return sz;
} }
DiscBuilderGCN::DiscBuilderGCN(std::string_view outPath, FProgress progressCB)
: DiscBuilderBase(outPath, 0x57058000, progressCB) {
m_partitions.emplace_back(std::make_unique<PartitionBuilderGCN>(*this));
}
DiscMergerGCN::DiscMergerGCN(std::string_view outPath, DiscGCN& sourceDisc, FProgress progressCB)
: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB)) {}
EBuildResult DiscMergerGCN::mergeFromDirectory(std::string_view dirIn) {
if (!m_builder.getFileIO().beginWriteStream())
return EBuildResult::Failed;
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000)) {
LogModule.report(logvisor::Error, FMT_STRING("not enough free disk space for {}"), m_builder.m_outPath);
return EBuildResult::DiskFull;
}
m_builder.m_progressCB(m_builder.getProgressFactor(), "Preallocating image", -1);
++m_builder.m_progressIdx;
{
auto ws = m_builder.m_fileIO->beginWriteStream(0);
if (!ws)
return EBuildResult::Failed;
char zeroBytes[1024] = {};
for (uint64_t i = 0; i < 0x57058000; i += 1024)
ws->write(zeroBytes, 1024);
}
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_builder.m_partitions[0]);
return pb.mergeFromDirectory(static_cast<PartitionGCN*>(m_sourceDisc.getDataPartition()), dirIn)
? EBuildResult::Success
: EBuildResult::Failed;
}
std::optional<uint64_t> DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, std::string_view dirIn) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn);
if (!sz)
return std::nullopt;
*sz += 0x30000;
if (sz > 0x57058000) {
LogModule.report(logvisor::Error, FMT_STRING("disc capacity exceeded [{} / {}]"), *sz, 0x57058000);
return std::nullopt;
}
return sz;
}
} // namespace nod

View File

@@ -1,69 +1,62 @@
#include <stdio.h>
#include "nod/Util.hpp"
#include "nod/IDiscIO.hpp" #include "nod/IDiscIO.hpp"
#include "nod/IFileIO.hpp" #include "nod/IFileIO.hpp"
#include "nod/Util.hpp"
namespace nod namespace nod {
{
class DiscIOISO : public IDiscIO {
std::unique_ptr<IFileIO> m_fio;
class DiscIOISO : public IDiscIO
{
std::unique_ptr<IFileIO> m_fio;
public: public:
DiscIOISO(const SystemString& fpin) DiscIOISO(std::string_view fpin) : m_fio(NewFileIO(fpin)) {}
: m_fio(NewFileIO(fpin)) {}
class ReadStream : public IReadStream class ReadStream : public IReadStream {
{ friend class DiscIOISO;
friend class DiscIOISO; std::unique_ptr<IFileIO::IReadStream> fp;
std::unique_ptr<IFileIO::IReadStream> fp; ReadStream(std::unique_ptr<IFileIO::IReadStream>&& fpin, bool& err) : fp(std::move(fpin)) {
ReadStream(std::unique_ptr<IFileIO::IReadStream>&& fpin, bool& err) if (!fp)
: fp(std::move(fpin)) { if (!fp) err = true; } err = true;
public:
uint64_t read(void* buf, uint64_t length)
{return fp->read(buf, length);}
uint64_t position() const
{return fp->position();}
void seek(int64_t offset, int whence)
{fp->seek(offset, whence);}
};
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
{
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_fio->beginReadStream(offset), Err));
if (Err)
return {};
return ret;
} }
class WriteStream : public IWriteStream public:
{ uint64_t read(void* buf, uint64_t length) override { return fp->read(buf, length); }
friend class DiscIOISO; uint64_t position() const override { return fp->position(); }
std::unique_ptr<IFileIO::IWriteStream> fp; void seek(int64_t offset, int whence) override { fp->seek(offset, whence); }
WriteStream(std::unique_ptr<IFileIO::IWriteStream>&& fpin, bool& err) };
: fp(std::move(fpin)) { if (!fp) err = true; }
public:
uint64_t write(const void* buf, uint64_t length)
{
return fp->write(buf, length);
}
};
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const override {
{ bool err = false;
bool Err = false; auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_fio->beginReadStream(offset), err));
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_fio->beginWriteStream(offset), Err));
if (Err) if (err)
return {}; return {};
return ret;
return ret;
}
class WriteStream : public IWriteStream {
friend class DiscIOISO;
std::unique_ptr<IFileIO::IWriteStream> fp;
WriteStream(std::unique_ptr<IFileIO::IWriteStream>&& fpin, bool& err) : fp(std::move(fpin)) {
if (!fp)
err = true;
} }
public:
uint64_t write(const void* buf, uint64_t length) override { return fp->write(buf, length); }
};
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override {
bool err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_fio->beginWriteStream(offset), err));
if (err)
return {};
return ret;
}
}; };
std::unique_ptr<IDiscIO> NewDiscIOISO(const SystemChar* path) std::unique_ptr<IDiscIO> NewDiscIOISO(std::string_view path) { return std::make_unique<DiscIOISO>(path); }
{
return std::unique_ptr<IDiscIO>(new DiscIOISO(path));
}
}
} // namespace nod

273
lib/DiscIONFS.cpp Normal file
View File

@@ -0,0 +1,273 @@
#include "nod/IDiscIO.hpp"
#include "nod/IFileIO.hpp"
#include "nod/Util.hpp"
#include "nod/aes.hpp"
#include <logvisor/logvisor.hpp>
namespace nod {
/*
* NFS is the image format used to distribute Wii VC games for the Wii U.
* It is an LBA format similar to WBFS but adds its own encryption layer.
* It logically stores a standard Wii disc image with partitions.
*/
class DiscIONFS : public IDiscIO {
std::vector<std::unique_ptr<IFileIO>> files;
struct NFSHead {
uint32_t magic; // EGGS
uint32_t version;
uint32_t unknown[2]; // Signature, UUID?
uint32_t lbaRangeCount;
struct {
uint32_t startBlock;
uint32_t numBlocks;
} lbaRanges[61];
uint32_t endMagic; // SGGE
} nfsHead;
uint8_t key[16];
uint32_t calculateNumFiles() const {
uint32_t totalBlockCount = 0;
for (uint32_t i = 0; i < nfsHead.lbaRangeCount; ++i)
totalBlockCount += nfsHead.lbaRanges[i].numBlocks;
return (uint64_t(totalBlockCount) * uint64_t(0x8000) + (uint64_t(0x200) + uint64_t(0xF9FFFFF))) /
uint64_t(0xFA00000);
}
struct FBO {
uint32_t file, block, lblock, offset;
};
FBO logicalToFBO(uint64_t offset) const {
auto blockAndRemBytes = nod::div(offset, uint64_t(0x8000)); /* 32768 bytes per block */
uint32_t block = UINT32_MAX;
for (uint32_t i = 0, physicalBlock = 0; i < nfsHead.lbaRangeCount; ++i) {
const auto& range = nfsHead.lbaRanges[i];
if (blockAndRemBytes.quot >= range.startBlock && blockAndRemBytes.quot - range.startBlock < range.numBlocks) {
block = physicalBlock + (blockAndRemBytes.quot - range.startBlock);
break;
}
physicalBlock += range.numBlocks;
}
/* This offset has no physical mapping, read zeroes */
if (block == UINT32_MAX)
return {UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX};
auto fileAndRemBlocks = nod::div(block, uint32_t(8000)); /* 8000 blocks per file */
return {uint32_t(fileAndRemBlocks.quot), uint32_t(fileAndRemBlocks.rem), uint32_t(blockAndRemBytes.quot),
uint32_t(blockAndRemBytes.rem)};
}
public:
DiscIONFS(std::string_view fpin, bool& err) {
/* Validate file path format */
using SignedSize = std::make_signed<std::string::size_type>::type;
const auto dotPos = SignedSize(fpin.rfind('.'));
const auto slashPos = SignedSize(fpin.find_last_of("/\\"));
if (fpin.size() <= 4 || dotPos == -1 || dotPos <= slashPos || fpin.compare(slashPos + 1, 4, "hif_") ||
fpin.compare(dotPos, fpin.size() - dotPos, ".nfs")) {
LogModule.report(logvisor::Error,
FMT_STRING("'{}' must begin with 'hif_' and end with '.nfs' to be accepted as an NFS image"),
fpin);
err = true;
return;
}
/* Load key file */
const std::string dir(fpin.begin(), fpin.begin() + slashPos + 1);
auto keyFile = NewFileIO(dir + "../code/htk.bin")->beginReadStream();
if (!keyFile)
keyFile = NewFileIO(dir + "htk.bin")->beginReadStream();
if (!keyFile) {
LogModule.report(logvisor::Error, FMT_STRING("Unable to open '{}../code/htk.bin' or '{}htk.bin'"), dir, dir);
err = true;
return;
}
if (keyFile->read(key, 16) != 16) {
LogModule.report(logvisor::Error, FMT_STRING("Unable to read from '{}../code/htk.bin' or '{}htk.bin'"), dir, dir);
err = true;
return;
}
/* Load header from first file */
const std::string firstPath = fmt::format(FMT_STRING("{}hif_{:06}.nfs"), dir, 0);
files.push_back(NewFileIO(firstPath));
auto rs = files.back()->beginReadStream();
if (!rs) {
LogModule.report(logvisor::Error, FMT_STRING("'{}' does not exist"), firstPath);
err = true;
return;
}
if (rs->read(&nfsHead, 0x200) != 0x200) {
LogModule.report(logvisor::Error, FMT_STRING("Unable to read header from '{}'"), firstPath);
err = true;
return;
}
if (std::memcmp(&nfsHead.magic, "EGGS", 4)) {
LogModule.report(logvisor::Error, FMT_STRING("Invalid magic in '{}'"), firstPath);
err = true;
return;
}
nfsHead.lbaRangeCount = SBig(nfsHead.lbaRangeCount);
for (uint32_t i = 0; i < nfsHead.lbaRangeCount; ++i) {
auto& range = nfsHead.lbaRanges[i];
range.startBlock = SBig(range.startBlock);
range.numBlocks = SBig(range.numBlocks);
}
/* Ensure remaining files exist */
const uint32_t numFiles = calculateNumFiles();
files.reserve(numFiles);
for (uint32_t i = 1; i < numFiles; ++i) {
std::string path = fmt::format(FMT_STRING("{}hif_{:06}.nfs"), dir, i);
files.push_back(NewFileIO(path));
if (!files.back()->exists()) {
LogModule.report(logvisor::Error, FMT_STRING("'{}' does not exist"), path);
err = true;
return;
}
}
}
class ReadStream : public IReadStream {
friend class DiscIONFS;
const DiscIONFS& m_parent;
std::unique_ptr<IReadStream> m_rs;
std::unique_ptr<IAES> m_aes;
/* Physical address - all UINT32_MAX indicates logical zero block */
DiscIONFS::FBO m_physAddr;
/* Logical address */
uint64_t m_offset;
/* Active file stream and its offset as set in the system.
* Block is typically one ahead of the presently decrypted block. */
uint32_t m_curFile = UINT32_MAX;
uint32_t m_curBlock = UINT32_MAX;
ReadStream(const DiscIONFS& parent, uint64_t offset, bool& err)
: m_parent(parent)
, m_aes(NewAES())
, m_physAddr({UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX})
, m_offset(offset) {
m_aes->setKey(m_parent.key);
setLogicalAddr(offset);
}
uint8_t m_encBuf[0x8000] = {};
uint8_t m_decBuf[0x8000] = {};
void setCurFile(uint32_t curFile) {
if (curFile >= m_parent.files.size()) {
LogModule.report(logvisor::Error, FMT_STRING("Out of bounds NFS file access"));
return;
}
m_curFile = curFile;
m_curBlock = UINT32_MAX;
m_rs = m_parent.files[m_curFile]->beginReadStream();
}
void setCurBlock(uint32_t curBlock) {
m_curBlock = curBlock;
m_rs->seek(m_curBlock * 0x8000 + 0x200);
}
void setPhysicalAddr(DiscIONFS::FBO physAddr) {
/* If we're just changing the offset, nothing else needs to be done */
if (m_physAddr.file == physAddr.file && m_physAddr.block == physAddr.block) {
m_physAddr.offset = physAddr.offset;
return;
}
m_physAddr = physAddr;
/* Set logical zero block */
if (m_physAddr.file == UINT32_MAX) {
memset(m_decBuf, 0, 0x8000);
return;
}
/* Make necessary file and block current with system */
if (m_physAddr.file != m_curFile)
setCurFile(m_physAddr.file);
if (m_physAddr.block != m_curBlock)
setCurBlock(m_physAddr.block);
/* Read block, handling 0x200 overlap case */
if (m_physAddr.block == 7999) {
m_rs->read(m_encBuf, 0x7E00);
setCurFile(m_curFile + 1);
m_rs->read(m_encBuf + 0x7E00, 0x200);
m_curBlock = 0;
} else {
m_rs->read(m_encBuf, 0x8000);
++m_curBlock;
}
/* Decrypt */
const uint32_t ivBuf[] = {0, 0, 0, SBig(m_physAddr.lblock)};
m_aes->decrypt((const uint8_t*)ivBuf, m_encBuf, m_decBuf, 0x8000);
}
void setLogicalAddr(uint64_t addr) { setPhysicalAddr(m_parent.logicalToFBO(m_offset)); }
public:
uint64_t read(void* buf, uint64_t length) override {
uint64_t rem = length;
uint8_t* dst = (uint8_t*)buf;
/* Perform reads on block boundaries */
while (rem) {
uint64_t readSize = rem;
uint32_t blockOffset = (m_physAddr.offset == UINT32_MAX) ? 0 : m_physAddr.offset;
if (readSize + blockOffset > 0x8000)
readSize = 0x8000 - blockOffset;
memmove(dst, m_decBuf + blockOffset, readSize);
dst += readSize;
rem -= readSize;
m_offset += readSize;
setLogicalAddr(m_offset);
}
return dst - (uint8_t*)buf;
}
uint64_t position() const override { return m_offset; }
void seek(int64_t offset, int whence) override {
if (whence == SEEK_SET)
m_offset = offset;
else if (whence == SEEK_CUR)
m_offset += offset;
else
return;
setLogicalAddr(m_offset);
}
};
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const override {
bool err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(*this, offset, err));
if (err)
return {};
return ret;
}
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override { return {}; }
bool hasWiiCrypto() const override { return false; }
};
std::unique_ptr<IDiscIO> NewDiscIONFS(std::string_view path) {
bool err = false;
auto ret = std::make_unique<DiscIONFS>(path, err);
if (err)
return {};
return ret;
}
} // namespace nod

View File

@@ -1,305 +1,280 @@
#include <stdio.h> #include <cinttypes>
#include <inttypes.h> #include <cstdint>
#include "nod/Util.hpp" #include <cstring>
#include <memory>
#include "nod/IDiscIO.hpp" #include "nod/IDiscIO.hpp"
#include "nod/IFileIO.hpp" #include "nod/IFileIO.hpp"
#include "nod/Util.hpp"
namespace nod #include <logvisor/logvisor.hpp>
{
#define ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1))) namespace nod {
static uint8_t size_to_shift(uint32_t size) #define ALIGN_LBA(x) (((x) + p->hd_sec_sz - 1) & (~(p->hd_sec_sz - 1)))
{
uint8_t ret = 0; static uint8_t size_to_shift(uint32_t size) {
while (size) uint8_t ret = 0;
{ while (size) {
ret++; ret++;
size>>=1; size >>= 1;
} }
return ret-1; return ret - 1;
} }
class DiscIOWBFS : public IDiscIO class DiscIOWBFS : public IDiscIO {
{ std::unique_ptr<IFileIO> m_fio;
SystemString filepath;
struct WBFSHead struct WBFSHead {
{ uint32_t magic;
uint32_t magic; // parameters copied in the partition for easy dumping, and bug reports
// parameters copied in the partition for easy dumping, and bug reports uint32_t n_hd_sec; // total number of hd_sec in this partition
uint32_t n_hd_sec; // total number of hd_sec in this partition uint8_t hd_sec_sz_s; // sector size in this partition
uint8_t hd_sec_sz_s; // sector size in this partition uint8_t wbfs_sec_sz_s; // size of a wbfs sec
uint8_t wbfs_sec_sz_s; // size of a wbfs sec uint8_t padding3[2];
uint8_t padding3[2]; uint8_t disc_table[0]; // size depends on hd sector size
uint8_t disc_table[0]; // size depends on hd sector size };
}; std::unique_ptr<uint8_t[]> wbfsHead;
std::unique_ptr<uint8_t[]> wbfsHead;
struct WBFSDiscInfo struct WBFSDiscInfo {
{ uint8_t disc_header_copy[0x100];
uint8_t disc_header_copy[0x100]; uint16_t wlba_table[0];
uint16_t wlba_table[0]; };
}; std::unique_ptr<uint8_t[]> wbfsDiscInfo;
std::unique_ptr<uint8_t[]> wbfsDiscInfo;
struct WBFS struct WBFS {
{ /* hdsectors, the size of the sector provided by the hosting hard drive */
/* hdsectors, the size of the sector provided by the hosting hard drive */ uint32_t hd_sec_sz;
uint32_t hd_sec_sz; uint8_t hd_sec_sz_s; // the power of two of the last number
uint8_t hd_sec_sz_s; // the power of two of the last number uint32_t n_hd_sec; // the number of hd sector in the wbfs partition
uint32_t n_hd_sec; // the number of hd sector in the wbfs partition
/* standard wii sector (0x8000 bytes) */ /* standard wii sector (0x8000 bytes) */
uint32_t wii_sec_sz; uint32_t wii_sec_sz;
uint8_t wii_sec_sz_s; uint8_t wii_sec_sz_s;
uint32_t n_wii_sec; uint32_t n_wii_sec;
uint32_t n_wii_sec_per_disc; uint32_t n_wii_sec_per_disc;
/* The size of a wbfs sector */ /* The size of a wbfs sector */
uint32_t wbfs_sec_sz; uint32_t wbfs_sec_sz;
uint32_t wbfs_sec_sz_s; uint32_t wbfs_sec_sz_s;
uint16_t n_wbfs_sec; // this must fit in 16 bit! uint16_t n_wbfs_sec; // this must fit in 16 bit!
uint16_t n_wbfs_sec_per_disc; // size of the lookup table uint16_t n_wbfs_sec_per_disc; // size of the lookup table
uint32_t part_lba; uint32_t part_lba;
uint16_t max_disc; uint16_t max_disc;
uint32_t freeblks_lba; uint32_t freeblks_lba;
uint32_t *freeblks; uint32_t* freeblks;
uint16_t disc_info_sz; uint16_t disc_info_sz;
uint32_t n_disc_open; uint32_t n_disc_open;
} wbfs; } wbfs;
static int _wbfsReadSector(IFileIO::IReadStream& rs, uint32_t lba, uint32_t count, void* buf) static int _wbfsReadSector(IFileIO::IReadStream& rs, uint32_t lba, uint32_t count, void* buf) {
{ uint64_t off = lba;
uint64_t off = lba; off *= 512ULL;
off*=512ULL; rs.seek(off, SEEK_SET);
rs.seek(off, SEEK_SET); if (rs.read(buf, count * 512ULL) != count * 512ULL) {
if (rs.read(buf, count*512ULL) != count*512ULL) LogModule.report(logvisor::Error, FMT_STRING("error reading disc"));
{ return 1;
LogModule.report(logvisor::Error, "error reading disc");
return 1;
}
return 0;
} }
return 0;
}
public: public:
DiscIOWBFS(const SystemString& fpin) DiscIOWBFS(std::string_view fpin) : m_fio(NewFileIO(fpin)) {
: filepath(fpin) /* Temporary file handle to read LBA table */
{ std::unique_ptr<IFileIO::IReadStream> rs = m_fio->beginReadStream();
/* Temporary file handle to read LBA table */ if (!rs)
std::unique_ptr<IFileIO> fio = NewFileIO(filepath); return;
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
if (!rs)
return;
WBFS* p = &wbfs; WBFS* p = &wbfs;
WBFSHead tmpHead; WBFSHead tmpHead;
if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead)) { if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead)) {
LogModule.report(logvisor::Error, "unable to read WBFS head"); LogModule.report(logvisor::Error, FMT_STRING("unable to read WBFS head"));
return; return;
} }
unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s; unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s;
unsigned num_hd_sector = SBig(tmpHead.n_hd_sec); unsigned num_hd_sector = SBig(tmpHead.n_hd_sec);
wbfsHead.reset(new uint8_t[hd_sector_size]); wbfsHead.reset(new uint8_t[hd_sector_size]);
WBFSHead* head = (WBFSHead*)wbfsHead.get(); WBFSHead* head = (WBFSHead*)wbfsHead.get();
rs->seek(0, SEEK_SET); rs->seek(0, SEEK_SET);
if (rs->read(head, hd_sector_size) != hd_sector_size) { if (rs->read(head, hd_sector_size) != hd_sector_size) {
LogModule.report(logvisor::Error, "unable to read WBFS head"); LogModule.report(logvisor::Error, FMT_STRING("unable to read WBFS head"));
return; return;
}
//constants, but put here for consistancy
p->wii_sec_sz = 0x8000;
p->wii_sec_sz_s = size_to_shift(0x8000);
p->n_wii_sec = (num_hd_sector/0x8000)*hd_sector_size;
p->n_wii_sec_per_disc = 143432*2;//support for double layers discs..
p->part_lba = 0;
if (_wbfsReadSector(*rs, p->part_lba, 1, head))
return;
if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) {
LogModule.report(logvisor::Error, "hd sector size doesn't match");
return;
}
if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) {
LogModule.report(logvisor::Error, "hd num sector doesn't match");
return;
}
p->hd_sec_sz = 1<<head->hd_sec_sz_s;
p->hd_sec_sz_s = head->hd_sec_sz_s;
p->n_hd_sec = SBig(head->n_hd_sec);
p->n_wii_sec = (p->n_hd_sec/p->wii_sec_sz)*(p->hd_sec_sz);
p->wbfs_sec_sz_s = head->wbfs_sec_sz_s;
p->wbfs_sec_sz = 1<<p->wbfs_sec_sz_s;
p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s);
p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s);
p->disc_info_sz = ALIGN_LBA(uint16_t(sizeof(WBFSDiscInfo)) + p->n_wbfs_sec_per_disc*2);
p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec/8)>>p->hd_sec_sz_s;
p->freeblks = 0; // will alloc and read only if needed
p->max_disc = (p->freeblks_lba-1)/(p->disc_info_sz>>p->hd_sec_sz_s);
if(p->max_disc > p->hd_sec_sz - sizeof(WBFSHead))
p->max_disc = p->hd_sec_sz - sizeof(WBFSHead);
p->n_disc_open = 0;
int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;
if (head->disc_table[0])
{
wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
if (!wbfsDiscInfo) {
LogModule.report(logvisor::Error, "allocating memory");
return;
}
if (_wbfsReadSector(*rs, p->part_lba+1, disc_info_sz_lba, wbfsDiscInfo.get()))
return;
p->n_disc_open++;
//for(i=0;i<p->n_wbfs_sec_per_disc;i++)
// printf("%d,",wbfs_ntohs(d->header->wlba_table[i]));
}
} }
class ReadStream : public IReadStream // constants, but put here for consistancy
{ p->wii_sec_sz = 0x8000;
friend class DiscIOWBFS; p->wii_sec_sz_s = size_to_shift(0x8000);
const DiscIOWBFS& m_parent; p->n_wii_sec = (num_hd_sector / 0x8000) * hd_sector_size;
std::unique_ptr<IFileIO::IReadStream> fp; p->n_wii_sec_per_disc = 143432 * 2; // support for double layers discs..
uint64_t m_offset; p->part_lba = 0;
std::unique_ptr<uint8_t[]> m_tmpBuffer; if (_wbfsReadSector(*rs, p->part_lba, 1, head))
return;
if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) {
LogModule.report(logvisor::Error, FMT_STRING("hd sector size doesn't match"));
return;
}
if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) {
LogModule.report(logvisor::Error, FMT_STRING("hd num sector doesn't match"));
return;
}
p->hd_sec_sz = 1 << head->hd_sec_sz_s;
p->hd_sec_sz_s = head->hd_sec_sz_s;
p->n_hd_sec = SBig(head->n_hd_sec);
ReadStream(const DiscIOWBFS& parent, std::unique_ptr<IFileIO::IReadStream>&& fpin, uint64_t offset, bool& err) p->n_wii_sec = (p->n_hd_sec / p->wii_sec_sz) * (p->hd_sec_sz);
: m_parent(parent),
fp(std::move(fpin)),
m_offset(offset),
m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) { if (!fp) err = true; }
int wbfsReadSector(uint32_t lba, uint32_t count, void* buf) p->wbfs_sec_sz_s = head->wbfs_sec_sz_s;
{return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf);} p->wbfs_sec_sz = 1 << p->wbfs_sec_sz_s;
p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s);
p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s);
p->disc_info_sz = ALIGN_LBA(uint16_t(sizeof(WBFSDiscInfo)) + p->n_wbfs_sec_per_disc * 2);
int wbfsDiscRead(uint32_t offset, uint8_t *data, uint64_t len) p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec / 8) >> p->hd_sec_sz_s;
{
const WBFS* p = &m_parent.wbfs;
const WBFSDiscInfo* d = (WBFSDiscInfo*)m_parent.wbfsDiscInfo.get();
uint16_t wlba = offset>>(p->wbfs_sec_sz_s-2);
uint32_t iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s;
uint32_t lba_mask = (p->wbfs_sec_sz-1)>>(p->hd_sec_sz_s);
uint64_t lba = (offset>>(p->hd_sec_sz_s-2))&lba_mask;
uint64_t off = offset&((p->hd_sec_sz>>2)-1);
uint16_t iwlba = SBig(d->wlba_table[wlba]);
uint64_t len_copied;
int err = 0;
uint8_t *ptr = data;
if (!iwlba)
return 1;
if (off)
{
off*=4;
err = wbfsReadSector(p->part_lba + (iwlba<<iwlba_shift) + lba, 1, m_tmpBuffer.get());
if (err)
return err;
len_copied = p->hd_sec_sz - off;
if (len < len_copied)
len_copied = len;
memcpy(ptr, m_tmpBuffer.get() + off, len_copied);
len -= len_copied;
ptr += len_copied;
lba++;
if (lba>lba_mask && len)
{
lba=0;
iwlba = SBig(d->wlba_table[++wlba]);
if (!iwlba)
return 1;
}
}
while (len>=p->hd_sec_sz)
{
uint32_t nlb = len>>(p->hd_sec_sz_s);
if (lba + nlb > p->wbfs_sec_sz) // dont cross wbfs sectors.. p->freeblks = 0; // will alloc and read only if needed
nlb = p->wbfs_sec_sz-lba; p->max_disc = (p->freeblks_lba - 1) / (p->disc_info_sz >> p->hd_sec_sz_s);
err = wbfsReadSector(p->part_lba + (iwlba<<iwlba_shift) + lba, nlb, ptr); if (p->max_disc > p->hd_sec_sz - sizeof(WBFSHead))
if (err) p->max_disc = p->hd_sec_sz - sizeof(WBFSHead);
return err;
len -= nlb<<p->hd_sec_sz_s;
ptr += nlb<<p->hd_sec_sz_s;
lba += nlb;
if (lba>lba_mask && len)
{
lba = 0;
iwlba = SBig(d->wlba_table[++wlba]);
if (!iwlba)
return 1;
}
}
if (len)
{
err = wbfsReadSector(p->part_lba + (iwlba<<iwlba_shift) + lba, 1, m_tmpBuffer.get());
if (err)
return err;
memcpy(ptr, m_tmpBuffer.get(), len);
}
return 0;
}
public: p->n_disc_open = 0;
uint64_t read(void* buf, uint64_t length)
{
uint8_t extra[4];
uint64_t rem_offset = m_offset % 4;
if (rem_offset)
{
uint64_t rem_rem = 4 - rem_offset;
if (wbfsDiscRead((uint32_t)(m_offset / 4), extra, 4))
return 0;
memcpy(buf, extra + rem_offset, rem_rem);
if (wbfsDiscRead((uint32_t)(m_offset / 4 + 1), (uint8_t*)buf + rem_rem, length - rem_rem))
return 0;
}
else
{
if (wbfsDiscRead((uint32_t)(m_offset / 4), (uint8_t*)buf, length))
return 0;
}
m_offset += length;
return length;
}
uint64_t position() const {return m_offset;}
void seek(int64_t offset, int whence)
{
if (whence == SEEK_SET)
m_offset = offset;
else if (whence == SEEK_CUR)
m_offset += offset;
}
};
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
{ if (head->disc_table[0]) {
bool Err = false; wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
auto ret = std::unique_ptr<IReadStream>(new ReadStream(*this, NewFileIO(filepath)->beginReadStream(), offset, Err)); if (!wbfsDiscInfo) {
if (Err) LogModule.report(logvisor::Error, FMT_STRING("allocating memory"));
return {}; return;
return ret; }
if (_wbfsReadSector(*rs, p->part_lba + 1, disc_info_sz_lba, wbfsDiscInfo.get()))
return;
p->n_disc_open++;
// for(i=0;i<p->n_wbfs_sec_per_disc;i++)
// printf("%d,",wbfs_ntohs(d->header->wlba_table[i]));
}
}
class ReadStream : public IReadStream {
friend class DiscIOWBFS;
const DiscIOWBFS& m_parent;
std::unique_ptr<IFileIO::IReadStream> fp;
uint64_t m_offset;
std::unique_ptr<uint8_t[]> m_tmpBuffer;
ReadStream(const DiscIOWBFS& parent, std::unique_ptr<IFileIO::IReadStream>&& fpin, uint64_t offset, bool& err)
: m_parent(parent), fp(std::move(fpin)), m_offset(offset), m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) {
if (!fp)
err = true;
} }
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const int wbfsReadSector(uint32_t lba, uint32_t count, void* buf) {
{ return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf);
return std::unique_ptr<IWriteStream>();
} }
int wbfsDiscRead(uint32_t offset, uint8_t* data, uint64_t len) {
const WBFS* p = &m_parent.wbfs;
const WBFSDiscInfo* d = (WBFSDiscInfo*)m_parent.wbfsDiscInfo.get();
uint16_t wlba = offset >> (p->wbfs_sec_sz_s - 2);
uint32_t iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s;
uint32_t lba_mask = (p->wbfs_sec_sz - 1) >> (p->hd_sec_sz_s);
uint64_t lba = (offset >> (p->hd_sec_sz_s - 2)) & lba_mask;
uint64_t off = offset & ((p->hd_sec_sz >> 2) - 1);
uint16_t iwlba = SBig(d->wlba_table[wlba]);
uint64_t len_copied;
int err = 0;
uint8_t* ptr = data;
if (!iwlba)
return 1;
if (off) {
off *= 4;
err = wbfsReadSector(p->part_lba + (iwlba << iwlba_shift) + lba, 1, m_tmpBuffer.get());
if (err)
return err;
len_copied = p->hd_sec_sz - off;
if (len < len_copied)
len_copied = len;
memcpy(ptr, m_tmpBuffer.get() + off, len_copied);
len -= len_copied;
ptr += len_copied;
lba++;
if (lba > lba_mask && len) {
lba = 0;
iwlba = SBig(d->wlba_table[++wlba]);
if (!iwlba)
return 1;
}
}
while (len >= p->hd_sec_sz) {
uint32_t nlb = len >> (p->hd_sec_sz_s);
if (lba + nlb > p->wbfs_sec_sz) // dont cross wbfs sectors..
nlb = p->wbfs_sec_sz - lba;
err = wbfsReadSector(p->part_lba + (iwlba << iwlba_shift) + lba, nlb, ptr);
if (err)
return err;
len -= nlb << p->hd_sec_sz_s;
ptr += nlb << p->hd_sec_sz_s;
lba += nlb;
if (lba > lba_mask && len) {
lba = 0;
iwlba = SBig(d->wlba_table[++wlba]);
if (!iwlba)
return 1;
}
}
if (len) {
err = wbfsReadSector(p->part_lba + (iwlba << iwlba_shift) + lba, 1, m_tmpBuffer.get());
if (err)
return err;
memcpy(ptr, m_tmpBuffer.get(), len);
}
return 0;
}
public:
uint64_t read(void* buf, uint64_t length) override {
uint8_t extra[4];
uint64_t rem_offset = m_offset % 4;
if (rem_offset) {
uint64_t rem_rem = 4 - rem_offset;
if (wbfsDiscRead((uint32_t)(m_offset / 4), extra, 4))
return 0;
memcpy(buf, extra + rem_offset, rem_rem);
if (wbfsDiscRead((uint32_t)(m_offset / 4 + 1), (uint8_t*)buf + rem_rem, length - rem_rem))
return 0;
} else {
if (wbfsDiscRead((uint32_t)(m_offset / 4), (uint8_t*)buf, length))
return 0;
}
m_offset += length;
return length;
}
uint64_t position() const override { return m_offset; }
void seek(int64_t offset, int whence) override {
if (whence == SEEK_SET)
m_offset = offset;
else if (whence == SEEK_CUR)
m_offset += offset;
}
};
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const override {
bool err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(*this, m_fio->beginReadStream(), offset, err));
if (err)
return {};
return ret;
}
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override { return {}; }
}; };
std::unique_ptr<IDiscIO> NewDiscIOWBFS(const SystemChar* path) std::unique_ptr<IDiscIO> NewDiscIOWBFS(std::string_view path) { return std::make_unique<DiscIOWBFS>(path); }
{
return std::unique_ptr<IDiscIO>(new DiscIOWBFS(path));
}
}
} // namespace nod

File diff suppressed because it is too large Load Diff

View File

@@ -1,190 +1,159 @@
#include <stdio.h> #include <cinttypes>
#include <stdlib.h> #include <cstdio>
#include <inttypes.h> #include <cstdint>
#include "nod/Util.hpp" #include <cstdlib>
#include "nod/IFileIO.hpp" #include "nod/IFileIO.hpp"
#include "nod/Util.hpp"
namespace nod #include <logvisor/logvisor.hpp>
{
namespace nod {
class FileIOFILE : public IFileIO {
std::string m_path;
int64_t m_maxWriteSize;
class FileIOFILE : public IFileIO
{
SystemString m_path;
int64_t m_maxWriteSize;
public: public:
FileIOFILE(const SystemString& path, int64_t maxWriteSize) FileIOFILE(std::string_view path, int64_t maxWriteSize) : m_path(path), m_maxWriteSize(maxWriteSize) {}
: m_path(path), m_maxWriteSize(maxWriteSize) {}
FileIOFILE(const SystemChar* path, int64_t maxWriteSize)
: m_path(path), m_maxWriteSize(maxWriteSize) {}
bool exists() bool exists() override {
{ FILE* fp = Fopen(m_path.c_str(), "rb");
FILE* fp = fopen(m_path.c_str(), "rb"); if (!fp)
if (!fp) return false;
return false; fclose(fp);
fclose(fp); return true;
return true; }
}
uint64_t size() uint64_t size() override {
{ FILE* fp = Fopen(m_path.c_str(), "rb");
FILE* fp = fopen(m_path.c_str(), "rb"); if (!fp)
if (!fp) return 0;
return 0; FSeek(fp, 0, SEEK_END);
FSeek(fp, 0, SEEK_END); uint64_t result = FTell(fp);
uint64_t result = FTell(fp); fclose(fp);
fclose(fp); return result;
return result; }
}
struct WriteStream : public IFileIO::IWriteStream struct WriteStream : public IFileIO::IWriteStream {
{ FILE* fp;
FILE* fp; int64_t m_maxWriteSize;
int64_t m_maxWriteSize; WriteStream(std::string_view path, int64_t maxWriteSize, bool& err) : m_maxWriteSize(maxWriteSize) {
WriteStream(const SystemString& path, int64_t maxWriteSize, bool& err) fp = Fopen(path.data(), "wb");
: m_maxWriteSize(maxWriteSize) if (!fp) {
{ LogModule.report(logvisor::Error, FMT_STRING("unable to open '{}' for writing"), path);
fp = fopen(path.c_str(), "wb"); err = true;
if (!fp) }
{
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true;
}
}
WriteStream(const SystemString& path, uint64_t offset, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize)
{
fp = fopen(path.c_str(), "ab");
if (!fp)
goto FailLoc;
fclose(fp);
fp = fopen(path.c_str(), "r+b");
if (!fp)
goto FailLoc;
FSeek(fp, offset, SEEK_SET);
return;
FailLoc:
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true;
}
~WriteStream()
{
fclose(fp);
}
uint64_t write(const void* buf, uint64_t length)
{
if (m_maxWriteSize >= 0)
{
if (FTell(fp) + length > m_maxWriteSize)
{
LogModule.report(logvisor::Error, _S("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize);
return 0;
}
}
return fwrite(buf, 1, length, fp);
}
};
std::unique_ptr<IWriteStream> beginWriteStream() const
{
bool Err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, Err));
if (Err)
return {};
return ret;
} }
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const WriteStream(std::string_view path, uint64_t offset, int64_t maxWriteSize, bool& err)
{ : m_maxWriteSize(maxWriteSize) {
bool Err = false; fp = Fopen(path.data(), "ab");
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, Err)); if (!fp)
if (Err) goto FailLoc;
return {}; fclose(fp);
return ret; fp = Fopen(path.data(), "r+b");
if (!fp)
goto FailLoc;
FSeek(fp, offset, SEEK_SET);
return;
FailLoc:
LogModule.report(logvisor::Error, FMT_STRING("unable to open '{}' for writing"), path);
err = true;
} }
~WriteStream() override { fclose(fp); }
uint64_t write(const void* buf, uint64_t length) override {
if (m_maxWriteSize >= 0) {
if (FTell(fp) + length > m_maxWriteSize) {
LogModule.report(logvisor::Error, FMT_STRING("write operation exceeds file's {}-byte limit"),
m_maxWriteSize);
return 0;
}
}
return fwrite(buf, 1, length, fp);
}
};
struct ReadStream : public IFileIO::IReadStream std::unique_ptr<IWriteStream> beginWriteStream() const override {
{ bool err = false;
FILE* fp; auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, err));
ReadStream(const SystemString& path, bool& err)
{ if (err)
fp = fopen(path.c_str(), "rb"); return {};
if (!fp)
{ return ret;
err = true; }
LogModule.report(logvisor::Error, _S("unable to open '%s' for reading"), path.c_str());
} std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override {
} bool err = false;
ReadStream(const SystemString& path, uint64_t offset, bool& err) auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, err));
: ReadStream(path, err)
{ if (err)
if (err) return {};
return;
FSeek(fp, offset, SEEK_SET); return ret;
} }
~ReadStream()
{ struct ReadStream : public IFileIO::IReadStream {
fclose(fp); FILE* fp;
} ReadStream(std::string_view path, bool& err) {
void seek(int64_t offset, int whence) fp = Fopen(path.data(), "rb");
{ if (!fp) {
FSeek(fp, offset, whence); err = true;
} LogModule.report(logvisor::Error, FMT_STRING("unable to open '{}' for reading"), path);
uint64_t position() const }
{
return FTell(fp);
}
uint64_t read(void* buf, uint64_t length)
{
return fread(buf, 1, length, fp);
}
uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length)
{
uint64_t written = 0;
uint8_t buf[0x7c00];
while (length)
{
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
if (read(buf, thisSz) != thisSz)
{
LogModule.report(logvisor::Error, "unable to read enough from file");
return written;
}
if (discio.write(buf, thisSz) != thisSz)
{
LogModule.report(logvisor::Error, "unable to write enough to disc");
return written;
}
length -= thisSz;
written += thisSz;
}
return written;
}
};
std::unique_ptr<IReadStream> beginReadStream() const
{
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, Err));
if (Err)
return {};
return ret;
} }
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const ReadStream(std::string_view path, uint64_t offset, bool& err) : ReadStream(path, err) {
{ if (err)
bool Err = false; return;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, offset, Err)); FSeek(fp, offset, SEEK_SET);
if (Err)
return {};
return ret;
} }
~ReadStream() override { fclose(fp); }
void seek(int64_t offset, int whence) override { FSeek(fp, offset, whence); }
uint64_t position() const override { return FTell(fp); }
uint64_t read(void* buf, uint64_t length) override { return fread(buf, 1, length, fp); }
uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) override {
uint64_t written = 0;
uint8_t buf[0x7c00];
while (length) {
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
if (read(buf, thisSz) != thisSz) {
LogModule.report(logvisor::Error, FMT_STRING("unable to read enough from file"));
return written;
}
if (discio.write(buf, thisSz) != thisSz) {
LogModule.report(logvisor::Error, FMT_STRING("unable to write enough to disc"));
return written;
}
length -= thisSz;
written += thisSz;
}
return written;
}
};
std::unique_ptr<IReadStream> beginReadStream() const override {
bool err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, err));
if (err)
return {};
return ret;
}
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const override {
bool err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, offset, err));
if (err)
return {};
return ret;
}
}; };
std::unique_ptr<IFileIO> NewFileIO(const SystemString& path, int64_t maxWriteSize) std::unique_ptr<IFileIO> NewFileIO(std::string_view path, int64_t maxWriteSize) {
{ return std::make_unique<FileIOFILE>(path, maxWriteSize);
return std::unique_ptr<IFileIO>(new FileIOFILE(path, maxWriteSize));
} }
std::unique_ptr<IFileIO> NewFileIO(const SystemChar* path, int64_t maxWriteSize) } // namespace nod
{
return std::unique_ptr<IFileIO>(new FileIOFILE(path, maxWriteSize));
}
}

View File

@@ -1,212 +1,215 @@
#include <stdio.h> #include <cinttypes>
#include <stdlib.h> #include <cstdint>
#include <inttypes.h> #include <cstdio>
#include "nod/Util.hpp" #include <cstdlib>
#include "nod/IFileIO.hpp" #include "nod/IFileIO.hpp"
#include "nod/Util.hpp"
namespace nod #include <logvisor/logvisor.hpp>
{
#include <nowide/convert.hpp>
#include <nowide/stackstring.hpp>
namespace nod {
class FileIOWin32 : public IFileIO {
std::wstring m_wpath;
int64_t m_maxWriteSize;
class FileIOWin32 : public IFileIO
{
SystemString m_path;
int64_t m_maxWriteSize;
public: public:
FileIOWin32(const SystemString& path, int64_t maxWriteSize) FileIOWin32(std::string_view path, int64_t maxWriteSize)
: m_path(path), m_maxWriteSize(maxWriteSize) {} : m_wpath(nowide::widen(path))
FileIOWin32(const SystemChar* path, int64_t maxWriteSize) , m_maxWriteSize(maxWriteSize) {}
: m_path(path), m_maxWriteSize(maxWriteSize) {}
bool exists() bool exists() override {
{ #if !WINDOWS_STORE
HANDLE fp = CreateFileW(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, HANDLE fp = CreateFileW(m_wpath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); FILE_ATTRIBUTE_NORMAL, nullptr);
if (fp == INVALID_HANDLE_VALUE) #else
return false; HANDLE fp = CreateFile2(m_path.get(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr);
CloseHandle(fp); #endif
return true; if (fp == INVALID_HANDLE_VALUE)
return false;
CloseHandle(fp);
return true;
}
uint64_t size() override {
#if !WINDOWS_STORE
HANDLE fp = CreateFileW(m_wpath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, nullptr);
#else
HANDLE fp = CreateFile2(m_path.get(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr);
#endif
if (fp == INVALID_HANDLE_VALUE)
return 0;
LARGE_INTEGER sz;
if (!GetFileSizeEx(fp, &sz)) {
CloseHandle(fp);
return 0;
}
CloseHandle(fp);
return sz.QuadPart;
}
struct WriteStream : public IFileIO::IWriteStream {
HANDLE fp;
int64_t m_maxWriteSize;
WriteStream(std::wstring_view wpath, int64_t maxWriteSize, bool& err) : m_maxWriteSize(maxWriteSize) {
#if !WINDOWS_STORE
fp = CreateFileW(wpath.data(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
nullptr);
#else
fp = CreateFile2(wpath.data(), GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS, nullptr);
#endif
if (fp == INVALID_HANDLE_VALUE) {
const nowide::stackstring path(wpath.data(), wpath.data() + wpath.size());
LogModule.report(logvisor::Error, FMT_STRING("unable to open '{}' for writing"), path.get());
err = true;
}
}
WriteStream(std::wstring_view wpath, uint64_t offset, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize) {
#if !WINDOWS_STORE
fp = CreateFileW(wpath.data(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
nullptr);
#else
fp = CreateFile2(wpath.data(), GENERIC_WRITE, FILE_SHARE_WRITE, OPEN_ALWAYS, nullptr);
#endif
if (fp == INVALID_HANDLE_VALUE) {
const nowide::stackstring path(wpath.data(), wpath.data() + wpath.size());
LogModule.report(logvisor::Error, FMT_STRING("unable to open '{}' for writing"), path.get());
err = true;
return;
}
LARGE_INTEGER lioffset;
lioffset.QuadPart = offset;
SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
}
~WriteStream() override { CloseHandle(fp); }
uint64_t write(const void* buf, uint64_t length) override {
if (m_maxWriteSize >= 0) {
LARGE_INTEGER li = {};
LARGE_INTEGER res;
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
if (res.QuadPart + int64_t(length) > m_maxWriteSize) {
LogModule.report(logvisor::Error, FMT_STRING("write operation exceeds file's {}-byte limit"),
m_maxWriteSize);
return 0;
}
}
DWORD ret = 0;
WriteFile(fp, buf, length, &ret, nullptr);
return ret;
}
};
std::unique_ptr<IWriteStream> beginWriteStream() const override {
bool err = false;
auto ret = std::make_unique<WriteStream>(m_wpath, m_maxWriteSize, err);
if (err) {
return nullptr;
} }
uint64_t size() return ret;
{ }
HANDLE fp = CreateFileW(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override {
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); bool err = false;
if (fp == INVALID_HANDLE_VALUE) auto ret = std::make_unique<WriteStream>(m_wpath, offset, m_maxWriteSize, err);
return 0;
LARGE_INTEGER sz; if (err) {
if (!GetFileSizeEx(fp, &sz)) return nullptr;
{
CloseHandle(fp);
return 0;
}
CloseHandle(fp);
return sz.QuadPart;
} }
struct WriteStream : public IFileIO::IWriteStream return ret;
{ }
HANDLE fp;
int64_t m_maxWriteSize;
WriteStream(const SystemString& path, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize)
{
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fp == INVALID_HANDLE_VALUE)
{
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true;
}
}
WriteStream(const SystemString& path, uint64_t offset, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize)
{
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fp == INVALID_HANDLE_VALUE)
{
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true;
return;
}
LARGE_INTEGER lioffset;
lioffset.QuadPart = offset;
SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
}
~WriteStream()
{
CloseHandle(fp);
}
uint64_t write(const void* buf, uint64_t length)
{
if (m_maxWriteSize >= 0)
{
LARGE_INTEGER li = {};
LARGE_INTEGER res;
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
if (res.QuadPart + int64_t(length) > m_maxWriteSize)
{
LogModule.report(logvisor::Error, _S("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize);
return 0;
}
}
DWORD ret = 0; struct ReadStream : public IFileIO::IReadStream {
WriteFile(fp, buf, length, &ret, nullptr); HANDLE fp;
return ret; ReadStream(std::wstring_view wpath, bool& err) {
} #if !WINDOWS_STORE
}; fp = CreateFileW(wpath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
std::unique_ptr<IWriteStream> beginWriteStream() const nullptr);
{ #else
bool Err = false; fp = CreateFile2(wpath.data(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr);
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, Err)); #endif
if (Err) if (fp == INVALID_HANDLE_VALUE) {
return {}; err = true;
return ret; const nowide::stackstring path(wpath.data(), wpath.data() + wpath.size());
LogModule.report(logvisor::Error, FMT_STRING("unable to open '{}' for reading"), path.get());
}
} }
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const ReadStream(std::wstring_view wpath, uint64_t offset, bool& err) : ReadStream(wpath, err) {
{ if (err)
bool Err = false; return;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, Err)); LARGE_INTEGER lioffset;
if (Err) lioffset.QuadPart = offset;
return {}; SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
return ret; }
~ReadStream() override { CloseHandle(fp); }
void seek(int64_t offset, int whence) override {
LARGE_INTEGER li;
li.QuadPart = offset;
SetFilePointerEx(fp, li, nullptr, whence);
}
uint64_t position() const override {
LARGE_INTEGER li = {};
LARGE_INTEGER res;
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
return res.QuadPart;
}
uint64_t read(void* buf, uint64_t length) override {
DWORD ret = 0;
ReadFile(fp, buf, length, &ret, nullptr);
return ret;
}
uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) override {
uint64_t written = 0;
uint8_t buf[0x7c00];
while (length) {
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
if (read(buf, thisSz) != thisSz) {
LogModule.report(logvisor::Error, FMT_STRING("unable to read enough from file"));
return written;
}
if (discio.write(buf, thisSz) != thisSz) {
LogModule.report(logvisor::Error, FMT_STRING("unable to write enough to disc"));
return written;
}
length -= thisSz;
written += thisSz;
}
return written;
}
};
std::unique_ptr<IReadStream> beginReadStream() const override {
bool err = false;
auto ret = std::make_unique<ReadStream>(m_wpath, err);
if (err) {
return nullptr;
} }
struct ReadStream : public IFileIO::IReadStream return ret;
{ }
HANDLE fp;
ReadStream(const SystemString& path, bool& err) std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const override {
{ bool err = false;
fp = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, auto ret = std::make_unique<ReadStream>(m_wpath, offset, err);
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fp == INVALID_HANDLE_VALUE) if (err) {
{ return nullptr;
err = true;
LogModule.report(logvisor::Error, _S("unable to open '%s' for reading"), path.c_str());
}
}
ReadStream(const SystemString& path, uint64_t offset, bool& err)
: ReadStream(path, err)
{
if (err)
return;
LARGE_INTEGER lioffset;
lioffset.QuadPart = offset;
SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
}
~ReadStream()
{
CloseHandle(fp);
}
void seek(int64_t offset, int whence)
{
LARGE_INTEGER li;
li.QuadPart = offset;
SetFilePointerEx(fp, li, nullptr, whence);
}
uint64_t position() const
{
LARGE_INTEGER li = {};
LARGE_INTEGER res;
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
return res.QuadPart;
}
uint64_t read(void* buf, uint64_t length)
{
DWORD ret = 0;
ReadFile(fp, buf, length, &ret, nullptr);
return ret;
}
uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length)
{
uint64_t written = 0;
uint8_t buf[0x7c00];
while (length)
{
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
if (read(buf, thisSz) != thisSz)
{
LogModule.report(logvisor::Error, "unable to read enough from file");
return written;
}
if (discio.write(buf, thisSz) != thisSz)
{
LogModule.report(logvisor::Error, "unable to write enough to disc");
return written;
}
length -= thisSz;
written += thisSz;
}
return written;
}
};
std::unique_ptr<IReadStream> beginReadStream() const
{
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, Err));
if (Err)
return {};
return ret;
}
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
{
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, offset, Err));
if (Err)
return {};
return ret;
} }
return ret;
}
}; };
std::unique_ptr<IFileIO> NewFileIO(const SystemString& path, int64_t maxWriteSize) std::unique_ptr<IFileIO> NewFileIO(std::string_view path, int64_t maxWriteSize) {
{ return std::make_unique<FileIOWin32>(path, maxWriteSize);
return std::unique_ptr<IFileIO>(new FileIOWin32(path, maxWriteSize));
} }
std::unique_ptr<IFileIO> NewFileIO(const SystemChar* path, int64_t maxWriteSize) } // namespace nod
{
return std::unique_ptr<IFileIO>(new FileIOWin32(path, maxWriteSize));
}
}

3149
lib/OSUTF.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,88 +1,87 @@
#include <stdio.h>
#include "nod/nod.hpp" #include "nod/nod.hpp"
#include "nod/DiscBase.hpp"
namespace nod #include <cstdio>
{
#include "nod/DiscBase.hpp"
#include "nod/DiscGCN.hpp"
#include "nod/DiscWii.hpp"
namespace nod {
logvisor::Module LogModule("nod"); logvisor::Module LogModule("nod");
std::unique_ptr<IDiscIO> NewDiscIOISO(const SystemChar* path); std::unique_ptr<IDiscIO> NewDiscIOISO(std::string_view path);
std::unique_ptr<IDiscIO> NewDiscIOWBFS(const SystemChar* path); std::unique_ptr<IDiscIO> NewDiscIOWBFS(std::string_view path);
std::unique_ptr<IDiscIO> NewDiscIONFS(std::string_view path);
std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii) std::unique_ptr<DiscBase> OpenDiscFromImage(std::string_view path, bool& isWii) {
{ /* Temporary file handle to determine image type */
/* Temporary file handle to determine image type */ std::unique_ptr<IFileIO> fio = NewFileIO(path);
std::unique_ptr<IFileIO> fio = NewFileIO(path); if (!fio->exists()) {
if (!fio->exists()) LogModule.report(logvisor::Error, FMT_STRING("Unable to open '{}'"), path);
{ return {};
LogModule.report(logvisor::Error, _S("Unable to open '%s'"), path); }
return {}; std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
} if (!rs)
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream(); return {};
if (!rs)
return {};
isWii = false; isWii = false;
std::unique_ptr<IDiscIO> discIO; std::unique_ptr<IDiscIO> discIO;
uint32_t magic = 0; uint32_t magic = 0;
if (rs->read(&magic, 4) != 4) if (rs->read(&magic, 4) != 4) {
{ LogModule.report(logvisor::Error, FMT_STRING("Unable to read magic from '{}'"), path);
LogModule.report(logvisor::Error, _S("Unable to read magic from '%s'"), path); return {};
return {}; }
}
if (magic == nod::SBig((uint32_t)'WBFS')) using SignedSize = std::make_signed<std::string::size_type>::type;
{ const auto dotPos = SignedSize(path.rfind('.'));
discIO = NewDiscIOWBFS(path); const auto slashPos = SignedSize(path.find_last_of("/\\"));
isWii = true; if (magic == nod::SBig((uint32_t)'WBFS')) {
} discIO = NewDiscIOWBFS(path);
else isWii = true;
{ } else if (path.size() > 4 && dotPos != -1 && dotPos > slashPos &&
rs->seek(0x18, SEEK_SET); !path.compare(slashPos + 1, 4, "hif_") &&
rs->read(&magic, 4); !path.compare(dotPos, path.size() - dotPos, ".nfs")) {
magic = nod::SBig(magic); discIO = NewDiscIONFS(path);
if (magic == 0x5D1C9EA3) isWii = true;
{ } else {
discIO = NewDiscIOISO(path); rs->seek(0x18, SEEK_SET);
isWii = true; rs->read(&magic, 4);
} magic = nod::SBig(magic);
else if (magic == 0x5D1C9EA3) {
{ discIO = NewDiscIOISO(path);
rs->read(&magic, 4); isWii = true;
magic = nod::SBig(magic); } else {
if (magic == 0xC2339F3D) rs->read(&magic, 4);
discIO = NewDiscIOISO(path); magic = nod::SBig(magic);
} if (magic == 0xC2339F3D)
discIO = NewDiscIOISO(path);
} }
}
if (!discIO) if (!discIO) {
{ LogModule.report(logvisor::Error, FMT_STRING("'{}' is not a valid image"), path);
LogModule.report(logvisor::Error, _S("'%s' is not a valid image"), path); return {};
return {}; }
}
bool Err = false; bool err = false;
std::unique_ptr<DiscBase> ret; std::unique_ptr<DiscBase> ret;
if (isWii) if (isWii) {
{ ret = std::make_unique<DiscWii>(std::move(discIO), err);
ret = std::unique_ptr<DiscBase>(new DiscWii(std::move(discIO), Err)); if (err)
if (Err) return {};
return {};
return ret;
}
ret = std::unique_ptr<DiscBase>(new DiscGCN(std::move(discIO), Err));
if (Err)
return {};
return ret; return ret;
}
ret = std::make_unique<DiscGCN>(std::move(discIO), err);
if (err)
return {};
return ret;
} }
std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path) std::unique_ptr<DiscBase> OpenDiscFromImage(std::string_view path) {
{ bool isWii;
bool isWii; return OpenDiscFromImage(path, isWii);
return OpenDiscFromImage(path, isWii);
}
} }
} // namespace nod