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
*.autosave
docs/*
.idea/
cmake-build-*
build/
out/

3
.gitmodules vendored
View File

@@ -1,3 +1,4 @@
[submodule "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)
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) # because of CMAKE_CXX_STANDARD
project(nod)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
project(nod VERSION 0.1)
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()
if (NOT TARGET logvisor)
add_subdirectory(logvisor)
set(LOGVISOR_INCLUDE_DIR logvisor/include)
endif()
include (CMakePackageConfigHelpers)
include_directories(include ${LOGVISOR_INCLUDE_DIR})
file(GLOB NOD_HEADERS include/nod/*.h*)
if (WIN32 AND NOT TARGET nowide)
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(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
/* Sample logging lambda for progress feedback */
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)
{
lastIdx = idx;
/* NOD provides I/O wrappers using wchar_t on Windows;
* _S() conditionally makes string-literals wide */
nod::Printf(_S("\n"));
fmt::print(_S("\n"));
}
if (bytes != -1)
nod::Printf(_S("\r%s %" PRISize " B"), name.c_str(), bytes);
fmt::print(_S("\r{} {} B"), name, bytes);
else
nod::Printf(_S("\r%s"), name.c_str());
fmt::print(_S("\r{}"), name);
fflush(stdout);
};

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -1,87 +1,34 @@
#ifndef __NOD_IFILE_IO__
#define __NOD_IFILE_IO__
#pragma once
#include <memory>
#include <cstdint>
#include <functional>
#include <stdlib.h>
#include "IDiscIO.hpp"
#include "Util.hpp"
#include <memory>
#include <string_view>
namespace nod
{
#include "nod/IDiscIO.hpp"
class IFileIO
{
namespace nod {
class IFileIO {
public:
virtual ~IFileIO() = default;
virtual bool exists()=0;
virtual uint64_t size()=0;
virtual ~IFileIO() = default;
virtual bool exists() = 0;
virtual uint64_t size() = 0;
struct IWriteStream : nod::IWriteStream
{
uint64_t 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)
{
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(uint64_t offset) const=0;
struct IWriteStream : nod::IWriteStream {
uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length);
uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length, const std::function<void(float)>& prog);
};
virtual std::unique_ptr<IWriteStream> beginWriteStream() const = 0;
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const = 0;
struct IReadStream : nod::IReadStream
{
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(uint64_t offset) const=0;
struct IReadStream : nod::IReadStream {
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(uint64_t offset) const = 0;
};
std::unique_ptr<IFileIO> NewFileIO(const SystemString& path, int64_t maxWriteSize=-1);
std::unique_ptr<IFileIO> NewFileIO(const SystemChar* path, int64_t maxWriteSize=-1);
std::unique_ptr<IFileIO> NewFileIO(std::string_view path, int64_t maxWriteSize = -1);
}
#endif // __NOD_IFILE_IO__
} // namespace nod

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__
#define __AES_HPP__
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <cstdint>
#include <cstdlib>
#include <memory>
namespace nod
{
namespace nod {
class IAES
{
class IAES {
public:
virtual ~IAES() = default;
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 setKey(const uint8_t* key)=0;
virtual ~IAES() = default;
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 setKey(const uint8_t* key) = 0;
};
std::unique_ptr<IAES> NewAES();
}
#endif //__AES_HPP__
} // namespace nod

View File

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

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
aes.cpp
sha1.c
DiscBase.cpp
DiscGCN.cpp
DiscIOISO.cpp
DiscIOWBFS.cpp
DiscWii.cpp
DirectoryEnumerator.cpp
nod.cpp
${PLAT_SRCS}
${NOD_HEADERS})
if(NOT MSVC)
set_source_files_properties(aes.cpp PROPERTIES COMPILE_FLAGS -maes)
aes.cpp
sha1.c
DirectoryEnumerator.cpp
DiscBase.cpp
DiscGCN.cpp
DiscIOISO.cpp
DiscIONFS.cpp
DiscIOWBFS.cpp
DiscWii.cpp
IFileIO.cpp
nod.cpp
OSUTF.c
Util.cpp
Util.hpp
../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()
if(NOT MSVC AND ${CMAKE_SYSTEM_PROCESSOR} STREQUAL x86_64)
set_source_files_properties(aes.cpp PROPERTIES COMPILE_FLAGS -maes)
endif()
# Associate target with export
install(
TARGETS nod
EXPORT nodTargets
ARCHIVE DESTINATION "lib"
INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target.
)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../include/nod DESTINATION include)

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

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

File diff suppressed because it is too large Load Diff

View File

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

Submodule logvisor deleted from f8ab0e03ba