121 Commits
v1.0 ... master

Author SHA1 Message Date
6febcc2c79 Fetch nowide on Windows 2025-04-04 17:29:38 -06:00
9584303083 Replace logvisor with spdlog 2025-04-03 21:06:08 -06:00
b513a7f4e0 Update logvisor 2022-08-03 18:16:55 -04:00
258076c298 Fix OSUTF32To8 and OSUTF16To32 2022-02-28 22:04:40 -08:00
72893dcacb Refactor to remove <Windows.h> from headers 2022-02-22 00:48:22 -05:00
30697375ad update logvisor 2022-01-10 23:28:51 -08:00
2f53b21740 Stop trying to make <ranges> happen, it's not going to happen 2021-10-25 22:34:12 -04:00
45f56d21f1 DirectoryEnumerator: Only include <ranges> on Windows 2021-10-25 19:34:50 -04:00
27da856af3 Update logvisor 2021-10-25 19:10:27 -04:00
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
36 changed files with 8784 additions and 5594 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,111 @@
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()
if (NOT TARGET logvisor) include (CMakePackageConfigHelpers)
add_subdirectory(logvisor)
set(LOGVISOR_INCLUDE_DIR logvisor/include)
endif()
include_directories(include ${LOGVISOR_INCLUDE_DIR}) if (WIN32 AND NOT TARGET nowide)
file(GLOB NOD_HEADERS include/nod/*.h*) include(FetchContent)
FetchContent_Declare(
nowide
URL https://github.com/boostorg/nowide/releases/download/v11.3.0/nowide_standalone_v11.3.0.tar.gz
URL_HASH SHA256=153ac93173c8de9c08e7701e471fa750f84c27e51fe329570c5aa06016591f8c
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(nowide)
endif ()
if(NOT TARGET spdlog)
find_package(spdlog REQUIRED)
endif()
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

@@ -1,11 +1,13 @@
add_executable(nodtool main.cpp) add_executable(nodtool main.cpp)
target_link_libraries(nodtool nod logvisor) target_link_libraries(nodtool nod spdlog::spdlog)
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,316 @@
#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 <spdlog/spdlog.h>
{
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" #include "../lib/Util.hpp"
" nodtool mergewii <fsroot-in> <image-in> [<image-out>]\n");
static void printHelp() {
fmt::print(stderr,
"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
{ int argidx = 1;
if (argc < 3 || std::string errand;
(!strcasecmp(argv[1], _S("makegcn")) && argc < 3) || bool verbose = false;
(!strcasecmp(argv[1], _S("makewii")) && argc < 3) || nod::ExtractionContext ctx = {true, [&](std::string_view str, float c) {
(!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) || if (verbose)
(!strcasecmp(argv[1], _S("mergewii")) && argc < 4)) fmt::print(stderr, "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;
}
}
if (errand.empty()) {
printHelp(); printHelp();
return 1; return 1;
} }
/* Enable logging to console */ auto progFunc = [&](float prog, std::string_view name, size_t bytes) {
logvisor::RegisterStandardExceptions(); fmt::print("\r ");
logvisor::RegisterConsoleLogger(); if (bytes != SIZE_MAX)
fmt::print("\r{:g}% {} {} B", prog * 100.f, name, bytes);
bool verbose = false;
nod::ExtractionContext ctx = {true,
[&](const std::string& str, float c) {
if (verbose)
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 else
outDir = argv[a]; fmt::print("\r{:g}% {}", prog * 100.f, name);
}
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); fflush(stdout);
}; };
if (!strcasecmp(argv[1], _S("extract"))) 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();
return 1;
}
}
if (dirOut.empty())
dirOut = ".";
bool isWii; bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(inDir, isWii); std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
if (!disc) if (!disc)
return 1; return 1;
nod::Mkdir(outDir, 0755); nod::Mkdir(dirOut.c_str(), 0755);
nod::Partition* dataPart = disc->getDataPartition(); nod::IPartition* dataPart = disc->getDataPartition();
if (!dataPart) if (!dataPart)
return 1; return 1;
if (!dataPart->extractToDirectory(outDir, ctx)) 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; return 1;
} }
else if (!strcasecmp(argv[1], _S("makegcn"))) }
{ if (imageOut.empty())
imageOut = fsrootIn + ".gcm";
/* Pre-validate path */ /* Pre-validate path */
nod::Sstat theStat; nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
{ spdlog::error("unable to stat {} as directory");
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
return 1; return 1;
} }
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2]) == -1) if (!nod::DiscBuilderGCN::CalculateTotalSizeRequired(fsrootIn))
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 4) nod::DiscBuilderGCN b(imageOut, progFunc);
{ ret = b.buildFromDirectory(fsrootIn);
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"); fmt::print("\n");
if (ret != nod::EBuildResult::Success) if (ret != nod::EBuildResult::Success)
return 1; 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;
} }
else if (!strcasecmp(argv[1], _S("makewii"))) }
{ if (imageOut.empty())
imageOut = fsrootIn + ".iso";
/* Pre-validate path */ /* Pre-validate path */
nod::Sstat theStat; nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
{ spdlog::error("unable to stat {} as directory");
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[4]);
return 1; return 1;
} }
bool dual = false; bool dual = false;
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual) == -1) if (!nod::DiscBuilderWii::CalculateTotalSizeRequired(fsrootIn, dual))
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 4) nod::DiscBuilderWii b(imageOut, dual, progFunc);
{ ret = b.buildFromDirectory(fsrootIn);
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"); fmt::print("\n");
if (ret != nod::EBuildResult::Success) if (ret != nod::EBuildResult::Success)
return 1; return 1;
} } else if (errand == "mergegcn") {
else if (!strcasecmp(argv[1], _S("mergegcn"))) std::string fsrootIn;
{ std::string imageIn;
/* Pre-validate paths */ std::string imageOut;
nod::Sstat theStat; while (argidx < argc) {
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) if (fsrootIn.empty()) {
{ fsrootIn = argv[argidx];
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]); ++argidx;
continue;
} else if (imageIn.empty()) {
imageIn = argv[argidx];
++argidx;
continue;
} else if (imageOut.empty()) {
imageOut = argv[argidx];
++argidx;
continue;
} else {
printHelp();
return 1; return 1;
} }
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) }
{ if (imageOut.empty())
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[3]); imageOut = fsrootIn + ".gcm";
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
spdlog::error("unable to stat {} as directory");
return 1;
}
if (nod::Stat(imageIn.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) {
spdlog::error("unable to stat {} as file");
return 1; return 1;
} }
bool isWii; bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii); std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
if (!disc) if (!disc) {
{ spdlog::error("unable to open image {}");
nod::LogModule.report(logvisor::Error, _S("unable to open image %s"), argv[3]);
return 1; return 1;
} }
if (isWii) if (isWii) {
{ spdlog::error("Wii images should be merged with 'mergewii'");
nod::LogModule.report(logvisor::Error, _S("Wii images should be merged with 'mergewii'"));
return 1; return 1;
} }
if (nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), argv[2]) == -1) if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), fsrootIn))
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 5) nod::DiscMergerGCN b(imageOut, static_cast<nod::DiscGCN&>(*disc), progFunc);
{ ret = b.mergeFromDirectory(fsrootIn);
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"); fmt::print("\n");
if (ret != nod::EBuildResult::Success) if (ret != nod::EBuildResult::Success)
return 1; return 1;
} } else if (errand == "mergewii") {
else if (!strcasecmp(argv[1], _S("mergewii"))) std::string fsrootIn;
{ std::string imageIn;
/* Pre-validate paths */ std::string imageOut;
nod::Sstat theStat; while (argidx < argc) {
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) if (fsrootIn.empty()) {
{ fsrootIn = argv[argidx];
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]); ++argidx;
continue;
} else if (imageIn.empty()) {
imageIn = argv[argidx];
++argidx;
continue;
} else if (imageOut.empty()) {
imageOut = argv[argidx];
++argidx;
continue;
} else {
printHelp();
return 1; return 1;
} }
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) }
{ if (imageOut.empty())
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[3]); imageOut = fsrootIn + ".iso";
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
spdlog::error("unable to stat {} as directory");
return 1;
}
if (nod::Stat(imageIn.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) {
spdlog::error("unable to stat {} as file");
return 1; return 1;
} }
bool isWii; bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii); std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
if (!disc) if (!disc) {
{ spdlog::error("unable to open image {}");
nod::LogModule.report(logvisor::Error, _S("unable to open image %s"), argv[3]);
return 1; return 1;
} }
if (!isWii) if (!isWii) {
{ spdlog::error("GameCube images should be merged with 'mergegcn'");
nod::LogModule.report(logvisor::Error, _S("GameCube images should be merged with 'mergegcn'"));
return 1; return 1;
} }
bool dual = false; bool dual = false;
if (nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), argv[2], dual) == -1) if (!nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), fsrootIn, dual))
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 5) nod::DiscMergerWii b(imageOut, static_cast<nod::DiscWii&>(*disc), dual, progFunc);
{ ret = b.mergeFromDirectory(fsrootIn);
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"); fmt::print("\n");
if (ret != nod::EBuildResult::Success) if (ret != nod::EBuildResult::Success)
return 1; return 1;
} } else {
else
{
printHelp(); printHelp();
return 1; return 1;
} }
nod::LogModule.report(logvisor::Info, _S("Success!")); spdlog::info("Success!");
return 0; 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>
#include <string>
#include <string_view>
namespace nod namespace nod {
{
struct CaseInsensitiveCompare /**
{ * @brief Case-insensitive comparator for std::map sorting
bool operator()(const std::string& lhs, const std::string& rhs) const */
{ struct CaseInsensitiveCompare {
#if _WIN32 // Allow heterogeneous lookup with maps that use this comparator.
if (_stricmp(lhs.c_str(), rhs.c_str()) < 0) using is_transparent = void;
#else
if (strcasecmp(lhs.c_str(), rhs.c_str()) < 0) bool operator()(std::string_view lhs, std::string_view rhs) const {
#endif return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](char lhs, char rhs) {
return true; return std::tolower(static_cast<unsigned char>(lhs)) < std::tolower(static_cast<unsigned char>(rhs));
return false; });
} }
#if _WIN32
bool operator()(const std::wstring& lhs, const std::wstring& rhs) const
{
if (_wcsicmp(lhs.c_str(), rhs.c_str()) < 0)
return true;
return false;
}
#endif
}; };
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,
DirsThenFilesSorted
};
struct Entry
{
SystemString m_path;
SystemString m_name;
size_t m_fileSz; size_t m_fileSz;
bool m_isDir; 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,65 +1,52 @@
#ifndef __NOD_DISC_BASE__ #pragma once
#define __NOD_DISC_BASE__
#include <vector> #include <cstddef>
#include <memory> #include <cstdint>
#include <string> #include <cstring>
#include <unordered_map>
#include <stdio.h>
#include <stdint.h>
#include <functional> #include <functional>
#include "Util.hpp" #include <optional>
#include "IDiscIO.hpp" #include <string>
#include "IFileIO.hpp" #include <string_view>
namespace nod #include "nod/IDiscIO.hpp"
{ #include "nod/IFileIO.hpp"
#include "nod/OSUTF.h"
#include "nod/Endian.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,
Update,
Channel
};
const SystemChar* getKindString(PartitionKind kind);
class FSTNode enum class PartitionKind : uint32_t { Data, Update, Channel };
{ const char* getKindString(PartitionKind kind);
class FSTNode {
uint32_t typeAndNameOffset; uint32_t typeAndNameOffset;
uint32_t offset; uint32_t offset;
uint32_t length; 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);
} }
inline bool isDir() const {return ((SBig(typeAndNameOffset) >> 24) != 0);} bool isDir() const { return ((SBig(typeAndNameOffset) >> 24) != 0); }
inline uint32_t getNameOffset() const {return SBig(typeAndNameOffset) & 0xffffff;} uint32_t getNameOffset() const { return SBig(typeAndNameOffset) & 0xffffff; }
inline uint32_t getOffset() const {return SBig(offset);} uint32_t getOffset() const { return SBig(offset); }
inline uint32_t getLength() const {return SBig(length);} uint32_t getLength() const { return SBig(length); }
void incrementLength() void incrementLength() {
{
uint32_t orig = SBig(length); uint32_t orig = SBig(length);
++orig; ++orig;
length = SBig(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;
@@ -85,19 +72,16 @@ struct Header
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; err = true;
return; 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);
@@ -113,8 +97,7 @@ struct Header
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);
@@ -132,8 +115,7 @@ struct Header
}; };
/* 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;
@@ -148,8 +130,7 @@ struct BI2Header
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);
@@ -166,8 +147,7 @@ struct BI2Header
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);
@@ -186,37 +166,14 @@ struct BI2Header
}; };
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:
{
public:
virtual ~IPartition() = default;
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
{
public:
enum class Kind
{
File,
Directory
};
private:
friend class IPartition; friend class IPartition;
const IPartition& m_parent; const IPartition& m_parent;
Kind m_kind; Kind m_kind;
@@ -228,69 +185,49 @@ public:
std::vector<Node>::iterator m_childrenBegin; std::vector<Node>::iterator m_childrenBegin;
std::vector<Node>::iterator m_childrenEnd; std::vector<Node>::iterator m_childrenEnd;
public: public:
Node(const IPartition& parent, const FSTNode& node, const char* name) Node(const IPartition& parent, const FSTNode& node, std::string_view name);
: m_parent(parent), Kind getKind() const { return m_kind; }
m_kind(node.isDir() ? Kind::Directory : Kind::File), std::string_view getName() const { return m_name; }
m_discOffset(parent.normalizeOffset(node.getOffset())), uint64_t size() const { return m_discLength; }
m_discLength(node.getLength()), std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset = 0) const;
m_name(name) {} std::unique_ptr<uint8_t[]> getBuf() const;
inline Kind getKind() const {return m_kind;} std::vector<Node>::iterator rawBegin() const { return m_childrenBegin; }
inline const std::string& getName() const {return m_name;} std::vector<Node>::iterator rawEnd() const { return m_childrenEnd; }
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> class DirectoryIterator {
{
friend class Node; friend class Node;
std::vector<Node>::iterator m_it; std::vector<Node>::iterator m_it;
DirectoryIterator(const std::vector<Node>::iterator& it) DirectoryIterator(const std::vector<Node>::iterator& it) : m_it(it) {}
: m_it(it) {}
public: public:
inline bool operator!=(const DirectoryIterator& other) {return m_it != other.m_it;} using iterator_category = std::forward_iterator_tag;
inline bool operator==(const DirectoryIterator& other) {return m_it == other.m_it;} using value_type = Node;
inline DirectoryIterator& operator++() 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) if (m_it->m_kind == Kind::Directory)
m_it = m_it->rawEnd(); m_it = m_it->rawEnd();
else else
++m_it; ++m_it;
return *this; return *this;
} }
inline Node& operator*() {return *m_it;} Node& operator*() { return *m_it; }
inline Node* operator->() {return &*m_it;} const Node& operator*() const { return *m_it; }
Node* operator->() { return &*m_it; }
const Node* operator->() const { return &*m_it; }
}; };
inline DirectoryIterator begin() const {return DirectoryIterator(m_childrenBegin);} DirectoryIterator begin() const { return DirectoryIterator(m_childrenBegin); }
inline DirectoryIterator end() const {return DirectoryIterator(m_childrenEnd);} DirectoryIterator end() const { return DirectoryIterator(m_childrenEnd); }
inline DirectoryIterator find(const std::string& name) const DirectoryIterator find(std::string_view name) const {
{ if (m_kind == Kind::Directory) {
if (m_kind == Kind::Directory) DirectoryIterator it = begin();
{ for (; it != end(); ++it) {
DirectoryIterator it=begin(); if (it->getName() == name)
for (; it != end() ; ++it)
{
if (!it->getName().compare(name))
return it; return it;
} }
return it; return it;
@@ -298,9 +235,25 @@ public:
return end(); return end();
} }
bool extractToDirectory(const SystemString& basePath, const ExtractionContext& ctx) const; bool extractToDirectory(std::string_view basePath, const ExtractionContext& ctx) const;
};
class IPartition {
public:
virtual ~IPartition() = default;
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;
}; };
protected:
protected:
Header m_header; Header m_header;
BI2Header m_bi2Header; BI2Header m_bi2Header;
uint64_t m_dolOff; uint64_t m_dolOff;
@@ -313,7 +266,6 @@ public:
std::vector<FSTNode> m_buildNodes; std::vector<FSTNode> m_buildNodes;
std::vector<std::string> m_buildNames; std::vector<std::string> m_buildNames;
size_t m_buildNameOff = 0; size_t m_buildNameOff = 0;
void recursiveBuildNodes(const SystemChar* dirIn, std::function<void(void)> incParents);
uint64_t m_dolSz; uint64_t m_dolSz;
void parseDOL(IPartReadStream& s); void parseDOL(IPartReadStream& s);
@@ -322,11 +274,11 @@ public:
PartitionKind m_kind; PartitionKind m_kind;
uint64_t m_offset; uint64_t m_offset;
bool m_isWii; bool m_isWii;
public:
public:
mutable size_t m_curNodeIdx = 0; mutable size_t m_curNodeIdx = 0;
float getProgressFactor() const { return getNodeCount() ? m_curNodeIdx / float(getNodeCount()) : 0.f; } float getProgressFactor() const { return getNodeCount() ? m_curNodeIdx / float(getNodeCount()) : 0.f; }
float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const {
{
if (!getNodeCount()) if (!getNodeCount())
return 0.f; return 0.f;
@@ -338,132 +290,128 @@ public:
IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset) IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset)
: m_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii) {} : m_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii) {}
virtual uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset;} virtual uint64_t normalizeOffset(uint64_t anOffset) const { return anOffset; }
inline PartitionKind getKind() const {return m_kind;} PartitionKind getKind() const { return m_kind; }
inline bool isWii() const {return m_isWii;} bool isWii() const { return m_isWii; }
inline uint64_t getDiscOffset() const {return m_offset;} uint64_t getDiscOffset() const { return m_offset; }
virtual std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset=0) const=0; virtual std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset = 0) const = 0;
inline std::unique_ptr<IPartReadStream> beginDOLReadStream(uint64_t offset=0) const std::unique_ptr<IPartReadStream> beginDOLReadStream(uint64_t offset = 0) const {
{return beginReadStream(m_dolOff + offset);} return beginReadStream(m_dolOff + offset);
inline std::unique_ptr<IPartReadStream> beginFSTReadStream(uint64_t offset=0) const }
{return beginReadStream(m_fstOff + offset);} std::unique_ptr<IPartReadStream> beginFSTReadStream(uint64_t offset = 0) const {
inline std::unique_ptr<IPartReadStream> beginApploaderReadStream(uint64_t offset=0) const return beginReadStream(m_fstOff + offset);
{return beginReadStream(0x2440 + offset);} }
inline const Node& getFSTRoot() const {return m_nodes[0];} std::unique_ptr<IPartReadStream> beginApploaderReadStream(uint64_t offset = 0) const {
inline Node& getFSTRoot() {return m_nodes[0];} return beginReadStream(0x2440 + offset);
bool extractToDirectory(const SystemString& path, const ExtractionContext& ctx); }
const Node& getFSTRoot() const { return m_nodes[0]; }
Node& getFSTRoot() { return m_nodes[0]; }
bool extractToDirectory(std::string_view path, const ExtractionContext& ctx);
inline uint64_t getDOLSize() const {return m_dolSz;} uint64_t getDOLSize() const { return m_dolSz; }
inline std::unique_ptr<uint8_t[]> getDOLBuf() const std::unique_ptr<uint8_t[]> getDOLBuf() const {
{
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_dolSz]); std::unique_ptr<uint8_t[]> buf(new uint8_t[m_dolSz]);
beginDOLReadStream()->read(buf.get(), m_dolSz); beginDOLReadStream()->read(buf.get(), m_dolSz);
return buf; return buf;
} }
inline uint64_t getFSTSize() const {return m_fstSz;} uint64_t getFSTSize() const { return m_fstSz; }
inline std::unique_ptr<uint8_t[]> getFSTBuf() const std::unique_ptr<uint8_t[]> getFSTBuf() const {
{
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_fstSz]); std::unique_ptr<uint8_t[]> buf(new uint8_t[m_fstSz]);
beginFSTReadStream()->read(buf.get(), m_fstSz); beginFSTReadStream()->read(buf.get(), m_fstSz);
return buf; return buf;
} }
inline uint64_t getApploaderSize() const {return m_apploaderSz;} uint64_t getApploaderSize() const { return m_apploaderSz; }
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const std::unique_ptr<uint8_t[]> getApploaderBuf() const {
{
std::unique_ptr<uint8_t[]> buf(new uint8_t[m_apploaderSz]); std::unique_ptr<uint8_t[]> buf(new uint8_t[m_apploaderSz]);
beginApploaderReadStream()->read(buf.get(), m_apploaderSz); beginApploaderReadStream()->read(buf.get(), m_apploaderSz);
return buf; return buf;
} }
inline size_t getNodeCount() const { return m_nodes.size(); } size_t getNodeCount() const { return m_nodes.size(); }
inline const Header& getHeader() const { return m_header; } const Header& getHeader() const { return m_header; }
inline const BI2Header& getBI2() const { return m_bi2Header; } const BI2Header& getBI2() const { return m_bi2Header; }
virtual bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const { return true; } virtual bool extractCryptoFiles(std::string_view path, const ExtractionContext& ctx) const { return true; }
}; bool extractSysFiles(std::string_view path, const ExtractionContext& ctx) const;
};
class DiscBase {
public:
virtual ~DiscBase() = default;
protected: protected:
std::unique_ptr<IDiscIO> m_discIO; std::unique_ptr<IDiscIO> m_discIO;
Header m_header; Header m_header;
std::vector<std::unique_ptr<IPartition>> m_partitions; std::vector<std::unique_ptr<IPartition>> m_partitions;
public:
DiscBase(std::unique_ptr<IDiscIO>&& dio, bool& err)
: m_discIO(std::move(dio)), m_header(*m_discIO, err) {}
inline const Header& getHeader() const {return m_header;} public:
inline const IDiscIO& getDiscIO() const {return *m_discIO;} DiscBase(std::unique_ptr<IDiscIO>&& dio, bool& err) : m_discIO(std::move(dio)), m_header(*m_discIO, err) {}
inline size_t getPartitonNodeCount(size_t partition = 0) const
{ const Header& getHeader() const { return m_header; }
if (partition > m_partitions.size()) const IDiscIO& getDiscIO() const { return *m_discIO; }
size_t getPartitionNodeCount(size_t partition = 0) const {
if (partition >= m_partitions.size()) {
return -1; return -1;
}
return m_partitions[partition]->getNodeCount(); return m_partitions[partition]->getNodeCount();
} }
inline IPartition* getDataPartition() IPartition* getDataPartition() {
{
for (const std::unique_ptr<IPartition>& part : m_partitions) for (const std::unique_ptr<IPartition>& part : m_partitions)
if (part->getKind() == PartitionKind::Data) if (part->getKind() == PartitionKind::Data)
return part.get(); return part.get();
return nullptr; return nullptr;
} }
inline IPartition* getUpdatePartition() IPartition* getUpdatePartition() {
{
for (const std::unique_ptr<IPartition>& part : m_partitions) for (const std::unique_ptr<IPartition>& part : m_partitions)
if (part->getKind() == PartitionKind::Update) if (part->getKind() == PartitionKind::Update)
return part.get(); return part.get();
return nullptr; return nullptr;
} }
inline void extractToDirectory(const SystemString& path, const ExtractionContext& ctx) void extractToDirectory(std::string_view path, const ExtractionContext& ctx) {
{
for (std::unique_ptr<IPartition>& part : m_partitions) for (std::unique_ptr<IPartition>& part : m_partitions)
part->extractToDirectory(path, ctx); part->extractToDirectory(path, ctx);
} }
virtual bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const=0; virtual bool extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const = 0;
}; };
class DiscBuilderBase class DiscBuilderBase {
{
friend class DiscMergerWii; friend class DiscMergerWii;
public: public:
class PartitionBuilderBase class PartitionBuilderBase {
{
public: public:
virtual ~PartitionBuilderBase() = default; virtual ~PartitionBuilderBase() = default;
protected: protected:
std::unordered_map<SystemString, std::pair<uint64_t,uint64_t>> m_fileOffsetsSizes; std::unordered_map<std::string, std::pair<uint64_t, uint64_t>> m_fileOffsetsSizes;
std::vector<FSTNode> m_buildNodes; std::vector<FSTNode> m_buildNodes;
std::vector<std::string> m_buildNames; std::vector<std::string> m_buildNames;
size_t m_buildNameOff = 0; size_t m_buildNameOff = 0;
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0; virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) = 0;
virtual uint32_t packOffset(uint64_t offset) const=0; virtual uint32_t packOffset(uint64_t offset) const = 0;
void recursiveBuildNodesPre(const SystemChar* dirIn); void recursiveBuildNodesPre(std::string_view dirIn);
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn); bool recursiveBuildNodes(IPartWriteStream& ws, bool system, std::string_view dirIn);
bool recursiveBuildFST(const SystemChar* dirIn, bool recursiveBuildFST(std::string_view dirIn, std::function<void(void)> incParents, size_t parentDirIdx);
std::function<void(void)> incParents);
void recursiveMergeNodesPre(const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn); void recursiveMergeNodesPre(const Node* nodeIn, std::string_view dirIn);
bool recursiveMergeNodes(IPartWriteStream& ws, bool system, bool recursiveMergeNodes(IPartWriteStream& ws, bool system, const Node* nodeIn, std::string_view dirIn,
const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn, std::string_view keyPath);
const SystemString& keyPath); bool recursiveMergeFST(const Node* nodeIn, std::string_view dirIn, std::function<void(void)> incParents,
bool recursiveMergeFST(const DiscBase::IPartition::Node* nodeIn, size_t parentDirIdx, std::string_view keyPath);
const SystemChar* dirIn, std::function<void(void)> incParents,
const SystemString& keyPath);
static bool RecursiveCalculateTotalSize(uint64_t& totalSz, static bool RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, std::string_view dirIn);
const DiscBase::IPartition::Node* nodeIn,
const SystemChar* dirIn);
void addBuildName(const SystemString& str) void addBuildName(std::string_view str) {
{ UTF8ToSJIS nameView(str);
SystemUTF8View utf8View(str); m_buildNames.emplace_back(nameView.str());
m_buildNames.push_back(utf8View.utf8_str()); m_buildNameOff += nameView.str().size() + 1;
m_buildNameOff += str.size() + 1;
} }
DiscBuilderBase& m_parent; DiscBuilderBase& m_parent;
@@ -471,36 +419,31 @@ public:
uint64_t m_dolOffset = 0; uint64_t m_dolOffset = 0;
uint64_t m_dolSize = 0; uint64_t m_dolSize = 0;
bool m_isWii; bool m_isWii;
public: public:
PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii) PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii)
: m_parent(parent), m_kind(kind), m_isWii(isWii) : m_parent(parent), m_kind(kind), m_isWii(isWii) {}
{} virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset) = 0;
virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)=0; bool buildFromDirectory(IPartWriteStream& ws, std::string_view dirIn);
bool buildFromDirectory(IPartWriteStream& ws, static std::optional<uint64_t> CalculateTotalSizeBuild(std::string_view dirIn, PartitionKind kind, bool isWii);
const SystemChar* dirIn); bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, std::string_view dirIn);
static uint64_t CalculateTotalSizeBuild(const SystemChar* dirIn, static std::optional<uint64_t> CalculateTotalSizeMerge(const IPartition* partIn, std::string_view 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; std::string m_outPath;
std::unique_ptr<IFileIO> m_fileIO; std::unique_ptr<IFileIO> m_fileIO;
std::vector<std::unique_ptr<PartitionBuilderBase>> m_partitions; std::vector<std::unique_ptr<PartitionBuilderBase>> m_partitions;
int64_t m_discCapacity; int64_t m_discCapacity;
public: public:
FProgress m_progressCB; FProgress m_progressCB;
size_t m_progressIdx = 0; size_t m_progressIdx = 0;
size_t m_progressTotal = 0; size_t m_progressTotal = 0;
float getProgressFactor() const float getProgressFactor() const {
{
return m_progressTotal ? std::min(1.f, m_progressIdx / float(m_progressTotal)) : 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 float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const {
{
if (!m_progressTotal) if (!m_progressTotal)
return 0.f; return 0.f;
@@ -511,19 +454,15 @@ public:
} }
virtual ~DiscBuilderBase() = default; virtual ~DiscBuilderBase() = default;
DiscBuilderBase(const SystemChar* outPath, DiscBuilderBase(std::string_view outPath, int64_t discCapacity, FProgress progressCB)
int64_t discCapacity, FProgress progressCB) : m_outPath(outPath)
: m_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)), , m_fileIO(NewFileIO(outPath, discCapacity))
m_discCapacity(discCapacity), m_progressCB(progressCB) {} , m_discCapacity(discCapacity)
, m_progressCB(std::move(progressCB)) {}
DiscBuilderBase(DiscBuilderBase&&) = default; DiscBuilderBase(DiscBuilderBase&&) = default;
DiscBuilderBase& operator=(DiscBuilderBase&&) = default; DiscBuilderBase& operator=(DiscBuilderBase&&) = default;
IFileIO& getFileIO() { return *m_fileIO; } IFileIO& getFileIO() { return *m_fileIO; }
}; };
using Partition = DiscBase::IPartition; } // namespace nod
using Node = Partition::Node;
}
#endif // __NOD_DISC_BASE__

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(const SystemChar* outPath, FProgress progressCB); DiscBuilderGCN makeMergeBuilder(std::string_view 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__

78
include/nod/Endian.hpp Normal file
View File

@@ -0,0 +1,78 @@
#pragma once
#include <cstdint>
#undef bswap16
#undef bswap32
#undef bswap64
namespace nod {
/* Type-sensitive byte swappers */
template <typename T>
static inline T bswap16(T val) {
#if __GNUC__
return __builtin_bswap16(val);
#elif _WIN32
return _byteswap_ushort(val);
#else
return (val = (val << 8) | ((val >> 8) & 0xFF));
#endif
}
template <typename T>
static inline T bswap32(T val) {
#if __GNUC__
return __builtin_bswap32(val);
#elif _WIN32
return _byteswap_ulong(val);
#else
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
return val;
#endif
}
template <typename T>
static inline T bswap64(T val) {
#if __GNUC__
return __builtin_bswap64(val);
#elif _WIN32
return _byteswap_uint64(val);
#else
return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
#endif
}
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
static inline int16_t SBig(int16_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 uint32_t SBig(uint32_t val) { return bswap32(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 int16_t SLittle(int16_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 uint32_t SLittle(uint32_t val) { return val; }
static inline int64_t SLittle(int64_t val) { return val; }
static inline uint64_t SLittle(uint64_t val) { return val; }
#else
static inline int16_t SLittle(int16_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 uint32_t SLittle(uint32_t val) { return bswap32(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 int16_t SBig(int16_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 uint32_t SBig(uint32_t val) { return val; }
static inline int64_t SBig(int64_t val) { return val; }
static inline uint64_t SBig(uint64_t val) { return val; }
#endif
} // namespace nod

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: public:
AthenaPartReadStream(std::unique_ptr<IPartReadStream>&& rs) : m_rs(std::move(rs)) {} AthenaPartReadStream(std::unique_ptr<IPartReadStream>&& rs) : m_rs(std::move(rs)) {}
inline void seek(atInt64 off, athena::SeekOrigin origin) void seek(atInt64 off, athena::SeekOrigin origin) override {
{ if (origin == athena::SeekOrigin::Begin) {
if (origin == athena::Begin)
m_rs->seek(off, SEEK_SET); m_rs->seek(off, SEEK_SET);
else if (origin == athena::Current) } else if (origin == athena::SeekOrigin::Current) {
m_rs->seek(off, SEEK_CUR); 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,34 @@
#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 <string_view>
#include "Util.hpp"
namespace nod #include "nod/IDiscIO.hpp"
{
class IFileIO 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 copyFromDisc(IPartReadStream& discio, uint64_t length, const std::function<void(float)>& prog);
{
uint64_t read = 0;
uint8_t buf[0x7c00];
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;
}
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, "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;
}
}; };
virtual std::unique_ptr<IWriteStream> beginWriteStream() const=0; virtual std::unique_ptr<IWriteStream> beginWriteStream() const = 0;
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) 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,352 +0,0 @@
#ifndef __NOD_UTIL_HPP__
#define __NOD_UTIL_HPP__
#if _WIN32 && UNICODE
#include <wctype.h>
#include <direct.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <ctype.h>
#include <sys/file.h>
#include <unistd.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/statvfs.h>
#endif
#include <sys/stat.h>
#include <string>
#include <cstring>
#include <algorithm>
#include "logvisor/logvisor.hpp"
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#include <sys/stat.h>
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#if !defined(S_ISLNK)
#define S_ISLNK(m) 0
#endif
#endif
#undef min
#undef max
namespace nod
{
/* define our own min/max to avoid MSVC BS */
template<typename T>
inline T min(T a, T b) { return a < b ? a : b; }
template<typename T>
inline T max(T a, T b) { return a > b ? a : b; }
/* Log Module */
extern logvisor::Module LogModule;
/* filesystem char type */
#if _WIN32 && UNICODE
#define NOD_UCS2 1
typedef struct _stat Sstat;
static inline int Mkdir(const wchar_t* path, int) {return _wmkdir(path);}
static inline int Stat(const wchar_t* path, Sstat* statout) {return _wstat(path, statout);}
#else
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);}
#endif
/* String-converting views */
#if NOD_UCS2
typedef wchar_t SystemChar;
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
typedef char SystemChar;
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
}
#undef bswap16
#undef bswap32
#undef bswap64
/* Type-sensitive byte swappers */
template <typename T>
static inline T bswap16(T val)
{
#if __GNUC__
return __builtin_bswap16(val);
#elif _WIN32
return _byteswap_ushort(val);
#else
return (val = (val << 8) | ((val >> 8) & 0xFF));
#endif
}
template <typename T>
static inline T bswap32(T val)
{
#if __GNUC__
return __builtin_bswap32(val);
#elif _WIN32
return _byteswap_ulong(val);
#else
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
return val;
#endif
}
template <typename T>
static inline T bswap64(T val)
{
#if __GNUC__
return __builtin_bswap64(val);
#elif _WIN32
return _byteswap_uint64(val);
#else
return ((val & 0xFF00000000000000ULL) >> 56) |
((val & 0x00FF000000000000ULL) >> 40) |
((val & 0x0000FF0000000000ULL) >> 24) |
((val & 0x000000FF00000000ULL) >> 8) |
((val & 0x00000000FF000000ULL) << 8) |
((val & 0x0000000000FF0000ULL) << 24) |
((val & 0x000000000000FF00ULL) << 40) |
((val & 0x00000000000000FFULL) << 56);
#endif
}
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
static inline int16_t SBig(int16_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 uint32_t SBig(uint32_t val) {return bswap32(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 int16_t SLittle(int16_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 uint32_t SLittle(uint32_t val) {return val;}
static inline int64_t SLittle(int64_t val) {return val;}
static inline uint64_t SLittle(uint64_t val) {return val;}
#else
static inline int16_t SLittle(int16_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 uint32_t SLittle(uint32_t val) {return bswap32(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 int16_t SBig(int16_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 uint32_t SBig(uint32_t val) {return val;}
static inline int64_t SBig(int64_t val) {return val;}
static inline uint64_t SBig(uint64_t val) {return val;}
#endif
#ifndef ROUND_UP_32
#define ROUND_UP_32(val) (((val) + 31) & ~31)
#define ROUND_UP_16(val) (((val) + 15) & ~15)
#endif
enum class FileLockType
{
None = 0,
Read,
Write
};
static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock=FileLockType::None)
{
#if NOD_UCS2
FILE* fp = _wfopen(path, mode);
if (!fp)
return nullptr;
#else
FILE* fp = fopen(path, mode);
if (!fp)
return nullptr;
#endif
if (lock != FileLockType::None)
{
#if _WIN32
OVERLAPPED ov = {};
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov);
#else
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
LogModule.report(logvisor::Error, "flock %s: %s", path, strerror(errno));
#endif
}
return fp;
}
static inline int FSeek(FILE* fp, int64_t offset, int whence)
{
#if _WIN32
return _fseeki64(fp, offset, whence);
#elif __APPLE__ || __FreeBSD__
return fseeko(fp, offset, whence);
#else
return fseeko64(fp, offset, whence);
#endif
}
static inline int64_t FTell(FILE* fp)
{
#if _WIN32
return _ftelli64(fp);
#elif __APPLE__ || __FreeBSD__
return ftello(fp);
#else
return ftello64(fp);
#endif
}
static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz)
{
#if _WIN32
ULARGE_INTEGER freeBytes;
wchar_t buf[1024];
wchar_t* end;
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
if (!ret || ret > 1024)
{
LogModule.report(logvisor::Error, _S("GetFullPathNameW %s"), path);
return false;
}
if (end)
end[0] = L'\0';
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr))
{
LogModule.report(logvisor::Error, _S("GetDiskFreeSpaceExW %s: %d"), path, GetLastError());
return false;
}
return reqSz < freeBytes.QuadPart;
#else
struct statvfs svfs;
if (statvfs(path, &svfs))
{
LogModule.report(logvisor::Error, "statvfs %s: %s", path, strerror(errno));
return false;
}
return reqSz < svfs.f_frsize * svfs.f_bavail;
#endif
}
#if __GNUC__
__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,19 @@
#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_view>
namespace nod namespace nod {
{
class DiscBase; class DiscBase;
struct ExtractionContext final struct ExtractionContext final {
{
bool force : 1; bool force : 1;
std::function<void(const std::string&, float)> progressCB; std::function<void(std::string_view, 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,57 @@
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
DirectoryEnumerator.cpp
DiscBase.cpp DiscBase.cpp
DiscGCN.cpp DiscGCN.cpp
DiscIOISO.cpp DiscIOISO.cpp
DiscIONFS.cpp
DiscIOWBFS.cpp DiscIOWBFS.cpp
DiscWii.cpp DiscWii.cpp
DirectoryEnumerator.cpp IFileIO.cpp
nod.cpp nod.cpp
${PLAT_SRCS} OSUTF.c
${NOD_HEADERS}) Util.cpp
if(NOT MSVC) Util.hpp
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/Endian.hpp
../include/nod/IDiscIO.hpp
../include/nod/IFileIO.hpp
../include/nod/nod.hpp
../include/nod/OSUTF.h
../include/nod/sha1.h
)
target_include_directories(nod PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
)
target_link_libraries(nod PUBLIC $<BUILD_INTERFACE:spdlog::spdlog>)
if(WIN32)
target_sources(nod PRIVATE FileIOWin32.cpp)
target_link_libraries(nod PRIVATE nowide::nowide)
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,147 +1,168 @@
#include "nod/DirectoryEnumerator.hpp"
#include "Util.hpp"
#ifdef _WIN32 #ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h> #include <windows.h>
#else #else
#include <sys/stat.h>
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h>
#endif #endif
#include <cstring>
#include <map> #include <map>
#include "nod/DirectoryEnumerator.hpp" namespace nod {
namespace nod DirectoryEnumerator::DirectoryEnumerator(std::string_view path, Mode mode, bool sizeSort, bool reverse, bool noHidden) {
{
DirectoryEnumerator::DirectoryEnumerator(const SystemChar* path, Mode mode,
bool sizeSort, bool reverse, bool noHidden)
{
Sstat theStat; Sstat theStat;
if (Stat(path, &theStat) || !S_ISDIR(theStat.st_mode)) if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode)) {
return; 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);
fp += '/';
fp += fileName;
Sstat st; Sstat st;
if (Stat(fp.c_str(), &st)) if (Stat(fp.c_str(), &st))
continue; 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"..")) {
{
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);
fp += '/';
fp += fileName;
Sstat st; Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) {
continue; continue;
sort.emplace(std::make_pair(d.cFileName, Entry(std::move(fp), d.cFileName, 0, true))); }
sort.emplace(fileName, Entry{fp, fileName, 0, true});
} while (FindNextFileW(dir, &d)); } 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 = sort.crbegin(); it != sort.crend(); ++it) {
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) if (mode == Mode::DirsSorted) {
break; break;
}
FindClose(dir); FindClose(dir);
dir = FindFirstFileW(wc.c_str(), &d); dir = FindFirstFileW(wc.c_str(), &d);
} }
case Mode::FilesSorted: case Mode::FilesSorted: {
{ if (mode == Mode::FilesSorted) {
if (mode == Mode::FilesSorted)
m_entries.clear(); m_entries.clear();
if (sizeSort)
{
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
{ if (sizeSort) {
std::map<SystemString, Entry, CaseInsensitiveCompare> sort; std::multimap<size_t, Entry> sort;
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);
fp += '/';
fp += fileName;
Sstat st; Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) {
continue; continue;
sort.emplace(std::make_pair(d.cFileName, Entry(std::move(fp), d.cFileName, st.st_size, false))); }
sort.emplace(st.st_size, Entry{fp, fileName, static_cast<size_t>(st.st_size), false});
} while (FindNextFileW(dir, &d)); } while (FindNextFileW(dir, &d));
m_entries.reserve(sort.size()); m_entries.reserve(m_entries.size() + sort.size());
if (reverse) if (reverse) {
for (auto it=sort.crbegin() ; it != sort.crend() ; ++it) for (auto it = sort.crbegin(); it != sort.crend(); ++it) {
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));
}
}
} 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 it = sort.crbegin(); it != sort.crend(); ++it) {
m_entries.emplace_back(std::move(it->second));
}
} else {
for (auto& e : sort) {
m_entries.emplace_back(std::move(e.second));
}
}
} }
break; break;
@@ -151,20 +172,18 @@ DirectoryEnumerator::DirectoryEnumerator(const SystemChar* path, Mode mode,
#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, "..")) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue; continue;
if (noHidden && d->d_name[0] == '.') if (noHidden && d->d_name[0] == '.')
continue; continue;
SystemString fp(path); std::string fp(path);
fp += '/'; fp += '/';
fp += d->d_name; fp += d->d_name;
Sstat st; Sstat st;
@@ -180,31 +199,29 @@ DirectoryEnumerator::DirectoryEnumerator(const SystemChar* path, Mode mode,
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; 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; while ((d = readdir(dir))) {
while ((d = readdir(dir)))
{
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue; continue;
if (noHidden && d->d_name[0] == '.') if (noHidden && d->d_name[0] == '.')
continue; continue;
SystemString fp(path); std::string fp(path);
fp += '/'; fp += '/';
fp += d->d_name; fp += d->d_name;
Sstat st; Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
continue; continue;
sort.emplace(std::make_pair(d->d_name, Entry(std::move(fp), d->d_name, 0, true))); sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, 0, true)));
} }
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 = sort.crbegin(); it != sort.crend(); ++it)
m_entries.push_back(std::move(it->second)); m_entries.push_back(std::move(it->second));
else else
for (auto& e : sort) for (auto& e : sort)
@@ -213,59 +230,54 @@ DirectoryEnumerator::DirectoryEnumerator(const SystemChar* path, Mode mode,
if (mode == Mode::DirsSorted) if (mode == Mode::DirsSorted)
break; break;
rewinddir(dir); rewinddir(dir);
[[fallthrough]];
} }
case Mode::FilesSorted: case Mode::FilesSorted: {
{
if (mode == Mode::FilesSorted) if (mode == Mode::FilesSorted)
m_entries.clear(); m_entries.clear();
if (sizeSort) if (sizeSort) {
{
std::multimap<size_t, Entry> sort; std::multimap<size_t, Entry> sort;
while ((d = readdir(dir))) while ((d = readdir(dir))) {
{
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue; continue;
if (noHidden && d->d_name[0] == '.') if (noHidden && d->d_name[0] == '.')
continue; continue;
SystemString fp(path); std::string fp(path);
fp += '/'; fp += '/';
fp += d->d_name; fp += d->d_name;
Sstat st; Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
continue; continue;
sort.emplace(std::make_pair(st.st_size, Entry(std::move(fp), d->d_name, st.st_size, false))); sort.emplace(std::make_pair(st.st_size, Entry(fp, d->d_name, st.st_size, false)));
} }
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 = sort.crbegin(); it != sort.crend(); ++it)
m_entries.push_back(std::move(it->second)); m_entries.push_back(std::move(it->second));
else else
for (auto& e : sort) for (auto& e : sort)
m_entries.push_back(std::move(e.second)); m_entries.push_back(std::move(e.second));
} } else {
else std::map<std::string, Entry, CaseInsensitiveCompare> sort;
{ while ((d = readdir(dir))) {
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
while ((d = readdir(dir)))
{
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue; continue;
if (noHidden && d->d_name[0] == '.') if (noHidden && d->d_name[0] == '.')
continue; continue;
SystemString fp(path); std::string fp(path);
fp += '/'; fp += '/';
fp += d->d_name; fp += d->d_name;
Sstat st; Sstat st;
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
continue; continue;
sort.emplace(std::make_pair(d->d_name, Entry(std::move(fp), d->d_name, st.st_size, false))); sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, st.st_size, false)));
} }
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 = sort.crbegin(); it != sort.crend(); ++it)
m_entries.push_back(std::move(it->second)); m_entries.push_back(std::move(it->second));
else else
for (auto& e : sort) for (auto& e : sort)
@@ -280,4 +292,4 @@ DirectoryEnumerator::DirectoryEnumerator(const SystemChar* path, Mode mode,
#endif #endif
} }
} } // namespace nod

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,27 @@
#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 "Util.hpp"
#include <spdlog/spdlog.h>
#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; err = true;
return; return;
} }
@@ -37,8 +43,7 @@ public:
parseDOL(*s); parseDOL(*s);
} }
class PartReadStream : public IPartReadStream class PartReadStream : public IPartReadStream {
{
const PartitionGCN& m_parent; const PartitionGCN& m_parent;
std::unique_ptr<IReadStream> m_dio; std::unique_ptr<IReadStream> m_dio;
@@ -47,21 +52,17 @@ public:
uint8_t m_buf[BUFFER_SZ]; uint8_t m_buf[BUFFER_SZ];
public: public:
PartReadStream(const PartitionGCN& parent, uint64_t offset, bool& err) PartReadStream(const PartitionGCN& parent, uint64_t offset, bool& err) : m_parent(parent), m_offset(offset) {
: m_parent(parent), m_offset(offset)
{
size_t block = m_offset / BUFFER_SZ; size_t block = m_offset / BUFFER_SZ;
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(block * BUFFER_SZ); m_dio = m_parent.m_parent.getDiscIO().beginReadStream(block * BUFFER_SZ);
if (!m_dio) if (!m_dio) {
{
err = true; err = true;
return; return;
} }
m_dio->read(m_buf, BUFFER_SZ); m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block; m_curBlock = block;
} }
void seek(int64_t offset, int whence) void seek(int64_t offset, int whence) override {
{
if (whence == SEEK_SET) if (whence == SEEK_SET)
m_offset = offset; m_offset = offset;
else if (whence == SEEK_CUR) else if (whence == SEEK_CUR)
@@ -69,26 +70,22 @@ public:
else else
return; return;
size_t block = m_offset / BUFFER_SZ; size_t block = m_offset / BUFFER_SZ;
if (block != m_curBlock) if (block != m_curBlock) {
{
m_dio->seek(block * BUFFER_SZ); m_dio->seek(block * BUFFER_SZ);
m_dio->read(m_buf, BUFFER_SZ); m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block; m_curBlock = block;
} }
} }
uint64_t position() const {return m_offset;} uint64_t position() const override { return m_offset; }
uint64_t read(void* buf, uint64_t length) uint64_t read(void* buf, uint64_t length) override {
{
size_t block = m_offset / BUFFER_SZ; size_t block = m_offset / BUFFER_SZ;
size_t cacheOffset = m_offset % BUFFER_SZ; size_t cacheOffset = m_offset % BUFFER_SZ;
uint64_t cacheSize; uint64_t cacheSize;
uint64_t rem = length; uint64_t rem = length;
uint8_t* dst = (uint8_t*)buf; uint8_t* dst = (uint8_t*)buf;
while (rem) while (rem) {
{ if (block != m_curBlock) {
if (block != m_curBlock)
{
m_dio->read(m_buf, BUFFER_SZ); m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block; m_curBlock = block;
} }
@@ -109,65 +106,56 @@ public:
} }
}; };
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const override {
{ bool err = false;
bool Err = false; auto ret = std::make_unique<PartReadStream>(*this, offset, err);
auto ret = std::unique_ptr<IPartReadStream>(new PartReadStream(*this, offset, Err));
if (Err) if (err) {
return {}; return nullptr;
}
return ret; return ret;
} }
}; };
DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err) DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err) : DiscBase(std::move(dio), err) {
: DiscBase(std::move(dio), err)
{
if (err) if (err)
return; return;
/* One lone partition for GCN */ /* One lone partition for GCN */
m_partitions.emplace_back(new PartitionGCN(*this, 0, err)); m_partitions.emplace_back(std::make_unique<PartitionGCN>(*this, 0, err));
} }
DiscBuilderGCN DiscGCN::makeMergeBuilder(const SystemChar* outPath, FProgress progressCB) DiscBuilderGCN DiscGCN::makeMergeBuilder(std::string_view outPath, FProgress progressCB) {
{
return DiscBuilderGCN(outPath, progressCB); return DiscBuilderGCN(outPath, progressCB);
} }
bool DiscGCN::extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const bool DiscGCN::extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const { return true; }
{
return true;
}
class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase {
{
uint64_t m_curUser = 0x57058000; uint64_t m_curUser = 0x57058000;
public: public:
class PartWriteStream : public IPartWriteStream class PartWriteStream : public IPartWriteStream {
{
const PartitionBuilderGCN& m_parent; const PartitionBuilderGCN& m_parent;
uint64_t m_offset; uint64_t m_offset;
std::unique_ptr<IFileIO::IWriteStream> m_fio; std::unique_ptr<IFileIO::IWriteStream> m_fio;
public: public:
PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset, bool& err) PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset, bool& err)
: m_parent(parent), m_offset(offset) : m_parent(parent), m_offset(offset) {
{
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset); m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset);
if (!m_fio) if (!m_fio)
err = true; err = true;
} }
void close() {m_fio.reset();} void close() override { m_fio.reset(); }
uint64_t position() const {return m_offset;} uint64_t position() const override { return m_offset; }
uint64_t write(const void* buf, uint64_t length) uint64_t write(const void* buf, uint64_t length) override {
{
uint64_t len = m_fio->write(buf, length); uint64_t len = m_fio->write(buf, length);
m_offset += len; m_offset += len;
return len; return len;
} }
void seek(size_t off) void seek(size_t off) {
{
m_offset = off; m_offset = off;
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(off); m_fio = m_parent.m_parent.getFileIO().beginWriteStream(off);
} }
@@ -176,38 +164,34 @@ public:
PartitionBuilderGCN(DiscBuilderBase& parent) PartitionBuilderGCN(DiscBuilderBase& parent)
: DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false) {} : DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false) {}
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) override {
{
m_curUser -= reqSz; m_curUser -= reqSz;
m_curUser &= 0xfffffffffffffff0; m_curUser &= 0xfffffffffffffff0;
if (m_curUser < 0x30000) if (m_curUser < 0x30000) {
{ spdlog::error("user area low mark reached");
LogModule.report(logvisor::Error, "user area low mark reached");
return -1; return -1;
} }
static_cast<PartWriteStream&>(ws).seek(m_curUser); static_cast<PartWriteStream&>(ws).seek(m_curUser);
return m_curUser; return m_curUser;
} }
uint32_t packOffset(uint64_t offset) const uint32_t packOffset(uint64_t offset) const override { return offset; }
{
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;
} }
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; return ret;
} }
bool _build(const std::function<bool(IPartWriteStream&, uint32_t, uint32_t, bool
uint32_t, uint32_t, uint32_t)>& headerFunc, _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&)>& bi2Func,
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc) const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc) {
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0x2440); std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0x2440);
if (!ws) if (!ws)
return false; return false;
@@ -217,19 +201,17 @@ public:
size_t fstOff = ROUND_UP_32(xferSz); size_t fstOff = ROUND_UP_32(xferSz);
size_t fstSz = sizeof(FSTNode) * m_buildNodes.size(); size_t fstSz = sizeof(FSTNode) * m_buildNodes.size();
for (size_t i=0 ; i<fstOff-xferSz ; ++i) for (size_t i = 0; i < fstOff - xferSz; ++i)
ws->write("\xff", 1); ws->write("\xff", 1);
fstOff += 0x2440; fstOff += 0x2440;
ws->write(m_buildNodes.data(), fstSz); ws->write(m_buildNodes.data(), fstSz);
for (const std::string& str : m_buildNames) for (const std::string& str : m_buildNames)
ws->write(str.data(), str.size()+1); ws->write(str.data(), str.size() + 1);
fstSz += m_buildNameOff; fstSz += m_buildNameOff;
fstSz = ROUND_UP_32(fstSz); fstSz = ROUND_UP_32(fstSz);
if (fstOff + fstSz >= m_curUser) if (fstOff + fstSz >= m_curUser) {
{ spdlog::error("FST flows into user area (one or the other is too big)");
LogModule.report(logvisor::Error,
"FST flows into user area (one or the other is too big)");
return false; return false;
} }
@@ -244,8 +226,7 @@ public:
return true; return true;
} }
bool buildFromDirectory(const SystemChar* dirIn) bool buildFromDirectory(std::string_view dirIn) {
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0); std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
if (!ws) if (!ws)
return false; return false;
@@ -253,39 +234,35 @@ public:
if (!result) if (!result)
return false; return false;
SystemString dirStr(dirIn); std::string dirStr(dirIn);
/* Check Apploader */ /* Check Apploader */
SystemString apploaderIn = dirStr + _S("/sys/apploader.img"); std::string apploaderIn = dirStr + "/sys/apploader.img";
Sstat apploaderStat; Sstat apploaderStat;
if (Stat(apploaderIn.c_str(), &apploaderStat)) if (Stat(apploaderIn.c_str(), &apploaderStat)) {
{ spdlog::error("unable to stat {}", apploaderIn);
LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn.c_str()); return false;
return -1;
} }
/* Check Boot */ /* Check Boot */
SystemString bootIn = dirStr + _S("/sys/boot.bin"); std::string bootIn = dirStr + "/sys/boot.bin";
Sstat bootStat; Sstat bootStat;
if (Stat(bootIn.c_str(), &bootStat)) if (Stat(bootIn.c_str(), &bootStat)) {
{ spdlog::error("unable to stat {}", bootIn);
LogModule.report(logvisor::Error, _S("unable to stat %s"), bootIn.c_str()); return false;
return -1;
} }
/* Check BI2 */ /* Check BI2 */
SystemString bi2In = dirStr + _S("/sys/bi2.bin"); std::string bi2In = dirStr + "/sys/bi2.bin";
Sstat bi2Stat; Sstat bi2Stat;
if (Stat(bi2In.c_str(), &bi2Stat)) if (Stat(bi2In.c_str(), &bi2Stat)) {
{ spdlog::error("unable to stat {}", bi2In);
LogModule.report(logvisor::Error, _S("unable to stat %s"), bi2In.c_str()); return false;
return -1;
} }
return _build( return _build(
[this, &bootIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz, [&bootIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz, uint32_t userOff,
uint32_t userOff, uint32_t userSz) -> bool uint32_t userSz) -> bool {
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bootIn.c_str())->beginReadStream(); std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bootIn.c_str())->beginReadStream();
if (!rs) if (!rs)
return false; return false;
@@ -300,8 +277,7 @@ public:
header.write(ws); header.write(ws);
return true; return true;
}, },
[this, &bi2In](IPartWriteStream& ws) -> bool [&bi2In](IPartWriteStream& ws) -> bool {
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bi2In.c_str())->beginReadStream(); std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bi2In.c_str())->beginReadStream();
if (!rs) if (!rs)
return false; return false;
@@ -310,23 +286,19 @@ public:
bi2.write(ws); bi2.write(ws);
return true; return true;
}, },
[this, &apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool [this, &apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool {
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn.c_str())->beginReadStream(); std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn.c_str())->beginReadStream();
if (!rs) if (!rs)
return false; return false;
char buf[8192]; char buf[8192];
while (true) while (true) {
{
size_t rdSz = rs->read(buf, 8192); size_t rdSz = rs->read(buf, 8192);
if (!rdSz) if (!rdSz)
break; break;
ws.write(buf, rdSz); ws.write(buf, rdSz);
xferSz += rdSz; xferSz += rdSz;
if (0x2440 + xferSz >= m_curUser) if (0x2440 + xferSz >= m_curUser) {
{ spdlog::error("apploader flows into user area (one or the other is too big)");
LogModule.report(logvisor::Error,
"apploader flows into user area (one or the other is too big)");
return false; return false;
} }
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz); m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
@@ -336,8 +308,7 @@ public:
}); });
} }
bool mergeFromDirectory(const PartitionGCN* partIn, const SystemChar* dirIn) bool mergeFromDirectory(const PartitionGCN* partIn, std::string_view dirIn) {
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0); std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
if (!ws) if (!ws)
return false; return false;
@@ -346,9 +317,8 @@ public:
return false; return false;
return _build( return _build(
[this, partIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz, [partIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz, uint32_t userOff,
uint32_t userOff, uint32_t userSz) -> bool uint32_t userSz) -> bool {
{
Header header = partIn->getHeader(); Header header = partIn->getHeader();
header.m_dolOff = dolOff; header.m_dolOff = dolOff;
header.m_fstOff = fstOff; header.m_fstOff = fstOff;
@@ -359,22 +329,18 @@ public:
header.write(ws); header.write(ws);
return true; return true;
}, },
[this, partIn](IPartWriteStream& ws) -> bool [partIn](IPartWriteStream& ws) -> bool {
{
partIn->getBI2().write(ws); partIn->getBI2().write(ws);
return true; return true;
}, },
[this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool [this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool {
{
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf(); std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
size_t apploaderSz = partIn->getApploaderSize(); size_t apploaderSz = partIn->getApploaderSize();
SystemString apploaderName(_S("<apploader>")); std::string apploaderName("<apploader>");
ws.write(apploaderBuf.get(), apploaderSz); ws.write(apploaderBuf.get(), apploaderSz);
xferSz += apploaderSz; xferSz += apploaderSz;
if (0x2440 + xferSz >= m_curUser) if (0x2440 + xferSz >= m_curUser) {
{ spdlog::error("apploader flows into user area (one or the other is too big)");
LogModule.report(logvisor::Error,
"apploader flows into user area (one or the other is too big)");
return false; return false;
} }
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
@@ -384,85 +350,84 @@ public:
} }
}; };
EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn) EBuildResult DiscBuilderGCN::buildFromDirectory(std::string_view dirIn) {
{
if (!m_fileIO->beginWriteStream()) if (!m_fileIO->beginWriteStream())
return EBuildResult::Failed; return EBuildResult::Failed;
if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000)) if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000)) {
{ spdlog::error("not enough free disk space for {}", m_outPath);
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_outPath.c_str());
return EBuildResult::DiskFull; return EBuildResult::DiskFull;
} }
m_progressCB(getProgressFactor(), _S("Preallocating image"), -1); m_progressCB(getProgressFactor(), "Preallocating image", -1);
++m_progressIdx; ++m_progressIdx;
auto ws = m_fileIO->beginWriteStream(0x57058000 - 1); {
auto ws = m_fileIO->beginWriteStream(0);
if (!ws) if (!ws)
return EBuildResult::Failed; return EBuildResult::Failed;
ws->write("", 1); char zeroBytes[1024] = {};
for (uint64_t i = 0; i < 0x57058000; i += 1024)
ws->write(zeroBytes, 1024);
}
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]); PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]);
return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed; return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed;
} }
uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn) std::optional<uint64_t> DiscBuilderGCN::CalculateTotalSizeRequired(std::string_view dirIn) {
{ std::optional<uint64_t> sz =
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false); DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false);
if (sz == -1) if (!sz)
return -1; return sz;
sz += 0x30000; *sz += 0x30000;
if (sz > 0x57058000) if (sz > 0x57058000) {
{ spdlog::error("disc capacity exceeded [{} / {}]", *sz, 0x57058000);
LogModule.report(logvisor::Error, _S("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000); return std::nullopt;
return -1;
} }
return sz; return sz;
} }
DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB) DiscBuilderGCN::DiscBuilderGCN(std::string_view outPath, FProgress progressCB)
: DiscBuilderBase(outPath, 0x57058000, progressCB) : DiscBuilderBase(outPath, 0x57058000, progressCB) {
{ m_partitions.emplace_back(std::make_unique<PartitionBuilderGCN>(*this));
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this);
m_partitions.emplace_back(partBuilder);
} }
DiscMergerGCN::DiscMergerGCN(const SystemChar* outPath, DiscGCN& sourceDisc, FProgress progressCB) DiscMergerGCN::DiscMergerGCN(std::string_view outPath, DiscGCN& sourceDisc, FProgress progressCB)
: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB)) : m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB)) {}
{}
EBuildResult DiscMergerGCN::mergeFromDirectory(const SystemChar* dirIn) EBuildResult DiscMergerGCN::mergeFromDirectory(std::string_view dirIn) {
{
if (!m_builder.getFileIO().beginWriteStream()) if (!m_builder.getFileIO().beginWriteStream())
return EBuildResult::Failed; return EBuildResult::Failed;
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000)) if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000)) {
{ spdlog::error("not enough free disk space for {}", m_builder.m_outPath);
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_builder.m_outPath.c_str());
return EBuildResult::DiskFull; return EBuildResult::DiskFull;
} }
m_builder.m_progressCB(m_builder.getProgressFactor(), _S("Preallocating image"), -1); m_builder.m_progressCB(m_builder.getProgressFactor(), "Preallocating image", -1);
++m_builder.m_progressIdx; ++m_builder.m_progressIdx;
auto ws = m_builder.m_fileIO->beginWriteStream(0x57058000 - 1); {
auto ws = m_builder.m_fileIO->beginWriteStream(0);
if (!ws) if (!ws)
return EBuildResult::Failed; return EBuildResult::Failed;
ws->write("", 1); 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]); PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_builder.m_partitions[0]);
return pb.mergeFromDirectory(static_cast<PartitionGCN*>(m_sourceDisc.getDataPartition()), dirIn) ? return pb.mergeFromDirectory(static_cast<PartitionGCN*>(m_sourceDisc.getDataPartition()), dirIn)
EBuildResult::Success : EBuildResult::Failed; ? EBuildResult::Success
: EBuildResult::Failed;
} }
uint64_t DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, const SystemChar* dirIn) std::optional<uint64_t> DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, std::string_view dirIn) {
{ std::optional<uint64_t> sz =
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge( DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn);
sourceDisc.getDataPartition(), dirIn); if (!sz)
if (sz == -1) return std::nullopt;
return -1; *sz += 0x30000;
sz += 0x30000; if (sz > 0x57058000) {
if (sz > 0x57058000) spdlog::error("disc capacity exceeded [{} / {}]", *sz, 0x57058000);
{ return std::nullopt;
LogModule.report(logvisor::Error, _S("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000);
return -1;
} }
return sz; return sz;
} }
} } // namespace nod

View File

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

272
lib/DiscIONFS.cpp Normal file
View File

@@ -0,0 +1,272 @@
#include "nod/IDiscIO.hpp"
#include "nod/IFileIO.hpp"
#include "nod/aes.hpp"
#include "nod/Endian.hpp"
#include "Util.hpp"
#include <spdlog/spdlog.h>
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")) {
spdlog::error("'{}' 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) {
spdlog::error("Unable to open '{}../code/htk.bin' or '{}htk.bin'", dir, dir);
err = true;
return;
}
if (keyFile->read(key, 16) != 16) {
spdlog::error("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("{}hif_{:06}.nfs", dir, 0);
files.push_back(NewFileIO(firstPath));
auto rs = files.back()->beginReadStream();
if (!rs) {
spdlog::error("'{}' does not exist", firstPath);
err = true;
return;
}
if (rs->read(&nfsHead, 0x200) != 0x200) {
spdlog::error("Unable to read header from '{}'", firstPath);
err = true;
return;
}
if (std::memcmp(&nfsHead.magic, "EGGS", 4)) {
spdlog::error("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("{}hif_{:06}.nfs", dir, i);
files.push_back(NewFileIO(path));
if (!files.back()->exists()) {
spdlog::error("'{}' 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()) {
spdlog::error("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,31 +1,32 @@
#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/Endian.hpp"
#include "Util.hpp"
namespace nod #include <spdlog/spdlog.h>
{
#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)))
{
static uint8_t size_to_shift(uint32_t size) {
uint8_t ret = 0; uint8_t ret = 0;
while (size) 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
@@ -36,15 +37,13 @@ class DiscIOWBFS : public IDiscIO
}; };
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
@@ -66,40 +65,35 @@ class DiscIOWBFS : public IDiscIO
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) {
{ spdlog::error("error reading disc");
LogModule.report(logvisor::Error, "error reading disc");
return 1; 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 */ /* Temporary file handle to read LBA table */
std::unique_ptr<IFileIO> fio = NewFileIO(filepath); std::unique_ptr<IFileIO::IReadStream> rs = m_fio->beginReadStream();
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
if (!rs) if (!rs)
return; 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"); spdlog::error("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;
@@ -109,65 +103,63 @@ public:
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"); spdlog::error("unable to read WBFS head");
return; return;
} }
//constants, but put here for consistancy // constants, but put here for consistancy
p->wii_sec_sz = 0x8000; p->wii_sec_sz = 0x8000;
p->wii_sec_sz_s = size_to_shift(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 = (num_hd_sector / 0x8000) * hd_sector_size;
p->n_wii_sec_per_disc = 143432*2;//support for double layers discs.. p->n_wii_sec_per_disc = 143432 * 2; // support for double layers discs..
p->part_lba = 0; p->part_lba = 0;
if (_wbfsReadSector(*rs, p->part_lba, 1, head)) if (_wbfsReadSector(*rs, p->part_lba, 1, head))
return; return;
if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) { 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"); spdlog::error("hd sector size doesn't match");
return; return;
} }
if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) { if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) {
LogModule.report(logvisor::Error, "hd num sector doesn't match"); spdlog::error("hd num sector doesn't match");
return; return;
} }
p->hd_sec_sz = 1<<head->hd_sec_sz_s; p->hd_sec_sz = 1 << head->hd_sec_sz_s;
p->hd_sec_sz_s = 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_hd_sec = SBig(head->n_hd_sec);
p->n_wii_sec = (p->n_hd_sec/p->wii_sec_sz)*(p->hd_sec_sz); 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_s = head->wbfs_sec_sz_s;
p->wbfs_sec_sz = 1<<p->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 = 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->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->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_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->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); 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)) if (p->max_disc > p->hd_sec_sz - sizeof(WBFSHead))
p->max_disc = p->hd_sec_sz - sizeof(WBFSHead); p->max_disc = p->hd_sec_sz - sizeof(WBFSHead);
p->n_disc_open = 0; p->n_disc_open = 0;
int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
if (head->disc_table[0]) if (head->disc_table[0]) {
{
wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]); wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
if (!wbfsDiscInfo) { if (!wbfsDiscInfo) {
LogModule.report(logvisor::Error, "allocating memory"); spdlog::error("allocating memory");
return; return;
} }
if (_wbfsReadSector(*rs, p->part_lba+1, disc_info_sz_lba, wbfsDiscInfo.get())) if (_wbfsReadSector(*rs, p->part_lba + 1, disc_info_sz_lba, wbfsDiscInfo.get()))
return; return;
p->n_disc_open++; p->n_disc_open++;
//for(i=0;i<p->n_wbfs_sec_per_disc;i++) // for(i=0;i<p->n_wbfs_sec_per_disc;i++)
// printf("%d,",wbfs_ntohs(d->header->wlba_table[i])); // printf("%d,",wbfs_ntohs(d->header->wlba_table[i]));
} }
} }
class ReadStream : public IReadStream class ReadStream : public IReadStream {
{
friend class DiscIOWBFS; friend class DiscIOWBFS;
const DiscIOWBFS& m_parent; const DiscIOWBFS& m_parent;
std::unique_ptr<IFileIO::IReadStream> fp; std::unique_ptr<IFileIO::IReadStream> fp;
@@ -175,33 +167,32 @@ public:
std::unique_ptr<uint8_t[]> m_tmpBuffer; std::unique_ptr<uint8_t[]> m_tmpBuffer;
ReadStream(const DiscIOWBFS& parent, std::unique_ptr<IFileIO::IReadStream>&& fpin, uint64_t offset, bool& err) ReadStream(const DiscIOWBFS& parent, std::unique_ptr<IFileIO::IReadStream>&& fpin, uint64_t offset, bool& err)
: m_parent(parent), : m_parent(parent), fp(std::move(fpin)), m_offset(offset), m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) {
fp(std::move(fpin)), if (!fp)
m_offset(offset), err = true;
m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) { if (!fp) err = true; } }
int wbfsReadSector(uint32_t lba, uint32_t count, void* buf) int wbfsReadSector(uint32_t lba, uint32_t count, void* buf) {
{return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf);} return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf);
}
int wbfsDiscRead(uint32_t offset, uint8_t *data, uint64_t len) int wbfsDiscRead(uint32_t offset, uint8_t* data, uint64_t len) {
{
const WBFS* p = &m_parent.wbfs; const WBFS* p = &m_parent.wbfs;
const WBFSDiscInfo* d = (WBFSDiscInfo*)m_parent.wbfsDiscInfo.get(); const WBFSDiscInfo* d = (WBFSDiscInfo*)m_parent.wbfsDiscInfo.get();
uint16_t wlba = offset>>(p->wbfs_sec_sz_s-2); 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 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); 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 lba = (offset >> (p->hd_sec_sz_s - 2)) & lba_mask;
uint64_t off = offset&((p->hd_sec_sz>>2)-1); uint64_t off = offset & ((p->hd_sec_sz >> 2) - 1);
uint16_t iwlba = SBig(d->wlba_table[wlba]); uint16_t iwlba = SBig(d->wlba_table[wlba]);
uint64_t len_copied; uint64_t len_copied;
int err = 0; int err = 0;
uint8_t *ptr = data; uint8_t* ptr = data;
if (!iwlba) if (!iwlba)
return 1; return 1;
if (off) if (off) {
{ off *= 4;
off*=4; err = wbfsReadSector(p->part_lba + (iwlba << iwlba_shift) + lba, 1, m_tmpBuffer.get());
err = wbfsReadSector(p->part_lba + (iwlba<<iwlba_shift) + lba, 1, m_tmpBuffer.get());
if (err) if (err)
return err; return err;
len_copied = p->hd_sec_sz - off; len_copied = p->hd_sec_sz - off;
@@ -211,37 +202,33 @@ public:
len -= len_copied; len -= len_copied;
ptr += len_copied; ptr += len_copied;
lba++; lba++;
if (lba>lba_mask && len) 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; lba = 0;
iwlba = SBig(d->wlba_table[++wlba]); iwlba = SBig(d->wlba_table[++wlba]);
if (!iwlba) if (!iwlba)
return 1; return 1;
} }
} }
if (len) while (len >= p->hd_sec_sz) {
{ uint32_t nlb = len >> (p->hd_sec_sz_s);
err = wbfsReadSector(p->part_lba + (iwlba<<iwlba_shift) + lba, 1, m_tmpBuffer.get());
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) if (err)
return err; return err;
memcpy(ptr, m_tmpBuffer.get(), len); memcpy(ptr, m_tmpBuffer.get(), len);
@@ -250,30 +237,25 @@ public:
} }
public: public:
uint64_t read(void* buf, uint64_t length) uint64_t read(void* buf, uint64_t length) override {
{
uint8_t extra[4]; uint8_t extra[4];
uint64_t rem_offset = m_offset % 4; uint64_t rem_offset = m_offset % 4;
if (rem_offset) if (rem_offset) {
{
uint64_t rem_rem = 4 - rem_offset; uint64_t rem_rem = 4 - rem_offset;
if (wbfsDiscRead((uint32_t)(m_offset / 4), extra, 4)) if (wbfsDiscRead((uint32_t)(m_offset / 4), extra, 4))
return 0; return 0;
memcpy(buf, extra + rem_offset, rem_rem); memcpy(buf, extra + rem_offset, rem_rem);
if (wbfsDiscRead((uint32_t)(m_offset / 4 + 1), (uint8_t*)buf + rem_rem, length - rem_rem)) if (wbfsDiscRead((uint32_t)(m_offset / 4 + 1), (uint8_t*)buf + rem_rem, length - rem_rem))
return 0; return 0;
} } else {
else
{
if (wbfsDiscRead((uint32_t)(m_offset / 4), (uint8_t*)buf, length)) if (wbfsDiscRead((uint32_t)(m_offset / 4), (uint8_t*)buf, length))
return 0; return 0;
} }
m_offset += length; m_offset += length;
return length; return length;
} }
uint64_t position() const {return m_offset;} uint64_t position() const override { return m_offset; }
void seek(int64_t offset, int whence) void seek(int64_t offset, int whence) override {
{
if (whence == SEEK_SET) if (whence == SEEK_SET)
m_offset = offset; m_offset = offset;
else if (whence == SEEK_CUR) else if (whence == SEEK_CUR)
@@ -281,25 +263,19 @@ public:
} }
}; };
std::unique_ptr<IReadStream> beginReadStream(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(*this, m_fio->beginReadStream(), offset, err));
auto ret = std::unique_ptr<IReadStream>(new ReadStream(*this, NewFileIO(filepath)->beginReadStream(), offset, Err));
if (Err) if (err)
return {}; return {};
return ret; return ret;
} }
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override { return {}; }
{
return std::unique_ptr<IWriteStream>();
}
}; };
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,34 +1,32 @@
#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 "Util.hpp"
namespace nod #include <spdlog/spdlog.h>
{
class FileIOFILE : public IFileIO namespace nod {
{
SystemString m_path; class FileIOFILE : public IFileIO {
std::string m_path;
int64_t m_maxWriteSize; int64_t m_maxWriteSize;
public:
FileIOFILE(const SystemString& path, int64_t maxWriteSize)
: m_path(path), m_maxWriteSize(maxWriteSize) {}
FileIOFILE(const SystemChar* path, int64_t maxWriteSize)
: m_path(path), m_maxWriteSize(maxWriteSize) {}
bool exists() public:
{ FileIOFILE(std::string_view path, int64_t maxWriteSize) : m_path(path), m_maxWriteSize(maxWriteSize) {}
FILE* fp = fopen(m_path.c_str(), "rb");
bool exists() override {
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);
@@ -37,120 +35,92 @@ public:
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(const SystemString& path, int64_t maxWriteSize, bool& err) WriteStream(std::string_view path, int64_t maxWriteSize, bool& err) : m_maxWriteSize(maxWriteSize) {
: m_maxWriteSize(maxWriteSize) fp = Fopen(path.data(), "wb");
{ if (!fp) {
fp = fopen(path.c_str(), "wb"); spdlog::error("unable to open '{}' for writing", path);
if (!fp)
{
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true; err = true;
} }
} }
WriteStream(const SystemString& path, uint64_t offset, int64_t maxWriteSize, bool& err) WriteStream(std::string_view path, uint64_t offset, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize) : m_maxWriteSize(maxWriteSize) {
{ fp = Fopen(path.data(), "ab");
fp = fopen(path.c_str(), "ab");
if (!fp) if (!fp)
goto FailLoc; goto FailLoc;
fclose(fp); fclose(fp);
fp = fopen(path.c_str(), "r+b"); fp = Fopen(path.data(), "r+b");
if (!fp) if (!fp)
goto FailLoc; goto FailLoc;
FSeek(fp, offset, SEEK_SET); FSeek(fp, offset, SEEK_SET);
return; return;
FailLoc: FailLoc:
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str()); spdlog::error("unable to open '{}' for writing", path);
err = true; err = true;
} }
~WriteStream() ~WriteStream() override { fclose(fp); }
{ uint64_t write(const void* buf, uint64_t length) override {
fclose(fp); if (m_maxWriteSize >= 0) {
} if (FTell(fp) + length > m_maxWriteSize) {
uint64_t write(const void* buf, uint64_t length) spdlog::error("write operation exceeds file's {}-byte limit", m_maxWriteSize);
{
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 0;
} }
} }
return fwrite(buf, 1, length, fp); return fwrite(buf, 1, length, fp);
} }
}; };
std::unique_ptr<IWriteStream> beginWriteStream() const
{ std::unique_ptr<IWriteStream> beginWriteStream() const override {
bool Err = false; bool err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, Err)); auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, err));
if (Err)
return {}; if (err)
return ret;
}
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const
{
bool Err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, Err));
if (Err)
return {}; return {};
return ret; return ret;
} }
struct ReadStream : public IFileIO::IReadStream std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override {
{ bool err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, err));
if (err)
return {};
return ret;
}
struct ReadStream : public IFileIO::IReadStream {
FILE* fp; FILE* fp;
ReadStream(const SystemString& path, bool& err) ReadStream(std::string_view path, bool& err) {
{ fp = Fopen(path.data(), "rb");
fp = fopen(path.c_str(), "rb"); if (!fp) {
if (!fp)
{
err = true; err = true;
LogModule.report(logvisor::Error, _S("unable to open '%s' for reading"), path.c_str()); spdlog::error("unable to open '{}' for reading", path);
} }
} }
ReadStream(const SystemString& path, uint64_t offset, bool& err) ReadStream(std::string_view path, uint64_t offset, bool& err) : ReadStream(path, err) {
: ReadStream(path, err)
{
if (err) if (err)
return; return;
FSeek(fp, offset, SEEK_SET); FSeek(fp, offset, SEEK_SET);
} }
~ReadStream() ~ReadStream() override { fclose(fp); }
{ void seek(int64_t offset, int whence) override { FSeek(fp, offset, whence); }
fclose(fp); uint64_t position() const override { return FTell(fp); }
} uint64_t read(void* buf, uint64_t length) override { return fread(buf, 1, length, fp); }
void seek(int64_t offset, int whence) uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) override {
{
FSeek(fp, offset, whence);
}
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; uint64_t written = 0;
uint8_t buf[0x7c00]; uint8_t buf[0x7c00];
while (length) while (length) {
{
uint64_t thisSz = nod::min(uint64_t(0x7c00), length); uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
if (read(buf, thisSz) != thisSz) if (read(buf, thisSz) != thisSz) {
{ spdlog::error("unable to read enough from file");
LogModule.report(logvisor::Error, "unable to read enough from file");
return written; return written;
} }
if (discio.write(buf, thisSz) != thisSz) if (discio.write(buf, thisSz) != thisSz) {
{ spdlog::error("unable to write enough to disc");
LogModule.report(logvisor::Error, "unable to write enough to disc");
return written; return written;
} }
length -= thisSz; length -= thisSz;
@@ -159,32 +129,30 @@ public:
return written; return written;
} }
}; };
std::unique_ptr<IReadStream> beginReadStream() const
{ std::unique_ptr<IReadStream> beginReadStream() const override {
bool Err = false; bool err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, Err)); auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, err));
if (Err)
if (err)
return {}; return {};
return ret; return ret;
} }
std::unique_ptr<IReadStream> beginReadStream(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_path, offset, Err)); auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, offset, err));
if (Err)
if (err)
return {}; 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<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,41 +1,56 @@
#include <stdio.h> #include <cstdint>
#include <stdlib.h> #if _WIN32
#include <inttypes.h> #ifndef WIN32_LEAN_AND_MEAN
#include "nod/Util.hpp" #define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#endif
#include "nod/IFileIO.hpp" #include "nod/IFileIO.hpp"
#include "Util.hpp"
namespace nod #include <spdlog/spdlog.h>
{
class FileIOWin32 : public IFileIO #include <nowide/convert.hpp>
{ #include <nowide/stackstring.hpp>
SystemString m_path;
namespace nod {
class FileIOWin32 : public IFileIO {
std::wstring m_wpath;
int64_t m_maxWriteSize; int64_t m_maxWriteSize;
public:
FileIOWin32(const SystemString& path, int64_t maxWriteSize)
: m_path(path), m_maxWriteSize(maxWriteSize) {}
FileIOWin32(const SystemChar* path, int64_t maxWriteSize)
: m_path(path), m_maxWriteSize(maxWriteSize) {}
bool exists() public:
{ FileIOWin32(std::string_view path, int64_t maxWriteSize)
HANDLE fp = CreateFileW(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, : m_wpath(nowide::widen(path)), m_maxWriteSize(maxWriteSize) {}
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
bool exists() 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) if (fp == INVALID_HANDLE_VALUE)
return false; return false;
CloseHandle(fp); CloseHandle(fp);
return true; return true;
} }
uint64_t size() uint64_t size() 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);
#else
HANDLE fp = CreateFile2(m_path.get(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr);
#endif
if (fp == INVALID_HANDLE_VALUE) if (fp == INVALID_HANDLE_VALUE)
return 0; return 0;
LARGE_INTEGER sz; LARGE_INTEGER sz;
if (!GetFileSizeEx(fp, &sz)) if (!GetFileSizeEx(fp, &sz)) {
{
CloseHandle(fp); CloseHandle(fp);
return 0; return 0;
} }
@@ -43,29 +58,33 @@ public:
return sz.QuadPart; return sz.QuadPart;
} }
struct WriteStream : public IFileIO::IWriteStream struct WriteStream : public IFileIO::IWriteStream {
{
HANDLE fp; HANDLE fp;
int64_t m_maxWriteSize; int64_t m_maxWriteSize;
WriteStream(const SystemString& path, int64_t maxWriteSize, bool& err) WriteStream(std::wstring_view wpath, int64_t maxWriteSize, bool& err) : m_maxWriteSize(maxWriteSize) {
: m_maxWriteSize(maxWriteSize) #if !WINDOWS_STORE
{ fp = CreateFileW(wpath.data(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr);
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); #else
if (fp == INVALID_HANDLE_VALUE) fp = CreateFile2(wpath.data(), GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS, nullptr);
{ #endif
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str()); if (fp == INVALID_HANDLE_VALUE) {
const nowide::stackstring path(wpath.data(), wpath.data() + wpath.size());
spdlog::error("unable to open '{}' for writing", path.get());
err = true; err = true;
} }
} }
WriteStream(const SystemString& path, uint64_t offset, int64_t maxWriteSize, bool& err) WriteStream(std::wstring_view wpath, uint64_t offset, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize) : m_maxWriteSize(maxWriteSize) {
{ #if !WINDOWS_STORE
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, fp = CreateFileW(wpath.data(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); nullptr);
if (fp == INVALID_HANDLE_VALUE) #else
{ fp = CreateFile2(wpath.data(), GENERIC_WRITE, FILE_SHARE_WRITE, OPEN_ALWAYS, nullptr);
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str()); #endif
if (fp == INVALID_HANDLE_VALUE) {
const nowide::stackstring path(wpath.data(), wpath.data() + wpath.size());
spdlog::error("unable to open '{}' for writing", path.get());
err = true; err = true;
return; return;
} }
@@ -73,20 +92,14 @@ public:
lioffset.QuadPart = offset; lioffset.QuadPart = offset;
SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN); SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
} }
~WriteStream() ~WriteStream() override { CloseHandle(fp); }
{ uint64_t write(const void* buf, uint64_t length) override {
CloseHandle(fp); if (m_maxWriteSize >= 0) {
}
uint64_t write(const void* buf, uint64_t length)
{
if (m_maxWriteSize >= 0)
{
LARGE_INTEGER li = {}; LARGE_INTEGER li = {};
LARGE_INTEGER res; LARGE_INTEGER res;
SetFilePointerEx(fp, li, &res, FILE_CURRENT); SetFilePointerEx(fp, li, &res, FILE_CURRENT);
if (res.QuadPart + int64_t(length) > m_maxWriteSize) if (res.QuadPart + int64_t(length) > m_maxWriteSize) {
{ spdlog::error("write operation exceeds file's {}-byte limit", m_maxWriteSize);
LogModule.report(logvisor::Error, _S("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize);
return 0; return 0;
} }
} }
@@ -96,83 +109,77 @@ public:
return ret; return ret;
} }
}; };
std::unique_ptr<IWriteStream> beginWriteStream() const std::unique_ptr<IWriteStream> beginWriteStream() const override {
{ bool err = false;
bool Err = false; auto ret = std::make_unique<WriteStream>(m_wpath, m_maxWriteSize, err);
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, Err));
if (Err) if (err) {
return {}; return nullptr;
}
return ret; return ret;
} }
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override {
{ bool err = false;
bool Err = false; auto ret = std::make_unique<WriteStream>(m_wpath, offset, m_maxWriteSize, err);
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, Err));
if (Err) if (err) {
return {}; return nullptr;
}
return ret; return ret;
} }
struct ReadStream : public IFileIO::IReadStream struct ReadStream : public IFileIO::IReadStream {
{
HANDLE fp; HANDLE fp;
ReadStream(const SystemString& path, bool& err) ReadStream(std::wstring_view wpath, bool& err) {
{ #if !WINDOWS_STORE
fp = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, fp = CreateFileW(wpath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); nullptr);
if (fp == INVALID_HANDLE_VALUE) #else
{ fp = CreateFile2(wpath.data(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr);
#endif
if (fp == INVALID_HANDLE_VALUE) {
err = true; err = true;
LogModule.report(logvisor::Error, _S("unable to open '%s' for reading"), path.c_str()); const nowide::stackstring path(wpath.data(), wpath.data() + wpath.size());
spdlog::error("unable to open '{}' for reading", path.get());
} }
} }
ReadStream(const SystemString& path, uint64_t offset, bool& err) ReadStream(std::wstring_view wpath, uint64_t offset, bool& err) : ReadStream(wpath, err) {
: ReadStream(path, err)
{
if (err) if (err)
return; return;
LARGE_INTEGER lioffset; LARGE_INTEGER lioffset;
lioffset.QuadPart = offset; lioffset.QuadPart = offset;
SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN); SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
} }
~ReadStream() ~ReadStream() override { CloseHandle(fp); }
{ void seek(int64_t offset, int whence) override {
CloseHandle(fp);
}
void seek(int64_t offset, int whence)
{
LARGE_INTEGER li; LARGE_INTEGER li;
li.QuadPart = offset; li.QuadPart = offset;
SetFilePointerEx(fp, li, nullptr, whence); SetFilePointerEx(fp, li, nullptr, whence);
} }
uint64_t position() const uint64_t position() const override {
{
LARGE_INTEGER li = {}; LARGE_INTEGER li = {};
LARGE_INTEGER res; LARGE_INTEGER res;
SetFilePointerEx(fp, li, &res, FILE_CURRENT); SetFilePointerEx(fp, li, &res, FILE_CURRENT);
return res.QuadPart; return res.QuadPart;
} }
uint64_t read(void* buf, uint64_t length) uint64_t read(void* buf, uint64_t length) override {
{
DWORD ret = 0; DWORD ret = 0;
ReadFile(fp, buf, length, &ret, nullptr); ReadFile(fp, buf, length, &ret, nullptr);
return ret; return ret;
} }
uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) override {
{
uint64_t written = 0; uint64_t written = 0;
uint8_t buf[0x7c00]; uint8_t buf[0x7c00];
while (length) while (length) {
{
uint64_t thisSz = nod::min(uint64_t(0x7c00), length); uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
if (read(buf, thisSz) != thisSz) if (read(buf, thisSz) != thisSz) {
{ spdlog::error("unable to read enough from file");
LogModule.report(logvisor::Error, "unable to read enough from file");
return written; return written;
} }
if (discio.write(buf, thisSz) != thisSz) if (discio.write(buf, thisSz) != thisSz) {
{ spdlog::error("unable to write enough to disc");
LogModule.report(logvisor::Error, "unable to write enough to disc");
return written; return written;
} }
length -= thisSz; length -= thisSz;
@@ -181,32 +188,32 @@ public:
return written; return written;
} }
}; };
std::unique_ptr<IReadStream> beginReadStream() const
{ std::unique_ptr<IReadStream> beginReadStream() const override {
bool Err = false; bool err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, Err)); auto ret = std::make_unique<ReadStream>(m_wpath, err);
if (Err)
return {}; if (err) {
return nullptr;
}
return ret; return ret;
} }
std::unique_ptr<IReadStream> beginReadStream(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_path, offset, Err)); auto ret = std::make_unique<ReadStream>(m_wpath, offset, err);
if (Err)
return {}; if (err) {
return nullptr;
}
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));
}
}

48
lib/IFileIO.cpp Normal file
View File

@@ -0,0 +1,48 @@
#include "nod/IFileIO.hpp"
#include "Util.hpp"
#include <spdlog/spdlog.h>
namespace nod {
uint64_t IFileIO::IWriteStream::copyFromDisc(IPartReadStream& discio, uint64_t length) {
uint64_t read = 0;
uint8_t buf[0x7c00];
while (length) {
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
uint64_t readSz = discio.read(buf, thisSz);
if (thisSz != readSz) {
spdlog::error("unable to read enough from disc");
return read;
}
if (write(buf, readSz) != readSz) {
spdlog::error("unable to write in file");
return read;
}
length -= thisSz;
read += thisSz;
}
return read;
}
uint64_t IFileIO::IWriteStream::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) {
spdlog::error("unable to read enough from disc");
return read;
}
if (write(buf, readSz) != readSz) {
spdlog::error("unable to write in file");
return read;
}
length -= thisSz;
read += thisSz;
prog(read / float(total));
}
return read;
}
} // namespace nod

3149
lib/OSUTF.c Normal file

File diff suppressed because it is too large Load Diff

71
lib/Util.cpp Normal file
View File

@@ -0,0 +1,71 @@
#include "Util.hpp"
#include <spdlog/spdlog.h>
#if _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#endif
namespace nod {
FILE* Fopen(const char* path, const char* mode, FileLockType lock) {
#if _WIN32
const nowide::wstackstring wpath(path);
const nowide::wshort_stackstring wmode(mode);
FILE* fp = _wfopen(wpath.get(), wmode.get());
if (!fp)
return nullptr;
#else
FILE* fp = fopen(path, mode);
if (!fp)
return nullptr;
#endif
if (lock != FileLockType::None) {
#if _WIN32
OVERLAPPED ov = {};
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1,
&ov);
#else
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
spdlog::error("flock {}: {}", path, strerror(errno));
#endif
}
return fp;
}
bool CheckFreeSpace(const char* path, size_t reqSz) {
#if _WIN32
ULARGE_INTEGER freeBytes;
const nowide::wstackstring wpath(path);
std::array<wchar_t, 1024> buf{};
wchar_t* end = nullptr;
DWORD ret = GetFullPathNameW(wpath.get(), 1024, buf.data(), &end);
if (ret == 0 || ret > 1024) {
spdlog::error("GetFullPathNameW {}", path);
return false;
}
if (end != nullptr) {
end[0] = L'\0';
}
if (!GetDiskFreeSpaceExW(buf.data(), &freeBytes, nullptr, nullptr)) {
spdlog::error("GetDiskFreeSpaceExW {}: {}", path, GetLastError());
return false;
}
return reqSz < freeBytes.QuadPart;
#else
struct statvfs svfs;
if (statvfs(path, &svfs)) {
spdlog::error("statvfs {}: {}", path, strerror(errno));
return false;
}
return reqSz < svfs.f_frsize * svfs.f_bavail;
#endif
}
} // namespace nod

126
lib/Util.hpp Normal file
View File

@@ -0,0 +1,126 @@
#pragma once
#if _WIN32
#include <array>
#include <cwctype>
#include <direct.h>
#include <nowide/stackstring.hpp>
#include <winapifamily.h>
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
#define WINDOWS_STORE 1
#else
#define WINDOWS_STORE 0
#endif
#else
#include <cctype>
#include <cerrno>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/statvfs.h>
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <algorithm>
#include <cstring>
#include <string>
#include <string_view>
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#include <sys/stat.h>
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
#endif
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
#endif
#if !defined(S_ISLNK)
#define S_ISLNK(m) 0
#endif
#endif
#undef min
#undef max
namespace nod {
/* define our own min/max to avoid MSVC BS */
template <typename T>
constexpr T min(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};
}
/* filesystem char type */
#if _WIN32
static inline int Mkdir(const char* path, int) {
const nowide::wstackstring str(path);
return _wmkdir(str.get());
}
using Sstat = struct ::_stat64;
static inline int Stat(const char* path, Sstat* statout) {
const nowide::wstackstring wpath(path);
return _wstat64(wpath.get(), statout);
}
#else
static inline int Mkdir(const char* path, mode_t mode) { return mkdir(path, mode); }
typedef struct stat Sstat;
static inline int Stat(const char* path, Sstat* statout) { return stat(path, statout); }
#endif
static inline int StrCaseCmp(const char* str1, const char* str2) {
#ifdef _MSC_VER
return _stricmp(str1, str2);
#else
return strcasecmp(str1, str2);
#endif
}
#ifndef ROUND_UP_32
#define ROUND_UP_32(val) (((val) + 31) & ~31)
#define ROUND_UP_16(val) (((val) + 15) & ~15)
#endif
enum class FileLockType { None = 0, Read, Write };
FILE* Fopen(const char* path, const char* mode, FileLockType lock = FileLockType::None);
static inline int FSeek(FILE* fp, int64_t offset, int whence) {
#if _WIN32
return _fseeki64(fp, offset, whence);
#elif __APPLE__ || __FreeBSD__
return fseeko(fp, offset, whence);
#else
return fseeko64(fp, offset, whence);
#endif
}
static inline int64_t FTell(FILE* fp) {
#if _WIN32
return _ftelli64(fp);
#elif __APPLE__ || __FreeBSD__
return ftello(fp);
#else
return ftello64(fp);
#endif
}
bool CheckFreeSpace(const char* path, size_t reqSz);
} // namespace nod

View File

@@ -1,36 +1,37 @@
#include "nod/aes.hpp" #include "nod/aes.hpp"
#include <stdio.h> #include <cstdio>
#include <string.h> #include <cstring>
#if _WIN32 #if _WIN32
#include <intrin.h> #include <intrin.h>
#else #elif __x86_64__
#include <cpuid.h> #include <cpuid.h>
#endif #endif
namespace nod #if __AES__ || (!defined(__clang__) && _MSC_VER >= 1800)
{ #define _AES_NI 1
#endif
namespace nod {
/* rotates x one bit to the left */ /* rotates x one bit to the left */
#define ROTL(x) (((x)>>7)|((x)<<1)) #define ROTL(x) (((x) >> 7) | ((x) << 1))
/* Rotates 32-bit word left by 1, 2 or 3 byte */ /* Rotates 32-bit word left by 1, 2 or 3 byte */
#define ROTL8(x) (((x)<<8)|((x)>>24)) #define ROTL8(x) (((x) << 8) | ((x) >> 24))
#define ROTL16(x) (((x)<<16)|((x)>>16)) #define ROTL16(x) (((x) << 16) | ((x) >> 16))
#define ROTL24(x) (((x)<<24)|((x)>>8)) #define ROTL24(x) (((x) << 24) | ((x) >> 8))
static const uint8_t InCo[4] = {0xB, 0xD, 0x9, 0xE}; /* Inverse Coefficients */ static const uint8_t InCo[4] = {0xB, 0xD, 0x9, 0xE}; /* Inverse Coefficients */
static inline uint32_t pack(const uint8_t* b) static uint32_t pack(const uint8_t* b) {
{
/* pack bytes into a 32-bit Word */ /* pack bytes into a 32-bit Word */
return ((uint32_t)b[3] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | (uint32_t)b[0]; return ((uint32_t)b[3] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | (uint32_t)b[0];
} }
static inline void unpack(uint32_t a, uint8_t* b) static void unpack(uint32_t a, uint8_t* b) {
{
/* unpack bytes from a word */ /* unpack bytes from a word */
b[0] = (uint8_t)a; b[0] = (uint8_t)a;
b[1] = (uint8_t)(a >> 8); b[1] = (uint8_t)(a >> 8);
@@ -38,13 +39,9 @@ static inline void unpack(uint32_t a, uint8_t* b)
b[3] = (uint8_t)(a >> 24); b[3] = (uint8_t)(a >> 24);
} }
static inline uint8_t xtime(uint8_t a) constexpr uint8_t xtime(uint8_t a) { return ((a << 1) ^ (((a >> 7) & 1) * 0x11B)); }
{
return ((a << 1) ^ (((a>>7) & 1) * 0x11B));
}
static const struct SoftwareAESTables static const struct SoftwareAESTables {
{
uint8_t fbsub[256]; uint8_t fbsub[256];
uint8_t rbsub[256]; uint8_t rbsub[256];
uint8_t ptab[256], ltab[256]; uint8_t ptab[256], ltab[256];
@@ -52,15 +49,15 @@ static const struct SoftwareAESTables
uint32_t rtable[256]; uint32_t rtable[256];
uint32_t rco[30]; uint32_t rco[30];
uint8_t bmul(uint8_t x, uint8_t y) const uint8_t bmul(uint8_t x, uint8_t y) const {
{
/* x.y= AntiLog(Log(x) + Log(y)) */ /* x.y= AntiLog(Log(x) + Log(y)) */
if (x && y) return ptab[(ltab[x] + ltab[y]) % 255]; if (x && y)
else return 0; return ptab[(ltab[x] + ltab[y]) % 255];
else
return 0;
} }
uint32_t SubByte(uint32_t a) const uint32_t SubByte(uint32_t a) const {
{
uint8_t b[4]; uint8_t b[4];
unpack(a, b); unpack(a, b);
b[0] = fbsub[b[0]]; b[0] = fbsub[b[0]];
@@ -70,17 +67,15 @@ static const struct SoftwareAESTables
return pack(b); return pack(b);
} }
uint8_t product(uint32_t x, uint32_t y) const uint8_t product(uint32_t x, uint32_t y) const {
{
/* dot product of two 4-byte arrays */ /* dot product of two 4-byte arrays */
uint8_t xb[4], yb[4]; uint8_t xb[4], yb[4];
unpack(x, xb); unpack(x, xb);
unpack(y, yb); unpack(y, yb);
return bmul(xb[0], yb[0])^bmul(xb[1], yb[1])^bmul(xb[2], yb[2])^bmul(xb[3], yb[3]); return bmul(xb[0], yb[0]) ^ bmul(xb[1], yb[1]) ^ bmul(xb[2], yb[2]) ^ bmul(xb[3], yb[3]);
} }
uint32_t InvMixCol(uint32_t x) const uint32_t InvMixCol(uint32_t x) const {
{
/* matrix Multiplication */ /* matrix Multiplication */
uint32_t y, m; uint32_t y, m;
uint8_t b[4]; uint8_t b[4];
@@ -97,8 +92,7 @@ static const struct SoftwareAESTables
return y; return y;
} }
uint8_t ByteSub(uint8_t x) const uint8_t ByteSub(uint8_t x) const {
{
uint8_t y = ptab[255 - ltab[x]]; /* multiplicative inverse */ uint8_t y = ptab[255 - ltab[x]]; /* multiplicative inverse */
x = y; x = y;
x = ROTL(x); x = ROTL(x);
@@ -113,8 +107,7 @@ static const struct SoftwareAESTables
return y; return y;
} }
SoftwareAESTables() SoftwareAESTables() {
{
/* generate tables */ /* generate tables */
int i; int i;
uint8_t y, b[4]; uint8_t y, b[4];
@@ -127,8 +120,7 @@ static const struct SoftwareAESTables
ptab[1] = 3; ptab[1] = 3;
ltab[3] = 1; ltab[3] = 1;
for (i = 2; i < 256; i++) for (i = 2; i < 256; i++) {
{
ptab[i] = ptab[i - 1] ^ xtime(ptab[i - 1]); ptab[i] = ptab[i - 1] ^ xtime(ptab[i - 1]);
ltab[ptab[i]] = i; ltab[ptab[i]] = i;
} }
@@ -138,22 +130,19 @@ static const struct SoftwareAESTables
fbsub[0] = 0x63; fbsub[0] = 0x63;
rbsub[0x63] = 0; rbsub[0x63] = 0;
for (i = 1; i < 256; i++) for (i = 1; i < 256; i++) {
{
y = ByteSub((uint8_t)i); y = ByteSub((uint8_t)i);
fbsub[i] = y; fbsub[i] = y;
rbsub[y] = i; rbsub[y] = i;
} }
for (i = 0, y = 1; i < 30; i++) for (i = 0, y = 1; i < 30; i++) {
{
rco[i] = y; rco[i] = y;
y = xtime(y); y = xtime(y);
} }
/* calculate forward and reverse tables */ /* calculate forward and reverse tables */
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++) {
{
y = fbsub[i]; y = fbsub[i];
b[3] = y ^ xtime(y); b[3] = y ^ xtime(y);
b[2] = y; b[2] = y;
@@ -171,8 +160,7 @@ static const struct SoftwareAESTables
} }
} AEStb; } AEStb;
class SoftwareAES : public IAES class SoftwareAES : public IAES {
{
protected: protected:
/* Parameter-dependent data */ /* Parameter-dependent data */
int Nk, Nb, Nr; int Nk, Nb, Nr;
@@ -185,13 +173,12 @@ protected:
void _decrypt(uint8_t* buff); void _decrypt(uint8_t* buff);
public: public:
void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len); void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) override;
void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len); void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) override;
void setKey(const uint8_t* key); void setKey(const uint8_t* key) override;
}; };
void SoftwareAES::gkey(int nb, int nk, const uint8_t* key) void SoftwareAES::gkey(int nb, int nk, const uint8_t* key) {
{
/* blocksize=32*nb bits. Key=32*nk bits */ /* blocksize=32*nb bits. Key=32*nk bits */
/* currently nb,bk = 4, 6 or 8 */ /* currently nb,bk = 4, 6 or 8 */
/* key comes as 4*Nk bytes */ /* key comes as 4*Nk bytes */
@@ -204,17 +191,23 @@ void SoftwareAES::gkey(int nb, int nk, const uint8_t* key)
Nk = nk; Nk = nk;
/* Nr is number of rounds */ /* Nr is number of rounds */
if (Nb >= Nk) Nr = 6 + Nb; if (Nb >= Nk)
else Nr = 6 + Nk; Nr = 6 + Nb;
else
Nr = 6 + Nk;
C1 = 1; C1 = 1;
if (Nb < 8) { C2 = 2; C3 = 3; } if (Nb < 8) {
else { C2 = 3; C3 = 4; } C2 = 2;
C3 = 3;
} else {
C2 = 3;
C3 = 4;
}
/* pre-calculate forward and reverse increments */ /* pre-calculate forward and reverse increments */
for (m = j = 0; j < nb; j++, m += 3) for (m = j = 0; j < nb; j++, m += 3) {
{
fi[m] = (j + C1) % nb; fi[m] = (j + C1) % nb;
fi[m + 1] = (j + C2) % nb; fi[m + 1] = (j + C2) % nb;
fi[m + 2] = (j + C3) % nb; fi[m + 2] = (j + C3) % nb;
@@ -225,61 +218,56 @@ void SoftwareAES::gkey(int nb, int nk, const uint8_t* key)
N = Nb * (Nr + 1); N = Nb * (Nr + 1);
for (i = j = 0; i < Nk; i++, j += 4) for (i = j = 0; i < Nk; i++, j += 4) {
{
CipherKey[i] = pack(key + j); CipherKey[i] = pack(key + j);
} }
for (i = 0; i < Nk; i++) fkey[i] = CipherKey[i]; for (i = 0; i < Nk; i++)
fkey[i] = CipherKey[i];
for (j = Nk, k = 0; j < N; j += Nk, k++) for (j = Nk, k = 0; j < N; j += Nk, k++) {
{ fkey[j] = fkey[j - Nk] ^ AEStb.SubByte(ROTL24(fkey[j - 1])) ^ AEStb.rco[k];
fkey[j] = fkey[j - Nk] ^ AEStb.SubByte(ROTL24(fkey[j - 1]))^AEStb.rco[k];
if (Nk <= 6) if (Nk <= 6) {
{
for (i = 1; i < Nk && (i + j) < N; i++) for (i = 1; i < Nk && (i + j) < N; i++)
fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1];
} } else {
else
{
for (i = 1; i < 4 && (i + j) < N; i++) for (i = 1; i < 4 && (i + j) < N; i++)
fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1];
if ((j + 4) < N) fkey[j + 4] = fkey[j + 4 - Nk] ^ AEStb.SubByte(fkey[j + 3]); if ((j + 4) < N)
fkey[j + 4] = fkey[j + 4 - Nk] ^ AEStb.SubByte(fkey[j + 3]);
for (i = 5; i < Nk && (i + j) < N; i++) for (i = 5; i < Nk && (i + j) < N; i++)
fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1];
} }
} }
/* now for the expanded decrypt key in reverse order */ /* now for the expanded decrypt key in reverse order */
for (j = 0; j < Nb; j++) rkey[j + N - Nb] = fkey[j]; for (j = 0; j < Nb; j++)
rkey[j + N - Nb] = fkey[j];
for (i = Nb; i < N - Nb; i += Nb) for (i = Nb; i < N - Nb; i += Nb) {
{
k = N - Nb - i; k = N - Nb - i;
for (j = 0; j < Nb; j++) rkey[k + j] = AEStb.InvMixCol(fkey[i + j]); for (j = 0; j < Nb; j++)
rkey[k + j] = AEStb.InvMixCol(fkey[i + j]);
} }
for (j = N - Nb; j < N; j++) rkey[j - N + Nb] = fkey[j]; for (j = N - Nb; j < N; j++)
rkey[j - N + Nb] = fkey[j];
} }
/* There is an obvious time/space trade-off possible here. * /* There is an obvious time/space trade-off possible here. *
* Instead of just one ftable[], I could have 4, the other * * Instead of just one ftable[], I could have 4, the other *
* 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */ * 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */
void SoftwareAES::_encrypt(uint8_t* buff) void SoftwareAES::_encrypt(uint8_t* buff) {
{
int i, j, k, m; int i, j, k, m;
uint32_t a[8], b[8], *x, *y, *t; uint32_t a[8], b[8], *x, *y, *t;
for (i = j = 0; i < Nb; i++, j += 4) for (i = j = 0; i < Nb; i++, j += 4) {
{
a[i] = pack(buff + j); a[i] = pack(buff + j);
a[i] ^= fkey[i]; a[i] ^= fkey[i];
} }
@@ -289,21 +277,17 @@ void SoftwareAES::_encrypt(uint8_t* buff)
y = b; y = b;
/* State alternates between a and b */ /* State alternates between a and b */
for (i = 1; i < Nr; i++) for (i = 1; i < Nr; i++) {
{
/* Nr is number of rounds. May be odd. */ /* Nr is number of rounds. May be odd. */
/* if Nb is fixed - unroll this next /* if Nb is fixed - unroll this next
loop and hard-code in the values of fi[] */ loop and hard-code in the values of fi[] */
for (m = j = 0; j < Nb; j++, m += 3) for (m = j = 0; j < Nb; j++, m += 3) {
{
/* deal with each 32-bit element of the State */ /* deal with each 32-bit element of the State */
/* This is the time-critical bit */ /* This is the time-critical bit */
y[j] = fkey[k++] ^ AEStb.ftable[(uint8_t)x[j]] ^ y[j] = fkey[k++] ^ AEStb.ftable[(uint8_t)x[j]] ^ ROTL8(AEStb.ftable[(uint8_t)(x[fi[m]] >> 8)]) ^
ROTL8(AEStb.ftable[(uint8_t)(x[fi[m]] >> 8)])^ ROTL16(AEStb.ftable[(uint8_t)(x[fi[m + 1]] >> 16)]) ^ ROTL24(AEStb.ftable[(uint8_t)(x[fi[m + 2]] >> 24)]);
ROTL16(AEStb.ftable[(uint8_t)(x[fi[m + 1]] >> 16)])^
ROTL24(AEStb.ftable[(uint8_t)(x[fi[m + 2]] >> 24)]);
} }
t = x; t = x;
@@ -312,16 +296,13 @@ void SoftwareAES::_encrypt(uint8_t* buff)
} }
/* Last Round - unroll if possible */ /* Last Round - unroll if possible */
for (m = j = 0; j < Nb; j++, m += 3) for (m = j = 0; j < Nb; j++, m += 3) {
{ y[j] = fkey[k++] ^ (uint32_t)AEStb.fbsub[(uint8_t)x[j]] ^ ROTL8((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m]] >> 8)]) ^
y[j] = fkey[k++] ^ (uint32_t)AEStb.fbsub[(uint8_t)x[j]] ^ ROTL16((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m + 1]] >> 16)]) ^
ROTL8((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m]] >> 8)])^
ROTL16((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m + 1]] >> 16)])^
ROTL24((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m + 2]] >> 24)]); ROTL24((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m + 2]] >> 24)]);
} }
for (i = j = 0; i < Nb; i++, j += 4) for (i = j = 0; i < Nb; i++, j += 4) {
{
unpack(y[i], (uint8_t*)&buff[j]); unpack(y[i], (uint8_t*)&buff[j]);
x[i] = y[i] = 0; /* clean up stack */ x[i] = y[i] = 0; /* clean up stack */
} }
@@ -329,13 +310,11 @@ void SoftwareAES::_encrypt(uint8_t* buff)
return; return;
} }
void SoftwareAES::_decrypt(uint8_t* buff) void SoftwareAES::_decrypt(uint8_t* buff) {
{
int i, j, k, m; int i, j, k, m;
uint32_t a[8], b[8], *x, *y, *t; uint32_t a[8], b[8], *x, *y, *t;
for (i = j = 0; i < Nb; i++, j += 4) for (i = j = 0; i < Nb; i++, j += 4) {
{
a[i] = pack(buff + j); a[i] = pack(buff + j);
a[i] ^= rkey[i]; a[i] ^= rkey[i];
} }
@@ -345,20 +324,16 @@ void SoftwareAES::_decrypt(uint8_t* buff)
y = b; y = b;
/* State alternates between a and b */ /* State alternates between a and b */
for (i = 1; i < Nr; i++) for (i = 1; i < Nr; i++) {
{
/* Nr is number of rounds. May be odd. */ /* Nr is number of rounds. May be odd. */
/* if Nb is fixed - unroll this next /* if Nb is fixed - unroll this next
loop and hard-code in the values of ri[] */ loop and hard-code in the values of ri[] */
for (m = j = 0; j < Nb; j++, m += 3) for (m = j = 0; j < Nb; j++, m += 3) {
{
/* This is the time-critical bit */ /* This is the time-critical bit */
y[j] = rkey[k++] ^ AEStb.rtable[(uint8_t)x[j]] ^ y[j] = rkey[k++] ^ AEStb.rtable[(uint8_t)x[j]] ^ ROTL8(AEStb.rtable[(uint8_t)(x[ri[m]] >> 8)]) ^
ROTL8(AEStb.rtable[(uint8_t)(x[ri[m]] >> 8)])^ ROTL16(AEStb.rtable[(uint8_t)(x[ri[m + 1]] >> 16)]) ^ ROTL24(AEStb.rtable[(uint8_t)(x[ri[m + 2]] >> 24)]);
ROTL16(AEStb.rtable[(uint8_t)(x[ri[m + 1]] >> 16)])^
ROTL24(AEStb.rtable[(uint8_t)(x[ri[m + 2]] >> 24)]);
} }
t = x; t = x;
@@ -367,16 +342,13 @@ void SoftwareAES::_decrypt(uint8_t* buff)
} }
/* Last Round - unroll if possible */ /* Last Round - unroll if possible */
for (m = j = 0; j < Nb; j++, m += 3) for (m = j = 0; j < Nb; j++, m += 3) {
{ y[j] = rkey[k++] ^ (uint32_t)AEStb.rbsub[(uint8_t)x[j]] ^ ROTL8((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m]] >> 8)]) ^
y[j] = rkey[k++] ^ (uint32_t)AEStb.rbsub[(uint8_t)x[j]] ^ ROTL16((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m + 1]] >> 16)]) ^
ROTL8((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m]] >> 8)])^
ROTL16((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m + 1]] >> 16)])^
ROTL24((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m + 2]] >> 24)]); ROTL24((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m + 2]] >> 24)]);
} }
for (i = j = 0; i < Nb; i++, j += 4) for (i = j = 0; i < Nb; i++, j += 4) {
{
unpack(y[i], (uint8_t*)&buff[j]); unpack(y[i], (uint8_t*)&buff[j]);
x[i] = y[i] = 0; /* clean up stack */ x[i] = y[i] = 0; /* clean up stack */
} }
@@ -384,45 +356,42 @@ void SoftwareAES::_decrypt(uint8_t* buff)
return; return;
} }
void SoftwareAES::setKey(const uint8_t* key) void SoftwareAES::setKey(const uint8_t* key) { gkey(4, 4, key); }
{
gkey(4, 4, key);
}
// CBC mode decryption // CBC mode decryption
void SoftwareAES::decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) void SoftwareAES::decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) {
{
uint8_t block[16]; uint8_t block[16];
const uint8_t* ctext_ptr; const uint8_t* ctext_ptr;
unsigned int blockno = 0, i; unsigned int blockno = 0, i;
//fprintf( stderr,"aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len ); // fprintf( stderr,"aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len );
//printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); // printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len);
for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) {
{
unsigned int fraction; unsigned int fraction;
if (blockno == (len / sizeof(block))) // last block if (blockno == (len / sizeof(block))) // last block
{ {
fraction = len % sizeof(block); fraction = len % sizeof(block);
if (fraction == 0) break; if (fraction == 0)
break;
memset(block, 0, sizeof(block)); memset(block, 0, sizeof(block));
} } else
else fraction = 16; fraction = 16;
// debug_printf("block %d: fraction = %d\n", blockno, fraction); // debug_printf("block %d: fraction = %d\n", blockno, fraction);
memcpy(block, inbuf + blockno * sizeof(block), fraction); memcpy(block, inbuf + blockno * sizeof(block), fraction);
_decrypt(block); _decrypt(block);
if (blockno == 0) ctext_ptr = iv; if (blockno == 0)
else ctext_ptr = (uint8_t*)(inbuf + (blockno - 1) * sizeof(block)); ctext_ptr = iv;
else
ctext_ptr = (uint8_t*)(inbuf + (blockno - 1) * sizeof(block));
for (i = 0; i < fraction; i++) for (i = 0; i < fraction; i++)
outbuf[blockno * sizeof(block) + i] = outbuf[blockno * sizeof(block) + i] = ctext_ptr[i] ^ block[i];
ctext_ptr[i] ^ block[i];
// debug_printf("Block %d output: ", blockno); // debug_printf("Block %d output: ", blockno);
// hexdump(outbuf + blockno*sizeof(block), 16); // hexdump(outbuf + blockno*sizeof(block), 16);
@@ -430,29 +399,28 @@ void SoftwareAES::decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outb
} }
// CBC mode encryption // CBC mode encryption
void SoftwareAES::encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) void SoftwareAES::encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) {
{
uint8_t block[16]; uint8_t block[16];
uint8_t feedback[16]; uint8_t feedback[16];
memcpy(feedback, iv, 16); memcpy(feedback, iv, 16);
unsigned int blockno = 0, i; unsigned int blockno = 0, i;
//printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); // printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len);
//fprintf( stderr,"aes_encrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); // fprintf( stderr,"aes_encrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len);
for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) {
{
unsigned int fraction; unsigned int fraction;
if (blockno == (len / sizeof(block))) // last block if (blockno == (len / sizeof(block))) // last block
{ {
fraction = len % sizeof(block); fraction = len % sizeof(block);
if (fraction == 0) break; if (fraction == 0)
break;
memset(block, 0, sizeof(block)); memset(block, 0, sizeof(block));
} } else
else fraction = 16; fraction = 16;
// debug_printf("block %d: fraction = %d\n", blockno, fraction); // debug_printf("block %d: fraction = %d\n", blockno, fraction);
memcpy(block, inbuf + blockno * sizeof(block), fraction); memcpy(block, inbuf + blockno * sizeof(block), fraction);
@@ -468,49 +436,45 @@ void SoftwareAES::encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outb
} }
} }
#if __AES__ || _MSC_VER >= 1800 #if _AES_NI
#include <wmmintrin.h> #include <wmmintrin.h>
class NiAES : public IAES class NiAES : public IAES {
{
__m128i m_ekey[11]; __m128i m_ekey[11];
__m128i m_dkey[11]; __m128i m_dkey[11];
public: public:
void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) {
{ __m128i feedback, data;
__m128i feedback,data; uint64_t i, j;
uint64_t i,j; if (len % 16)
if (len%16) len = len / 16 + 1;
len = len/16+1;
else else
len /= 16; len /= 16;
feedback = _mm_loadu_si128((__m128i*)iv); feedback = _mm_loadu_si128((__m128i*)iv);
for (i=0 ; i<len ; i++) for (i = 0; i < len; i++) {
{
data = _mm_loadu_si128(&((__m128i*)inbuf)[i]); data = _mm_loadu_si128(&((__m128i*)inbuf)[i]);
feedback = _mm_xor_si128(data, feedback); feedback = _mm_xor_si128(data, feedback);
feedback = _mm_xor_si128(feedback, m_ekey[0]); feedback = _mm_xor_si128(feedback, m_ekey[0]);
for (j=1 ; j<10 ; j++) for (j = 1; j < 10; j++)
feedback = _mm_aesenc_si128(feedback, m_ekey[j]); feedback = _mm_aesenc_si128(feedback, m_ekey[j]);
feedback = _mm_aesenclast_si128(feedback, m_ekey[j]); feedback = _mm_aesenclast_si128(feedback, m_ekey[j]);
_mm_storeu_si128(&((__m128i*)outbuf)[i], feedback); _mm_storeu_si128(&((__m128i*)outbuf)[i], feedback);
} }
} }
void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) {
{ __m128i data, feedback, last_in;
__m128i data,feedback,last_in; uint64_t i, j;
uint64_t i,j; if (len % 16)
if (len%16) len = len / 16 + 1;
len = len/16+1;
else else
len /= 16; len /= 16;
feedback = _mm_loadu_si128((__m128i*)iv); feedback = _mm_loadu_si128((__m128i*)iv);
for (i=0 ; i<len ; i++) for (i = 0; i < len; i++) {
{ last_in = _mm_loadu_si128(&((__m128i*)inbuf)[i]);
last_in=_mm_loadu_si128(&((__m128i*)inbuf)[i]);
data = _mm_xor_si128(last_in, m_dkey[0]); data = _mm_xor_si128(last_in, m_dkey[0]);
for (j=1 ; j<10 ; j++) for (j = 1; j < 10; j++)
data = _mm_aesdec_si128(data, m_dkey[j]); data = _mm_aesdec_si128(data, m_dkey[j]);
data = _mm_aesdeclast_si128(data, m_dkey[j]); data = _mm_aesdeclast_si128(data, m_dkey[j]);
data = _mm_xor_si128(data, feedback); data = _mm_xor_si128(data, feedback);
@@ -519,22 +483,20 @@ public:
} }
} }
static inline __m128i AES_128_ASSIST (__m128i temp1, __m128i temp2) static inline __m128i AES_128_ASSIST(__m128i temp1, __m128i temp2) {
{
__m128i temp3; __m128i temp3;
temp2 = _mm_shuffle_epi32 (temp2 ,0xff); temp2 = _mm_shuffle_epi32(temp2, 0xff);
temp3 = _mm_slli_si128 (temp1, 0x4); temp3 = _mm_slli_si128(temp1, 0x4);
temp1 = _mm_xor_si128 (temp1, temp3); temp1 = _mm_xor_si128(temp1, temp3);
temp3 = _mm_slli_si128 (temp3, 0x4); temp3 = _mm_slli_si128(temp3, 0x4);
temp1 = _mm_xor_si128 (temp1, temp3); temp1 = _mm_xor_si128(temp1, temp3);
temp3 = _mm_slli_si128 (temp3, 0x4); temp3 = _mm_slli_si128(temp3, 0x4);
temp1 = _mm_xor_si128 (temp1, temp3); temp1 = _mm_xor_si128(temp1, temp3);
temp1 = _mm_xor_si128 (temp1, temp2); temp1 = _mm_xor_si128(temp1, temp2);
return temp1; return temp1;
} }
void setKey(const uint8_t* key) void setKey(const uint8_t* key) {
{
__m128i temp1, temp2; __m128i temp1, temp2;
temp1 = _mm_loadu_si128((__m128i*)key); temp1 = _mm_loadu_si128((__m128i*)key);
@@ -583,32 +545,30 @@ public:
} }
}; };
static int HAS_AES_NI = -1;
#endif #endif
static int HAS_AES_NI = -1; std::unique_ptr<IAES> NewAES() {
std::unique_ptr<IAES> NewAES() #if _AES_NI
{ if (HAS_AES_NI == -1) {
#if __AES__ || _MSC_VER >= 1800
if (HAS_AES_NI == -1)
{
#if _MSC_VER #if _MSC_VER
int info[4]; int info[4];
__cpuid(info, 1); __cpuid(info, 1);
HAS_AES_NI = ((info[2] & 0x2000000) != 0); HAS_AES_NI = ((info[2] & 0x2000000) != 0);
#else #else
unsigned int a,b,c,d; unsigned int a, b, c, d;
__cpuid(1, a,b,c,d); __cpuid(1, a, b, c, d);
HAS_AES_NI = ((c & 0x2000000) != 0); HAS_AES_NI = ((c & 0x2000000) != 0);
#endif #endif
} }
if (HAS_AES_NI) if (HAS_AES_NI)
return std::unique_ptr<IAES>(new NiAES); return std::make_unique<NiAES>();
else else
return std::unique_ptr<IAES>(new SoftwareAES); return std::make_unique<SoftwareAES>();
#else #else
return std::unique_ptr<IAES>(new SoftwareAES); return std::make_unique<SoftwareAES>();
#endif #endif
} }
} } // namespace nod

View File

@@ -1,22 +1,23 @@
#include <stdio.h>
#include "nod/nod.hpp" #include "nod/nod.hpp"
#include <cstdio>
#include "nod/DiscBase.hpp" #include "nod/DiscBase.hpp"
#include "nod/DiscGCN.hpp"
#include "nod/DiscWii.hpp"
namespace nod #include <spdlog/spdlog.h>
{
logvisor::Module LogModule("nod"); namespace nod {
std::unique_ptr<IDiscIO> NewDiscIOISO(std::string_view path);
std::unique_ptr<IDiscIO> NewDiscIOWBFS(std::string_view path);
std::unique_ptr<IDiscIO> NewDiscIONFS(std::string_view path);
std::unique_ptr<IDiscIO> NewDiscIOISO(const SystemChar* path); std::unique_ptr<DiscBase> OpenDiscFromImage(std::string_view path, bool& isWii) {
std::unique_ptr<IDiscIO> NewDiscIOWBFS(const SystemChar* path);
std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* 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()) {
{ spdlog::error("Unable to open '{}'", path);
LogModule.report(logvisor::Error, _S("Unable to open '%s'"), path);
return {}; return {};
} }
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream(); std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
@@ -26,29 +27,30 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii)
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) {
{ spdlog::error("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('.'));
const auto slashPos = SignedSize(path.find_last_of("/\\"));
if (magic == nod::SBig((uint32_t)'WBFS')) {
discIO = NewDiscIOWBFS(path); discIO = NewDiscIOWBFS(path);
isWii = true; isWii = true;
} } else if (path.size() > 4 && dotPos != -1 && dotPos > slashPos &&
else !path.compare(slashPos + 1, 4, "hif_") &&
{ !path.compare(dotPos, path.size() - dotPos, ".nfs")) {
discIO = NewDiscIONFS(path);
isWii = true;
} else {
rs->seek(0x18, SEEK_SET); rs->seek(0x18, SEEK_SET);
rs->read(&magic, 4); rs->read(&magic, 4);
magic = nod::SBig(magic); magic = nod::SBig(magic);
if (magic == 0x5D1C9EA3) if (magic == 0x5D1C9EA3) {
{
discIO = NewDiscIOISO(path); discIO = NewDiscIOISO(path);
isWii = true; isWii = true;
} } else {
else
{
rs->read(&magic, 4); rs->read(&magic, 4);
magic = nod::SBig(magic); magic = nod::SBig(magic);
if (magic == 0xC2339F3D) if (magic == 0xC2339F3D)
@@ -56,33 +58,29 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii)
} }
} }
if (!discIO) if (!discIO) {
{ spdlog::error("'{}' 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; return ret;
} }
ret = std::unique_ptr<DiscBase>(new DiscGCN(std::move(discIO), Err)); ret = std::make_unique<DiscGCN>(std::move(discIO), err);
if (Err) if (err)
return {}; return {};
return ret; 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

Submodule logvisor deleted from f8ab0e03ba