Compare commits
No commits in common. "main" and "synthxex-0.0.3" have entirely different histories.
main
...
synthxex-0
28 changed files with 1144 additions and 2678 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,6 +0,0 @@
|
|||
build
|
||||
result
|
||||
*~
|
||||
*#
|
||||
\#*
|
||||
*.orig
|
|
@ -16,63 +16,79 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cmake_minimum_required(VERSION 3.24.2)
|
||||
# This version of CMake is available on Debian bullseye backports. I think that's a reasonably old version that most people should have.
|
||||
cmake_minimum_required(VERSION 3.25.1)
|
||||
project(synthxex)
|
||||
|
||||
# Make sure we're not being compiled with MSVC (we don't support it)
|
||||
if(MSVC)
|
||||
message(SEND_ERROR "Compiling with MSVC is not supported. Please use GCC or Clang via the -DCMAKE_C_COMPILER flag or CC environment variable. (Example: cmake -DCMAKE_C_COMPILER=\"gcc\" ..).")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Gather all source and header files
|
||||
file(GLOB_RECURSE allsources
|
||||
${CMAKE_SOURCE_DIR}/src/*.c
|
||||
${CMAKE_SOURCE_DIR}/src/*.h
|
||||
${CMAKE_SOURCE_DIR}/include/getopt_port/*.c
|
||||
${CMAKE_SOURCE_DIR}/include/getopt_port/*.h
|
||||
${CMAKE_SOURCE_DIR}/include/nettle/*.c
|
||||
${CMAKE_SOURCE_DIR}/include/nettle/*.h
|
||||
)
|
||||
# Setting sources...
|
||||
set(allsources ${CMAKE_SOURCE_DIR}/src/main.c)
|
||||
|
||||
# Setting compilation settings
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/common.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/crypto.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/datastorage.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/common/datastorage.c)
|
||||
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getdata.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/getdata/getdata.c)
|
||||
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/pemapper/pemapper.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/pemapper/pemapper.c)
|
||||
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/placer/placer.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/placer/placer.c)
|
||||
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/optheaders.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/pagedescriptors.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/populateheaders.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/optheaders.c)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/pagedescriptors.c)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/setdata/populateheaders.c)
|
||||
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/headerhash.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/writexex.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/headerhash.c)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/src/write/writexex.c)
|
||||
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/getopt_port/getopt.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/getopt_port/getopt.c)
|
||||
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/macros.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/nettle-types.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/nettle-write.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/sha1.h)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/sha1.c)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/sha1-compress.c)
|
||||
list(APPEND allsources ${CMAKE_SOURCE_DIR}/include/nettle/write-be32.c)
|
||||
|
||||
# Setting compilation settings...
|
||||
add_executable(synthxex ${allsources})
|
||||
target_include_directories(synthxex PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
# Debug/release build handling
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
set(SYNTHXEX_BUILD_TYPE "MultiConf") # Multi-config generators handle debug build options themselves, don't apply ours
|
||||
else()
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
set(SYNTHXEX_BUILD_TYPE ${CMAKE_BUILD_TYPE})
|
||||
else()
|
||||
set(SYNTHXEX_BUILD_TYPE "Debug")
|
||||
endif()
|
||||
endif()
|
||||
# If we're doing a debug build, compile with debugging and git commit hash
|
||||
if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Release"))
|
||||
|
||||
if(${SYNTHXEX_BUILD_TYPE} MATCHES "Deb")
|
||||
add_compile_definitions(_DEBUG=1)
|
||||
|
||||
if(NOT MINGW)
|
||||
target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address -fsanitize=undefined)
|
||||
target_link_options(synthxex PRIVATE -lasan -lubsan -fsanitize=address -fsanitize=undefined)
|
||||
# MinGW doesn't support ASAN
|
||||
if(NOT (MINGW))
|
||||
target_compile_options(synthxex PRIVATE -O0 -g -fsanitize=address)
|
||||
target_link_options(synthxex PRIVATE -fsanitize=address)
|
||||
else()
|
||||
target_compile_options(synthxex PRIVATE -O0 -g)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set the version
|
||||
execute_process(
|
||||
COMMAND git describe --tags --dirty
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE VERSION_STRING
|
||||
OUTPUT_VARIABLE GIT_COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# Only use the result from the git command if it's a valid version string
|
||||
if(${VERSION_STRING} MATCHES "^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+-g[0-9a-f]+(-dirty)?)?$")
|
||||
add_compile_definitions(SYNTHXEX_VERSION="${VERSION_STRING}")
|
||||
else()
|
||||
add_compile_definitions(SYNTHXEX_VERSION="v0.0.5") # Only used as a fallback
|
||||
add_compile_definitions(GIT_COMMIT="${GIT_COMMIT}")
|
||||
endif()
|
||||
|
||||
# Setting install target settings...
|
||||
|
|
124
CONTRIBUTING.md
124
CONTRIBUTING.md
|
@ -1,124 +0,0 @@
|
|||
# Contributing to SynthXEX
|
||||
|
||||
## Bug reports
|
||||
Please direct any bug reports to either the [main repository](https://git.aidenisik.scot/FreeChainXenon/SynthXEX/issues), or the [GitHub mirror](https://github.com/FreeChainXenon/SynthXEX/issues).
|
||||
|
||||
## Patches
|
||||
To contribute code, first ensure you have read and adhered to the [code style](#code-style) section.
|
||||
|
||||
There are two ways to contribute code patches:
|
||||
|
||||
- Pull requests, which you can submit either to the [main repository](https://git.aidenisik.scot/FreeChainXenon/SynthXEX/pulls), or the [GitHub mirror](https://github.com/FreeChainXenon/SynthXEX/pulls).
|
||||
|
||||
- Patch files, generated by Git, can be sent to \<aidenisik+git@member.fsf.org\>, which I will review and possibly merge.
|
||||
|
||||
## Code Style
|
||||
|
||||
If you are using [AStyle](https://astyle.sourceforge.net/), the following command will format the code correctly (run from the project root):
|
||||
```
|
||||
astyle --style=allman --add-braces --break-blocks --indent-labels --indent-switches --unpad-paren --align-pointer=name --pad-oper --indent-preproc-block --add-one-line-braces --squeeze-ws --squeeze-lines=1 --recursive "./src/*.c" "./src/*.h"
|
||||
```
|
||||
|
||||
The code style used in SynthXEX is the [Allman/BSD style as specified by AStyle](https://astyle.sourceforge.net/astyle.html#_style=allman), with the following extra specifications:
|
||||
|
||||
- C++-style comments are used, rather than multiline/C89-style comments, e.g:
|
||||
```
|
||||
// This is a comment
|
||||
// This is another comment
|
||||
// This is yet another comment
|
||||
```
|
||||
|
||||
- All conditional statements utilise curly brackets, even when there is only one line within them, e.g:
|
||||
```
|
||||
if(condition)
|
||||
{
|
||||
printf("Hello!\n");
|
||||
}
|
||||
```
|
||||
|
||||
- It is acceptable to have the curly brackets on the same line as the code if it is short, e.g:
|
||||
```
|
||||
if(condition) { printf("Hello!\n"); }
|
||||
|
||||
if(condition)
|
||||
{ printf("Hello!\n"); }
|
||||
```
|
||||
|
||||
- All brackets immediately succeed/precede the name/keyword they correspond to with no spaces, e.g:
|
||||
```
|
||||
foo(bar);
|
||||
(float)(a + b);
|
||||
```
|
||||
|
||||
- All code blocks have one empty line between the start/end and any other code, e.g.:
|
||||
```
|
||||
int a = 1;
|
||||
|
||||
if(a == 2)
|
||||
{
|
||||
printf("Hi!\n");
|
||||
}
|
||||
|
||||
int b = 2;
|
||||
```
|
||||
|
||||
- Labels are always 1 indentation level below the current level, e.g:
|
||||
```
|
||||
if(1)
|
||||
{
|
||||
if(2)
|
||||
{
|
||||
int a = 1;
|
||||
label1:
|
||||
int b = 2;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Case statements are always 1 indentation level above their corresponding switch, e.g.:
|
||||
```
|
||||
switch(val)
|
||||
{
|
||||
case 1:
|
||||
printf("1\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("0\n");
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
- Pointer operators are always attached to variables, e.g:
|
||||
```
|
||||
char *string = NULL;
|
||||
int a = 0;
|
||||
setA(&a);
|
||||
```
|
||||
|
||||
- Operators are always padded to be one space away from their operands, e.g:
|
||||
```
|
||||
int a = 1 + 2;
|
||||
```
|
||||
|
||||
- Commas in function calls, etc, always have one space after them, e.g:
|
||||
```
|
||||
myFunc(1, 2);
|
||||
```
|
||||
|
||||
- Preprocessor macros outside functions are indented, just like regular code, e.g:
|
||||
```
|
||||
#ifdef MACRO_ONE
|
||||
#define MACRO 1
|
||||
#else
|
||||
#define MACRO 0
|
||||
#endif
|
||||
```
|
||||
|
||||
- There should never be more than one blank line between code, e.g:
|
||||
```
|
||||
int a;
|
||||
int b;
|
||||
|
||||
int c;
|
||||
```
|
106
README.md
106
README.md
|
@ -10,17 +10,20 @@ SynthXEX is the XEX(2) builder (and thus the last step in building an executable
|
|||
|
||||
This was developed by referencing public knowledge on the XEX(2) file format, along with staring at XEX files in a hex editor to decypher aspects which do not seem to be documented anywhere. No Microsoft code was decompiled to develop SynthXEX, and I ask that any contributors do not do so either.
|
||||
|
||||
This is in early development and MANY features are missing (most notable exports). No guarantees are made as to the functionality of the program, or it's stability. Functionality may change at any time.
|
||||
This is in early development and MANY features are missing (most notable imports/exports). No guarantees are made as to the functionality of the program, or it's stability. Functionality may change at any time.
|
||||
|
||||
This *should* support any POSIX-compliant operating system, along with Windows.
|
||||
|
||||
## Installing
|
||||
|
||||
## Building (POSIX, standard method)
|
||||
SynthXEX is part of the FreeChainXenon toolchain. It's installer is located [here](https://git.aidenisik.scot/FreeChainXenon/FCX-Installer).
|
||||
|
||||
## Building
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- A C compiler (GCC/Clang)
|
||||
- CMake (>= 3.24.2)
|
||||
- CMake (>= 3.25.1)
|
||||
- Make
|
||||
- Git
|
||||
|
||||
|
@ -28,7 +31,7 @@ To install these on Debian: ```sudo apt install gcc cmake make git```
|
|||
|
||||
### Downloading
|
||||
|
||||
Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX```
|
||||
Run: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX```
|
||||
|
||||
### Compiling
|
||||
|
||||
|
@ -42,102 +45,15 @@ Build: ```make```
|
|||
|
||||
Install: ```sudo make install```
|
||||
|
||||
|
||||
## Building (Guix)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Guix
|
||||
- Git
|
||||
|
||||
### Compiling
|
||||
|
||||
Choose ONE of the options below.
|
||||
|
||||
#### Compiling & Installing
|
||||
|
||||
Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX```
|
||||
|
||||
Switch to the newly cloned directory: ```cd SynthXEX```
|
||||
|
||||
Install: ```guix time-machine -C channels.scm -- package -f synthxex.scm```
|
||||
|
||||
#### Compiling Only
|
||||
|
||||
Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX```
|
||||
|
||||
Switch to the newly cloned directory: ```cd SynthXEX```
|
||||
|
||||
Build: ```guix time-machine -C channels.scm -- build -f synthxex.scm```
|
||||
|
||||
#### Compiling & Installing to Config
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
## Building (Nix)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Nix
|
||||
- Git
|
||||
|
||||
### Compiling
|
||||
|
||||
Choose ONE of the options below.
|
||||
|
||||
#### Compiling & Installing
|
||||
|
||||
Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX```
|
||||
|
||||
Switch to the newly cloned directory: ```cd SynthXEX```
|
||||
|
||||
Run: ```nix profile install .#synthxex```
|
||||
|
||||
#### Compiling Only
|
||||
|
||||
Clone: ```git clone https://git.aidenisik.scot/FreeChainXenon/SynthXEX```
|
||||
|
||||
Switch to the newly cloned directory: ```cd SynthXEX```
|
||||
|
||||
Run: ```nix build .#synthxex```
|
||||
|
||||
#### Compiling & Installing to Config
|
||||
|
||||
Add SynthXEX to your configuration.nix file:
|
||||
|
||||
```
|
||||
{ config, modulesPath, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
synthxex = builtins.getFlake "git+https://git.aidenisik.scot/FreeChainXenon/SynthXEX";
|
||||
synthxexPkgs = synthxex.packages.${pkgs.hostPlatform.system};
|
||||
in {
|
||||
# ...
|
||||
environment.systemPackages = with pkgs; [
|
||||
# ...
|
||||
synthxexPkgs.synthxex
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Special thanks to:
|
||||
- [InvoxiPlayGames](https://github.com/InvoxiPlayGames), for help understanding the XEX format
|
||||
- [emoose](https://github.com/emoose), for xex1tool, which was very useful for taking apart XEX files and getting info from them
|
||||
- [Free60Project](https://github.com/Free60Project), for documentation on the XEX format, which was useful in the early days
|
||||
- [Vali0004](https://github.com/Vali0004), for helping clean up and harden code, along with Nix packaging
|
||||
- Several members of the Xbox360Hub Discord server, for a multitude of reasons
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
||||
- Several other members of the Xbox360Hub Discord server, for a multitude of reasons
|
||||
|
||||
## Licensing
|
||||
|
||||
### SynthXEX
|
||||
### SynthXEX (src/*, CMakeLists.txt)
|
||||
|
||||
Copyright (c) 2024-25 Aiden Isik
|
||||
|
||||
|
@ -154,7 +70,7 @@ GNU Affero General Public License for more details.
|
|||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
### getopt_port
|
||||
### getopt_port (include/getopt_port/*)
|
||||
|
||||
Copyright (c) 2012-2023, Kim Grasman <kim.grasman@gmail.com>
|
||||
|
||||
|
@ -182,7 +98,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
### GNU Nettle
|
||||
### GNU Nettle (include/nettle/*)
|
||||
|
||||
Copyright (C) 2001, 2013 Niels Möller
|
||||
|
||||
|
|
14
channels.scm
14
channels.scm
|
@ -1,14 +0,0 @@
|
|||
;;; Lock the Guix repository to a specific commit.
|
||||
;;; For reproducibility purposes in building SynthXEX.
|
||||
|
||||
(list (channel
|
||||
(name 'guix)
|
||||
(url "https://codeberg.org/guix/guix.git")
|
||||
(branch "master")
|
||||
(commit
|
||||
"95f8c22bbe9cf25ce492ef520f94a34f7ec3d160")
|
||||
(introduction
|
||||
(make-channel-introduction
|
||||
"9edb3f66fd807b096b48283debdcddccfea34bad"
|
||||
(openpgp-fingerprint
|
||||
"BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))))
|
60
flake.lock
generated
60
flake.lock
generated
|
@ -1,60 +0,0 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1750032973,
|
||||
"narHash": "sha256-UqiBHZueCKKB8oQrVFooBA0DHh8AUVwhqiLSdhSXyYA=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5a0be37d950a180a4853647674c89ec286e538c1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"utils": "utils"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
35
flake.nix
35
flake.nix
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
inputs = {
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
# to update: nix flake update nixpkgs
|
||||
nixpkgs.url = "github:nixos/nixpkgs";
|
||||
};
|
||||
outputs = { self, utils, nixpkgs }:
|
||||
(utils.lib.eachSystem [ "x86_64-linux" "ppc64" "ppc32" ] (system:
|
||||
let
|
||||
pkgsLut = {
|
||||
x86_64-linux = nixpkgs.legacyPackages.${system}.extend self.overlay;
|
||||
ppc32 = import nixpkgs {
|
||||
crossSystem.config = "powerpc-none-eabi";
|
||||
system = "x86_64-linux";
|
||||
overlays = [ self.overlay ];
|
||||
};
|
||||
ppc64 = import nixpkgs {
|
||||
crossSystem.config = "powerpc64-unknown-linux-gnuabielfv2";
|
||||
system = "x86_64-linux";
|
||||
overlays = [ self.overlay ];
|
||||
config.allowUnsupportedSystem = true;
|
||||
};
|
||||
};
|
||||
pkgs = pkgsLut.${system};
|
||||
in {
|
||||
packages = {
|
||||
inherit (pkgs) synthxex;
|
||||
};
|
||||
devShell = pkgs.synthxex;
|
||||
})) // {
|
||||
overlay = self: super: {
|
||||
synthxex = self.callPackage ./synthxex.nix {};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -19,28 +19,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Forbid the use of the free function
|
||||
#define freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse free
|
||||
#pragma GCC poison free
|
||||
// Program identifiers
|
||||
#define NAME "SynthXEX"
|
||||
#define VERSION "v0.0.3"
|
||||
#define COPYRIGHT "2024-25"
|
||||
|
||||
// Program identifiers (version set in CMakeLists.txt)
|
||||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
#define SYNTHXEX_NAME "SynthXEX-Debug"
|
||||
#ifdef GIT_COMMIT
|
||||
#define VERSION_STRING NAME " " VERSION "-dev-" GIT_COMMIT
|
||||
#else
|
||||
#define SYNTHXEX_NAME "SynthXEX"
|
||||
#define VERSION_STRING NAME " " VERSION
|
||||
#endif
|
||||
|
||||
#define SYNTHXEX_COPYRIGHT "2024-2025"
|
||||
#define SYNTHXEX_VERSION_STRING SYNTHXEX_NAME " " SYNTHXEX_VERSION
|
||||
|
||||
// Print constants
|
||||
#define SYNTHXEX_PRINT_STEM SYNTHXEX_NAME ">"
|
||||
#define PRINT_STEM "SynthXEX>"
|
||||
|
||||
// Return values
|
||||
#define SUCCESS 0
|
||||
|
@ -49,9 +45,7 @@
|
|||
#define ERR_MISSING_SECTION_FLAG -2
|
||||
#define ERR_FILE_OPEN -3
|
||||
#define ERR_FILE_READ -4
|
||||
#define ERR_FILE_WRITE -5
|
||||
#define ERR_OUT_OF_MEM -6
|
||||
#define ERR_UNSUPPORTED_STRUCTURE -7
|
||||
#define ERR_INVALID_RVA_OR_OFFSET -8
|
||||
#define ERR_INVALID_IMPORT_NAME -9
|
||||
#define ERR_DATA_OVERFLOW -10
|
||||
#define ERR_OUT_OF_MEM -5
|
||||
#define ERR_UNSUPPORTED_STRUCTURE -6
|
||||
|
||||
void infoPrint(char *str);
|
||||
|
|
|
@ -18,148 +18,6 @@
|
|||
|
||||
#include "datastorage.h"
|
||||
|
||||
// Frees memory and sets the pointer to NULL
|
||||
// If the pointer is currently NULL, do nothing (avoid double-free)
|
||||
// Disable pointer type checking here to make this easier to use
|
||||
void nullAndFree(void **ptr)
|
||||
{
|
||||
if(ptr != NULL)
|
||||
{
|
||||
if(*ptr != NULL)
|
||||
{
|
||||
freeOnlyUseThisFunctionInTheNullAndFreeFunctionNowhereElse(*ptr);
|
||||
*ptr = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These functions together handle freeing all of the main structs.
|
||||
// They can also be called individually, and it doesn't matter if you call them twice.
|
||||
// Each function checks if the struct going to be accessed is NULL, so it doesn't try
|
||||
// to access unallocated data.
|
||||
//
|
||||
// DO NOT CALL THESE OUTWITH MAIN. Those which take double pointers will replace
|
||||
// the pointer given to them with null ONLY in the function they're called in, not it's callers
|
||||
// (including main), which WILL result in a double free.
|
||||
//
|
||||
// Even if it doesn't access data right now, it's still there so it's not forgotten
|
||||
// about if it becomes necessary with future additions.
|
||||
void freeOffsetsStruct(struct offsets **offsets)
|
||||
{
|
||||
if(offsets != NULL)
|
||||
{
|
||||
if(*offsets != NULL)
|
||||
{
|
||||
nullAndFree((void **) & ((*offsets)->optHeaders));
|
||||
nullAndFree((void **)offsets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void freeXexHeaderStruct(struct xexHeader **xexHeader)
|
||||
{
|
||||
if(xexHeader != NULL)
|
||||
{
|
||||
if(*xexHeader != NULL)
|
||||
{ nullAndFree((void **)xexHeader); }
|
||||
}
|
||||
}
|
||||
|
||||
void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader)
|
||||
{
|
||||
if(secInfoHeader != NULL)
|
||||
{
|
||||
if(*secInfoHeader != NULL)
|
||||
{
|
||||
struct pageDescriptor *descriptors = (*secInfoHeader)->descriptors; // To avoid dereferencing an unaligned pointer
|
||||
nullAndFree((void **)&descriptors);
|
||||
//(*secInfoHeader)->descriptors = descriptors; // Not required in this case as secInfoHeader is freed anyways
|
||||
nullAndFree((void **)secInfoHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void freeSectionsStruct(struct sections *sections)
|
||||
{
|
||||
nullAndFree((void **) & (sections->section));
|
||||
}
|
||||
|
||||
void freePeImportInfoStruct(struct peImportInfo *peImportInfo)
|
||||
{
|
||||
if(peImportInfo->tables != NULL)
|
||||
{
|
||||
// Free the imports within each table first, then the tables,
|
||||
// otherwise we'll have a memory leak
|
||||
for(uint32_t i = 0; i < peImportInfo->tableCount; i++)
|
||||
{
|
||||
nullAndFree((void **) & (peImportInfo->tables[i].name));
|
||||
nullAndFree((void **) & (peImportInfo->tables[i].imports));
|
||||
}
|
||||
|
||||
nullAndFree((void **) & (peImportInfo->tables));
|
||||
}
|
||||
}
|
||||
|
||||
void freePeDataStruct(struct peData **peData)
|
||||
{
|
||||
if(*peData != NULL)
|
||||
{
|
||||
freeSectionsStruct(&((*peData)->sections));
|
||||
freePeImportInfoStruct(&((*peData)->peImportInfo));
|
||||
nullAndFree((void **)peData);
|
||||
}
|
||||
}
|
||||
|
||||
void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries)
|
||||
{
|
||||
if(*optHeaderEntries != NULL)
|
||||
{
|
||||
nullAndFree((void **) & ((*optHeaderEntries)->optHeaderEntry));
|
||||
nullAndFree((void **)optHeaderEntries);
|
||||
}
|
||||
}
|
||||
|
||||
void freeImportLibrariesStruct(struct importLibraries *importLibraries)
|
||||
{
|
||||
struct importTable *importTables = importLibraries->importTables;
|
||||
char *nameTable = importLibraries->nameTable; // Use these to avoid dereferencing unaligned pointers
|
||||
|
||||
nullAndFree((void **)&nameTable);
|
||||
|
||||
if(importTables != NULL)
|
||||
{
|
||||
for(uint32_t i = 0; i < importLibraries->tableCount; i++)
|
||||
{
|
||||
uint32_t *addresses = importTables[i].addresses; // Avoid dereferencing unaligned pointer
|
||||
nullAndFree((void **)&addresses);
|
||||
importTables[i].addresses = addresses;
|
||||
}
|
||||
|
||||
nullAndFree((void **)&importTables);
|
||||
importLibraries->importTables = importTables;
|
||||
}
|
||||
}
|
||||
|
||||
void freeOptHeadersStruct(struct optHeaders **optHeaders)
|
||||
{
|
||||
if(*optHeaders != NULL)
|
||||
{
|
||||
freeImportLibrariesStruct(&((*optHeaders)->importLibraries));
|
||||
nullAndFree((void **)optHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, struct secInfoHeader **secInfoHeader,
|
||||
struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders)
|
||||
{
|
||||
freeOffsetsStruct(offsets);
|
||||
freeXexHeaderStruct(xexHeader);
|
||||
freeSecInfoHeaderStruct(secInfoHeader);
|
||||
freePeDataStruct(peData);
|
||||
freeOptHeaderEntriesStruct(optHeaderEntries);
|
||||
freeOptHeadersStruct(optHeaders);
|
||||
}
|
||||
|
||||
uint32_t getNextAligned(uint32_t offset, uint32_t alignment)
|
||||
{
|
||||
if(offset % alignment) // If offset not aligned
|
||||
|
@ -170,108 +28,84 @@ uint32_t getNextAligned(uint32_t offset, uint32_t alignment)
|
|||
return offset; // Offset already aligned
|
||||
}
|
||||
|
||||
uint32_t rvaToOffset(uint32_t rva, struct sections *sections)
|
||||
{
|
||||
if((sections->count > 0) && (rva >= sections->section[sections->count - 1].rva + sections->section[sections->count - 1].virtualSize))
|
||||
{
|
||||
return 0; // Not found (beyond end of PE)
|
||||
}
|
||||
|
||||
for(int32_t i = sections->count - 1; i >= 0; i--)
|
||||
{
|
||||
if(rva >= sections->section[i].rva)
|
||||
{ return (rva - sections->section[i].rva) + sections->section[i].offset; }
|
||||
}
|
||||
|
||||
return 0; // Not found
|
||||
}
|
||||
|
||||
uint32_t offsetToRVA(uint32_t offset, struct sections *sections)
|
||||
{
|
||||
if((sections->count > 0)
|
||||
&& (offset >= sections->section[sections->count - 1].offset + sections->section[sections->count - 1].rawSize))
|
||||
{
|
||||
return 0; // Not found (beyond end of PE)
|
||||
}
|
||||
|
||||
for(int32_t i = sections->count - 1; i >= 0; i--)
|
||||
{
|
||||
if(offset >= sections->section[i].offset)
|
||||
{ return (offset - sections->section[i].offset) + sections->section[i].rva; }
|
||||
}
|
||||
|
||||
return 0; // Not found
|
||||
}
|
||||
// TODO: Combine all of these into a single function
|
||||
|
||||
uint32_t get32BitFromPE(FILE *pe)
|
||||
{
|
||||
uint32_t result;
|
||||
errno = SUCCESS;
|
||||
uint8_t store[4];
|
||||
fread(store, sizeof(uint8_t), 4, pe);
|
||||
|
||||
if(fread(&result, sizeof(uint32_t), 1, pe) != 1)
|
||||
uint32_t result = 0;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
errno = ERR_FILE_READ;
|
||||
return 0;
|
||||
result |= store[i] << i * 8;
|
||||
}
|
||||
|
||||
// If system is big endian, swap endianness (PE is LE)
|
||||
#ifdef BIG_ENDIAN_SYSTEM
|
||||
return __builtin_bswap32(result);
|
||||
#else
|
||||
return result;
|
||||
result = __builtin_bswap32(result);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t get16BitFromPE(FILE *pe)
|
||||
{
|
||||
uint16_t result;
|
||||
errno = SUCCESS;
|
||||
uint8_t store[2];
|
||||
fread(store, sizeof(uint8_t), 2, pe);
|
||||
|
||||
if(fread(&result, sizeof(uint16_t), 1, pe) != 1)
|
||||
uint16_t result = 0;
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
errno = ERR_FILE_READ;
|
||||
return 0;
|
||||
result |= store[i] << i * 8;
|
||||
}
|
||||
|
||||
// If system is big endian, swap endianness (PE is LE)
|
||||
#ifdef BIG_ENDIAN_SYSTEM
|
||||
return __builtin_bswap16(result);
|
||||
#else
|
||||
return result;
|
||||
result = htons(result);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t get32BitFromXEX(FILE *xex)
|
||||
{
|
||||
uint32_t result;
|
||||
errno = SUCCESS;
|
||||
uint8_t store[4];
|
||||
fread(store, sizeof(uint8_t), 4, xex);
|
||||
|
||||
if(fread(&result, sizeof(uint32_t), 1, xex) != 1)
|
||||
uint32_t result = 0;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
errno = ERR_FILE_READ;
|
||||
return 0;
|
||||
result |= store[i] << i * 8;
|
||||
}
|
||||
|
||||
// If system and file endianness don't match we need to change it
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
return __builtin_bswap32(result);
|
||||
#else
|
||||
return result;
|
||||
result = __builtin_bswap32(result);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t get16BitFromXEX(FILE *xex)
|
||||
{
|
||||
uint16_t result;
|
||||
errno = SUCCESS;
|
||||
uint8_t store[2];
|
||||
fread(store, sizeof(uint8_t), 2, xex);
|
||||
|
||||
if(fread(&result, sizeof(uint16_t), 1, xex) != 1)
|
||||
uint32_t result = 0;
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
errno = ERR_FILE_READ;
|
||||
return 0;
|
||||
result |= store[i] << i * 8;
|
||||
}
|
||||
|
||||
// If system and file endianness don't match we need to change it
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
return __builtin_bswap16(result);
|
||||
#else
|
||||
return result;
|
||||
result = __builtin_bswap16(result);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -67,9 +67,6 @@
|
|||
#define PE_SECTION_FLAG_READ 0x40000000
|
||||
#define PE_SECTION_FLAG_WRITE 0x80000000
|
||||
|
||||
// PE import ordinal flag
|
||||
#define PE_IMPORT_ORDINAL_FLAG 0x80000000
|
||||
|
||||
// RWX flags (XEX)
|
||||
#define XEX_SECTION_CODE 0x1
|
||||
#define XEX_SECTION_RWDATA 0x2
|
||||
|
@ -79,37 +76,18 @@
|
|||
struct sections
|
||||
{
|
||||
uint16_t count;
|
||||
struct section *section;
|
||||
struct sectionPerms *sectionPerms;
|
||||
};
|
||||
|
||||
struct section
|
||||
struct sectionPerms
|
||||
{
|
||||
uint8_t permFlag;
|
||||
uint32_t virtualSize;
|
||||
uint32_t rva;
|
||||
uint32_t rawSize;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct peImport
|
||||
{
|
||||
uint32_t iatAddr;
|
||||
};
|
||||
|
||||
struct peImportTable
|
||||
{
|
||||
char *name;
|
||||
uint32_t rva;
|
||||
uint32_t importCount;
|
||||
struct peImport *imports;
|
||||
};
|
||||
|
||||
struct peImportInfo
|
||||
{
|
||||
uint32_t idtRVA;
|
||||
uint32_t tableCount;
|
||||
uint32_t totalImportCount;
|
||||
struct peImportTable *tables;
|
||||
uint16_t count;
|
||||
};
|
||||
|
||||
struct peExportInfo
|
||||
|
@ -195,26 +173,10 @@ struct __attribute__((packed)) basefileFormat
|
|||
uint32_t zeroSize;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) importTable
|
||||
{
|
||||
uint32_t size;
|
||||
uint8_t sha1[0x14];
|
||||
uint32_t unknown; // Unique to each executable imported from, and always the same
|
||||
uint32_t targetVer;
|
||||
uint32_t minimumVer;
|
||||
uint8_t padding;
|
||||
uint8_t tableIndex;
|
||||
uint16_t addressCount;
|
||||
uint32_t *addresses; // IAT entry address followed by branch stub code address for functions, just IAT address for other symbols
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) importLibraries
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t nameTableSize;
|
||||
uint32_t tableCount;
|
||||
char *nameTable;
|
||||
struct importTable *importTables;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) tlsInfo
|
||||
|
@ -244,35 +206,11 @@ struct optHeaders
|
|||
struct tlsInfo tlsInfo;
|
||||
};
|
||||
|
||||
// Functions used for easier memory management
|
||||
|
||||
// Frees memory and sets the pointer to NULL
|
||||
// If the pointer is currently NULL, do nothing (avoid double-free)
|
||||
void nullAndFree(void **ptr);
|
||||
|
||||
// These functions together handle freeing all of the main structs.
|
||||
// They can also be called individually, and it doesn't matter if you call them twice.
|
||||
// DO NOT CALL THESE OUTWITH MAIN. Those which take double pointers will replace
|
||||
// the pointer given to them with null ONLY in the function they're called in, not it's callers
|
||||
// (including main), which WILL result in a double free.
|
||||
void freeOffsetsStruct(struct offsets **offsets);
|
||||
void freeXexHeaderStruct(struct xexHeader **xexHeader);
|
||||
void freeSecInfoHeaderStruct(struct secInfoHeader **secInfoHeader);
|
||||
void freeSectionsStruct(struct sections *sections);
|
||||
void freePeImportInfoStruct(struct peImportInfo *peImportInfo);
|
||||
void freePeDataStruct(struct peData **peData);
|
||||
void freeOptHeaderEntriesStruct(struct optHeaderEntries **optHeaderEntries);
|
||||
void freeOptHeadersStruct(struct optHeaders **optHeaders);
|
||||
void freeAllMainStructs(struct offsets **offsets, struct xexHeader **xexHeader, struct secInfoHeader **secInfoHeader,
|
||||
struct peData **peData, struct optHeaderEntries **optHeaderEntries, struct optHeaders **optHeaders);
|
||||
|
||||
// Functions used for file data manipulation
|
||||
|
||||
uint32_t getNextAligned(uint32_t offset, uint32_t alignment);
|
||||
|
||||
uint32_t rvaToOffset(uint32_t rva, struct sections *sections);
|
||||
uint32_t offsetToRVA(uint32_t offset, struct sections *sections);
|
||||
|
||||
// TODO: combine these into a single function
|
||||
uint32_t get32BitFromPE(FILE *pe);
|
||||
uint16_t get16BitFromPE(FILE *pe);
|
||||
uint32_t get32BitFromXEX(FILE *xex);
|
||||
|
|
232
src/getdata/getdata.c
Normal file
232
src/getdata/getdata.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// Copyright (c) 2024-25 Aiden Isik
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "getdata.h"
|
||||
|
||||
// Validate PE. This isn't thorough, but it's enough to catch any non-PE/360 files.
|
||||
// I was considering merging this with getHdrData and mapPEToBasefile as we're
|
||||
// basically reading the same data twice, but I think it's beneficial to have a
|
||||
// dedicated place where we validate the input.
|
||||
bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false
|
||||
{
|
||||
// Check if we have at least the size of a DOS header, so we don't overrun the PE
|
||||
fseek(pe, 0, SEEK_END);
|
||||
size_t finalOffset = ftell(pe);
|
||||
|
||||
if(finalOffset < 0x3C + 0x4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check magic
|
||||
fseek(pe, 0, SEEK_SET);
|
||||
uint16_t magic = get16BitFromPE(pe);
|
||||
|
||||
if(magic != 0x5A4D) // PE magic
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if pointer to PE header is valid
|
||||
fseek(pe, 0x3C, SEEK_SET);
|
||||
size_t peHeaderOffset = get32BitFromPE(pe);
|
||||
|
||||
if(finalOffset < peHeaderOffset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the file is big enough to get size of optional header, and therefore size of whole PE header
|
||||
if(finalOffset < 0x14 + 0x2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check section count
|
||||
fseek(pe, peHeaderOffset + 0x6, SEEK_SET);
|
||||
uint16_t sectionCount = get16BitFromPE(pe);
|
||||
|
||||
if(sectionCount == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the file is large enough to contain the whole PE header
|
||||
fseek(pe, peHeaderOffset + 0x14, SEEK_SET);
|
||||
uint16_t sizeOfOptHdr = get16BitFromPE(pe);
|
||||
|
||||
// 0x18 == size of COFF header, 0x28 == size of one entry in section table
|
||||
if(finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check machine ID
|
||||
fseek(pe, peHeaderOffset + 0x4, SEEK_SET);
|
||||
uint16_t machineID = get16BitFromPE(pe);
|
||||
|
||||
if(machineID != 0x1F2 && !skipMachineCheck) // 0x1F2 == POWERPCBE
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check subsystem
|
||||
fseek(pe, peHeaderOffset + 0x5C, SEEK_SET);
|
||||
uint16_t subsystem = get16BitFromPE(pe);
|
||||
|
||||
if(subsystem != 0xE) // 0xE == XBOX
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check page size/alignment
|
||||
fseek(pe, peHeaderOffset + 0x38, SEEK_SET);
|
||||
uint32_t pageSize = get32BitFromPE(pe);
|
||||
|
||||
if(pageSize != 0x1000 && pageSize != 0x10000) // 4KiB and 64KiB are the only valid sizes
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check each raw offset + raw size in section table
|
||||
fseek(pe, peHeaderOffset + 0x18 + sizeOfOptHdr + 0x10, SEEK_SET); // 0x10 == raw offset in entry
|
||||
|
||||
for(uint16_t i = 0; i < sectionCount; i++)
|
||||
{
|
||||
// If raw size + raw offset exceeds file size, PE is invalid
|
||||
if(finalOffset < get32BitFromPE(pe) + get32BitFromPE(pe))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(pe, 0x20, SEEK_CUR); // Next entry
|
||||
}
|
||||
|
||||
return true; // Checked enough, this is an Xbox 360 PE file
|
||||
}
|
||||
|
||||
int getSectionRwxFlags(FILE *pe, struct sections *sections)
|
||||
{
|
||||
fseek(pe, 0x3C, SEEK_SET);
|
||||
uint32_t peOffset = get32BitFromPE(pe);
|
||||
|
||||
fseek(pe, peOffset + 0x6, SEEK_SET); // 0x6 == section count
|
||||
sections->count = get16BitFromPE(pe);
|
||||
|
||||
sections->sectionPerms = calloc(sections->count, sizeof(struct sectionPerms)); // free() is called for this in setdata
|
||||
if(sections->sectionPerms == NULL) {return ERR_OUT_OF_MEM;}
|
||||
fseek(pe, peOffset + 0xF8, SEEK_SET); // 0xF8 == beginning of section table
|
||||
|
||||
for(uint16_t i = 0; i < sections->count; i++)
|
||||
{
|
||||
fseek(pe, 0xC, SEEK_CUR); // Seek to RVA of section
|
||||
sections->sectionPerms[i].rva = get32BitFromPE(pe);
|
||||
|
||||
fseek(pe, 0x14, SEEK_CUR); // Now progress to characteristics, where we will check flags
|
||||
uint32_t characteristics = get32BitFromPE(pe);
|
||||
|
||||
if(characteristics & PE_SECTION_FLAG_EXECUTE)
|
||||
{
|
||||
sections->sectionPerms[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1
|
||||
}
|
||||
else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE)
|
||||
{
|
||||
sections->sectionPerms[i].permFlag = XEX_SECTION_RWDATA | 0b10000;
|
||||
}
|
||||
else if(characteristics & PE_SECTION_FLAG_READ)
|
||||
{
|
||||
sections->sectionPerms[i].permFlag = XEX_SECTION_RODATA | 0b10000;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ERR_MISSING_SECTION_FLAG;
|
||||
}
|
||||
|
||||
// Don't need to progress any more to get to beginning of next entry, as characteristics is last field
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int getHdrData(FILE *pe, struct peData *peData, uint8_t flags)
|
||||
{
|
||||
// No flags supported at this time (will be used for getting additional info, for e.g. other optional headers)
|
||||
if(flags)
|
||||
{
|
||||
return ERR_UNKNOWN_DATA_REQUEST;
|
||||
}
|
||||
|
||||
// Get header data required for ANY XEX
|
||||
// PE size
|
||||
fseek(pe, 0, SEEK_SET); // If we don't do this, the size returned is wrong (?)
|
||||
struct stat peStat;
|
||||
fstat(fileno(pe), &peStat);
|
||||
peData->size = peStat.st_size;
|
||||
|
||||
// Getting PE header offset before we go any further..
|
||||
fseek(pe, 0x3C, SEEK_SET);
|
||||
peData->peHeaderOffset = get32BitFromPE(pe);
|
||||
|
||||
// Number of sections
|
||||
fseek(pe, peData->peHeaderOffset + 0x6, SEEK_SET);
|
||||
peData->numberOfSections = get16BitFromPE(pe);
|
||||
|
||||
// Size of section table
|
||||
peData->sectionTableSize = peData->numberOfSections * 0x28;
|
||||
|
||||
// Size of header
|
||||
// 0x18 == size of COFF header, get16BitFromPE value == size of optional header
|
||||
fseek(pe, peData->peHeaderOffset + 0x14, SEEK_SET);
|
||||
peData->headerSize = (peData->peHeaderOffset + 1) + 0x18 + get16BitFromPE(pe);
|
||||
|
||||
// PE characteristics
|
||||
fseek(pe, peData->peHeaderOffset + 0x16, SEEK_SET);
|
||||
peData->characteristics = get16BitFromPE(pe);
|
||||
|
||||
// Entry point (RVA)
|
||||
fseek(pe, peData->peHeaderOffset + 0x28, SEEK_SET);
|
||||
peData->entryPoint = get32BitFromPE(pe);
|
||||
|
||||
// Base address
|
||||
fseek(pe, peData->peHeaderOffset + 0x34, SEEK_SET);
|
||||
peData->baseAddr = get32BitFromPE(pe);
|
||||
|
||||
// Page alignment/size
|
||||
fseek(pe, peData->peHeaderOffset + 0x38, SEEK_SET);
|
||||
peData->pageSize = get32BitFromPE(pe);
|
||||
|
||||
// Export tables
|
||||
fseek(pe, peData->peHeaderOffset + 0x78, SEEK_SET);
|
||||
peData->peExportInfo.count = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data
|
||||
|
||||
// Import tables
|
||||
fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET);
|
||||
peData->peImportInfo.count = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data
|
||||
|
||||
// TLS status (PE TLS is currently UNSUPPORTED, so if we find it, we'll need to abort)
|
||||
fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET);
|
||||
peData->tlsAddr = get32BitFromPE(pe);
|
||||
peData->tlsSize = get32BitFromPE(pe);
|
||||
if(peData->tlsAddr != 0 || peData->tlsSize != 0) {return ERR_UNSUPPORTED_STRUCTURE;}
|
||||
|
||||
// Page RWX flags
|
||||
int ret = getSectionRwxFlags(pe, &(peData->sections));
|
||||
if(ret != 0) {return ret;}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// Copyright (c) 2024-25 Aiden Isik
|
||||
// Copyright (c) 2024 Aiden Isik
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -18,6 +18,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "../common/common.h"
|
||||
#include "../common/datastorage.h"
|
||||
|
|
@ -1,280 +0,0 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// Copyright (c) 2024-25 Aiden Isik
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "gethdrdata.h"
|
||||
|
||||
// Validate PE. This isn't thorough, but it's enough to catch any non-PE/360 files.
|
||||
// I was considering merging this with getHdrData and mapPEToBasefile as we're
|
||||
// basically reading the same data twice, but I think it's beneficial to have a
|
||||
// dedicated place where we validate the input.
|
||||
bool validatePE(FILE *pe, bool skipMachineCheck) // True if valid, else false
|
||||
{
|
||||
// Check if we have at least the size of a DOS header, so we don't overrun the PE
|
||||
fseek(pe, 0, SEEK_END);
|
||||
size_t finalOffset = ftell(pe);
|
||||
|
||||
if(finalOffset < 0x3C + 0x4)
|
||||
{ return false; }
|
||||
|
||||
// Check magic
|
||||
fseek(pe, 0, SEEK_SET);
|
||||
uint16_t magic = get16BitFromPE(pe);
|
||||
|
||||
if(magic != 0x5A4D || errno != SUCCESS) // PE magic
|
||||
{ return false; }
|
||||
|
||||
// Check if pointer to PE header is valid
|
||||
fseek(pe, 0x3C, SEEK_SET);
|
||||
size_t peHeaderOffset = get32BitFromPE(pe);
|
||||
|
||||
if(finalOffset < peHeaderOffset || errno != SUCCESS)
|
||||
{ return false; }
|
||||
|
||||
// Check if the file is big enough to get size of optional header, and therefore size of whole PE header
|
||||
if(finalOffset < 0x14 + 0x2)
|
||||
{ return false; }
|
||||
|
||||
// Check section count
|
||||
fseek(pe, peHeaderOffset + 0x6, SEEK_SET);
|
||||
uint16_t sectionCount = get16BitFromPE(pe);
|
||||
|
||||
if(sectionCount == 0 || errno != SUCCESS)
|
||||
{ return false; }
|
||||
|
||||
// Check if the file is large enough to contain the whole PE header
|
||||
fseek(pe, peHeaderOffset + 0x14, SEEK_SET);
|
||||
uint16_t sizeOfOptHdr = get16BitFromPE(pe);
|
||||
|
||||
// 0x18 == size of COFF header, 0x28 == size of one entry in section table
|
||||
if(finalOffset < peHeaderOffset + 0x18 + sizeOfOptHdr + (sectionCount * 0x28) || errno != SUCCESS)
|
||||
{ return false; }
|
||||
|
||||
// Check machine ID
|
||||
fseek(pe, peHeaderOffset + 0x4, SEEK_SET);
|
||||
|
||||
// 0x1F2 == POWERPCBE
|
||||
uint16_t machineID = get16BitFromPE(pe);
|
||||
|
||||
if((machineID != 0x1F2 && !skipMachineCheck) || errno != SUCCESS)
|
||||
{ return false; }
|
||||
|
||||
// Check subsystem
|
||||
fseek(pe, peHeaderOffset + 0x5C, SEEK_SET);
|
||||
|
||||
uint16_t subsystem = get16BitFromPE(pe);
|
||||
|
||||
if(subsystem != 0xE || errno != SUCCESS) // 0xE == XBOX
|
||||
{ return false; }
|
||||
|
||||
// Check page size/alignment
|
||||
fseek(pe, peHeaderOffset + 0x38, SEEK_SET);
|
||||
|
||||
// 4KiB and 64KiB are the only valid sizes
|
||||
uint32_t pageSize = get32BitFromPE(pe);
|
||||
|
||||
if((pageSize != 0x1000 && pageSize != 0x10000) || errno != SUCCESS)
|
||||
{ return false; }
|
||||
|
||||
// Check each raw offset + raw size in section table
|
||||
fseek(pe, peHeaderOffset + 0x18 + sizeOfOptHdr + 0x10, SEEK_SET); // 0x10 == raw offset in entry
|
||||
|
||||
for(uint16_t i = 0; i < sectionCount; i++)
|
||||
{
|
||||
// If raw size + raw offset exceeds file size, PE is invalid
|
||||
uint32_t rawSize = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return false; }
|
||||
|
||||
uint32_t rawOffset = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return false; }
|
||||
|
||||
if(finalOffset < rawSize + rawOffset)
|
||||
{ return false; }
|
||||
|
||||
// Next entry
|
||||
fseek(pe, 0x20, SEEK_CUR);
|
||||
}
|
||||
|
||||
return true; // Checked enough, this is an Xbox 360 PE file
|
||||
}
|
||||
|
||||
int getSectionInfo(FILE *pe, struct sections *sections)
|
||||
{
|
||||
fseek(pe, 0x3C, SEEK_SET);
|
||||
uint32_t peOffset = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
fseek(pe, peOffset + 0x6, SEEK_SET); // 0x6 == section count
|
||||
sections->count = get16BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
sections->section = calloc(sections->count, sizeof(struct section)); // free() is called for this in setdata
|
||||
|
||||
if(sections->section == NULL)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
fseek(pe, peOffset + 0xF8, SEEK_SET); // 0xF8 == beginning of section table
|
||||
|
||||
for(uint16_t i = 0; i < sections->count; i++)
|
||||
{
|
||||
fseek(pe, 0x8, SEEK_CUR); // Seek to virtual size of section
|
||||
sections->section[i].virtualSize = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
sections->section[i].rva = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
sections->section[i].rawSize = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
sections->section[i].offset = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
fseek(pe, 0xC, SEEK_CUR); // Now progress to characteristics, where we will check flags
|
||||
uint32_t characteristics = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
if(characteristics & PE_SECTION_FLAG_EXECUTE)
|
||||
{
|
||||
sections->section[i].permFlag = XEX_SECTION_CODE | 0b10000; // | 0b(1)0000 == include size of 1
|
||||
}
|
||||
else if(characteristics & PE_SECTION_FLAG_WRITE || characteristics & PE_SECTION_FLAG_DISCARDABLE)
|
||||
{ sections->section[i].permFlag = XEX_SECTION_RWDATA | 0b10000; }
|
||||
else if(characteristics & PE_SECTION_FLAG_READ)
|
||||
{ sections->section[i].permFlag = XEX_SECTION_RODATA | 0b10000; }
|
||||
else
|
||||
{ return ERR_MISSING_SECTION_FLAG; }
|
||||
}
|
||||
|
||||
// Don't need to progress any more to get to beginning of next entry, as characteristics is last field
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int getHdrData(FILE *pe, struct peData *peData, uint8_t flags)
|
||||
{
|
||||
// No flags supported at this time (will be used for getting additional info, for e.g. other optional headers)
|
||||
if(flags)
|
||||
{ return ERR_UNKNOWN_DATA_REQUEST; }
|
||||
|
||||
// Getting PE header offset before we go any further..
|
||||
fseek(pe, 0x3C, SEEK_SET);
|
||||
peData->peHeaderOffset = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Number of sections
|
||||
fseek(pe, peData->peHeaderOffset + 0x6, SEEK_SET);
|
||||
peData->numberOfSections = get16BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Size of section table
|
||||
peData->sectionTableSize = peData->numberOfSections * 0x28;
|
||||
|
||||
// Size of header
|
||||
// 0x18 == size of COFF header, get16BitFromPE value == size of optional header
|
||||
fseek(pe, peData->peHeaderOffset + 0x14, SEEK_SET);
|
||||
peData->headerSize = (peData->peHeaderOffset + 1) + 0x18 + get16BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// PE characteristics
|
||||
fseek(pe, peData->peHeaderOffset + 0x16, SEEK_SET);
|
||||
peData->characteristics = get16BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Entry point (RVA)
|
||||
fseek(pe, peData->peHeaderOffset + 0x28, SEEK_SET);
|
||||
peData->entryPoint = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Base address
|
||||
fseek(pe, peData->peHeaderOffset + 0x34, SEEK_SET);
|
||||
peData->baseAddr = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Page alignment/size
|
||||
fseek(pe, peData->peHeaderOffset + 0x38, SEEK_SET);
|
||||
peData->pageSize = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Export tables
|
||||
fseek(pe, peData->peHeaderOffset + 0x78, SEEK_SET);
|
||||
peData->peExportInfo.count = (get32BitFromPE(pe) == 0 ? 0 : 1); // TODO: Actually read the data
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Import tables
|
||||
fseek(pe, peData->peHeaderOffset + 0x80, SEEK_SET);
|
||||
peData->peImportInfo.idtRVA = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// TLS status (PE TLS is currently UNSUPPORTED, so if we find it, we'll need to abort)
|
||||
fseek(pe, peData->peHeaderOffset + 0xC0, SEEK_SET);
|
||||
peData->tlsAddr = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
peData->tlsSize = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
if(peData->tlsAddr != 0 || peData->tlsSize != 0)
|
||||
{ return ERR_UNSUPPORTED_STRUCTURE; }
|
||||
|
||||
// Section info
|
||||
int ret = getSectionInfo(pe, &(peData->sections));
|
||||
|
||||
if(ret != 0)
|
||||
{ return ret; }
|
||||
|
||||
return SUCCESS;
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// Copyright (c) 2025 Aiden Isik
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "getimports.h"
|
||||
|
||||
int getImports(FILE *pe, struct peData *peData)
|
||||
{
|
||||
// Make sure the peImportInfo struct is blank, except the IDT RVA
|
||||
memset(&(peData->peImportInfo.tableCount), 0, sizeof(struct peImportInfo) - sizeof(uint32_t));
|
||||
|
||||
// If there is no IDT, just skip handling imports
|
||||
if(peData->peImportInfo.idtRVA == 0)
|
||||
{ return SUCCESS; }
|
||||
|
||||
// Seek to the IDT and read the first entry
|
||||
uint32_t idtOffset = rvaToOffset(peData->peImportInfo.idtRVA, &(peData->sections));
|
||||
|
||||
if(idtOffset == 0)
|
||||
{ return ERR_INVALID_RVA_OR_OFFSET; }
|
||||
|
||||
if(fseek(pe, idtOffset, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
uint32_t *currentIDT = malloc(5 * sizeof(uint32_t));
|
||||
|
||||
if(currentIDT == NULL)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
uint32_t *blankIDT = calloc(5, sizeof(uint32_t)); // Blank IDT for comparisons
|
||||
|
||||
if(blankIDT == NULL)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
return ERR_OUT_OF_MEM;
|
||||
}
|
||||
|
||||
if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
// While the IDT is not blank, process it
|
||||
for(uint32_t i = 0; memcmp(currentIDT, blankIDT, 5 * sizeof(uint32_t)) != 0; i++)
|
||||
{
|
||||
// Allocate space for the current table data
|
||||
peData->peImportInfo.tableCount++;
|
||||
peData->peImportInfo.tables = realloc(peData->peImportInfo.tables, (i + 1) * sizeof(struct peImportTable));
|
||||
|
||||
if(peData->peImportInfo.tables == NULL)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_OUT_OF_MEM;
|
||||
}
|
||||
|
||||
memset(&(peData->peImportInfo.tables[i]), 0, sizeof(struct peImportTable)); // Make sure it's blank
|
||||
|
||||
#ifdef BIG_ENDIAN_SYSTEM
|
||||
|
||||
// Byteswap the IDT fields
|
||||
for(uint8_t j = 0; j < 5; j++)
|
||||
{ currentIDT[j] = __builtin_bswap32(currentIDT[j]); }
|
||||
|
||||
#endif
|
||||
|
||||
// Retrieve the name pointed to by the table
|
||||
uint32_t savedOffset = ftell(pe);
|
||||
uint32_t tableNameOffset = rvaToOffset(currentIDT[3], &(peData->sections));
|
||||
|
||||
if(tableNameOffset == 0)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_INVALID_RVA_OR_OFFSET;
|
||||
}
|
||||
|
||||
if(fseek(pe, tableNameOffset, SEEK_SET) != 0)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
// Allocating is expensive, go 16 bytes at a time to avoid excessive realloc calls
|
||||
for(uint32_t j = 0;; j += 16)
|
||||
{
|
||||
peData->peImportInfo.tables[i].name = realloc(peData->peImportInfo.tables[i].name, (j + 16) * sizeof(char));
|
||||
|
||||
if(peData->peImportInfo.tables[i].name == NULL)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_OUT_OF_MEM;
|
||||
}
|
||||
|
||||
if(fread(peData->peImportInfo.tables[i].name + j, sizeof(char), 16, pe) < 16)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
// Check for null terminator
|
||||
for(uint32_t k = j; k < j + 16; k++)
|
||||
if(peData->peImportInfo.tables[i].name[k] == '\0')
|
||||
{ goto doneGettingTableName; }
|
||||
}
|
||||
|
||||
doneGettingTableName:
|
||||
// Store the IAT RVA for this table
|
||||
peData->peImportInfo.tables[i].rva = currentIDT[4];
|
||||
|
||||
// Seek to the IAT and read the first entry
|
||||
uint32_t iatOffset = rvaToOffset(currentIDT[4], &(peData->sections));
|
||||
|
||||
if(iatOffset == 0)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_INVALID_RVA_OR_OFFSET;
|
||||
}
|
||||
|
||||
if(fseek(pe, iatOffset, SEEK_SET) != 0)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
uint32_t currentImport;
|
||||
|
||||
if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
// While the import is not blank, process it
|
||||
for(int j = 0; currentImport != 0; j++)
|
||||
{
|
||||
#ifdef BIG_ENDIAN_SYSTEM
|
||||
// Byteswap the import
|
||||
currentImport = __builtin_bswap32(currentImport);
|
||||
#endif
|
||||
// Allocate space for the current import
|
||||
peData->peImportInfo.tables[i].importCount++;
|
||||
peData->peImportInfo.tables[i].imports = realloc(peData->peImportInfo.tables[i].imports, (j + 1) * sizeof(struct peImport));
|
||||
|
||||
// Store the address of the current import entry in iatAddr
|
||||
uint32_t currentImportRVA = offsetToRVA(ftell(pe) - 4, &(peData->sections));
|
||||
|
||||
if(currentImportRVA == 0)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_INVALID_RVA_OR_OFFSET;
|
||||
}
|
||||
|
||||
peData->peImportInfo.tables[i].imports[j].iatAddr = peData->baseAddr + currentImportRVA;
|
||||
|
||||
// Read the next import
|
||||
if(fread(¤tImport, sizeof(uint32_t), 1, pe) < 1)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
}
|
||||
|
||||
// Add table's import count to total and return to next IDT entry
|
||||
peData->peImportInfo.totalImportCount += peData->peImportInfo.tables[i].importCount;
|
||||
|
||||
if(fseek(pe, savedOffset, SEEK_SET) != 0)
|
||||
{
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
// Read next IDT
|
||||
if(fread(currentIDT, sizeof(uint32_t), 5, pe) < 5)
|
||||
{ return ERR_FILE_READ; }
|
||||
}
|
||||
|
||||
nullAndFree((void **)¤tIDT);
|
||||
nullAndFree((void **)&blankIDT);
|
||||
return SUCCESS;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// Copyright (c) 2025 Aiden Isik
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "../common/datastorage.h"
|
||||
|
||||
int getImports(FILE *pe, struct peData *peData);
|
407
src/main.c
407
src/main.c
|
@ -16,14 +16,10 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// TODO: Harden all functions against bad data, unallocated data (check for NULL), etc.
|
||||
// Some functions are not robust as I wanted things up and running as quickly as possible.
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/datastorage.h"
|
||||
#include "pemapper/pemapper.h"
|
||||
#include "getdata/gethdrdata.h"
|
||||
#include "getdata/getimports.h"
|
||||
#include "getdata/getdata.h"
|
||||
#include "setdata/populateheaders.h"
|
||||
#include "setdata/pagedescriptors.h"
|
||||
#include "setdata/optheaders.h"
|
||||
|
@ -90,8 +86,8 @@ void dispLibs()
|
|||
|
||||
void dispVer()
|
||||
{
|
||||
printf("\n%s\n\nThe XEX builder of the FreeChainXenon project\n\n", SYNTHXEX_VERSION_STRING);
|
||||
printf("Copyright (c) %s Aiden Isik\n\nThis program is free software: you can redistribute it and/or modify\n", SYNTHXEX_COPYRIGHT);
|
||||
printf("\n%s\n\nThe XEX builder of the FreeChainXenon project\n\n", VERSION_STRING);
|
||||
printf("Copyright (c) %s Aiden Isik\n\nThis program is free software: you can redistribute it and/or modify\n", COPYRIGHT);
|
||||
printf("it under the terms of the GNU Affero General Public License as published by\n");
|
||||
printf("the Free Software Foundation, either version 3 of the License, or\n");
|
||||
printf("(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\n");
|
||||
|
@ -110,86 +106,22 @@ void dispHelp(char **argv)
|
|||
printf("-l,\t--libs,\t\t\tShow licensing information of libraries used\n");
|
||||
printf("-s,\t--skip-machine-check,\tSkip the PE file machine ID check\n");
|
||||
printf("-i,\t--input,\t\tSpecify input PE file path\n");
|
||||
printf("-o,\t--output,\t\tSpecify output XEX file path\n");
|
||||
printf("-t,\t--type,\t\t\tOverride automatic executable type detection\n\t\t\t\t(options: title, titledll, sysdll, dll)\n\n");
|
||||
}
|
||||
|
||||
void handleError(int ret)
|
||||
{
|
||||
switch(ret)
|
||||
{
|
||||
case ERR_UNKNOWN_DATA_REQUEST:
|
||||
fprintf(stderr, "%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_FILE_READ:
|
||||
fprintf(stderr, "%s ERROR: Failed to read data from PE file. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_FILE_WRITE:
|
||||
fprintf(stderr, "%s ERROR: Failed to write data to file. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_OUT_OF_MEM:
|
||||
fprintf(stderr, "%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_MISSING_SECTION_FLAG:
|
||||
fprintf(stderr, "%s ERROR: R/W/X flag missing from PE section. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_UNSUPPORTED_STRUCTURE:
|
||||
fprintf(stderr, "%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_INVALID_RVA_OR_OFFSET:
|
||||
fprintf(stderr, "%s ERROR: Invalid RVA or offset found. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_INVALID_IMPORT_NAME:
|
||||
fprintf(stderr, "%s ERROR: Invalid import name found. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
case ERR_DATA_OVERFLOW:
|
||||
fprintf(stderr, "%s ERROR: Data overflow. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "%s ERROR: Unknown error: %d. Aborting.\n", SYNTHXEX_PRINT_STEM, ret);
|
||||
break;
|
||||
}
|
||||
printf("-o,\t--output,\t\tSpecify output XEX file path\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static struct option longOptions[] =
|
||||
{
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "version", no_argument, 0, 'v' },
|
||||
{ "libs", no_argument, 0, 'l' },
|
||||
{ "skip-machine-check", no_argument, 0, 's' },
|
||||
{ "input", required_argument, 0, 'i' },
|
||||
{ "output", required_argument, 0, 'o' },
|
||||
{ "type", required_argument, 0, 't' },
|
||||
{"help", no_argument, 0, 0},
|
||||
{"version", no_argument, 0, 0},
|
||||
{"libs", no_argument, 0, 0},
|
||||
{"skip-machine-check", no_argument, 0, 0},
|
||||
{"input", required_argument, 0, 0},
|
||||
{"output", required_argument, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
struct offsets *offsets = calloc(1, sizeof(struct offsets));
|
||||
struct xexHeader *xexHeader = calloc(1, sizeof(struct xexHeader));
|
||||
struct secInfoHeader *secInfoHeader = calloc(1, sizeof(struct secInfoHeader));
|
||||
struct peData *peData = calloc(1, sizeof(struct peData));
|
||||
struct optHeaderEntries *optHeaderEntries = calloc(1, sizeof(struct optHeaderEntries));
|
||||
struct optHeaders *optHeaders = calloc(1, sizeof(struct optHeaders));
|
||||
|
||||
if(offsets == NULL || xexHeader == NULL || secInfoHeader == NULL ||
|
||||
peData == NULL || optHeaderEntries == NULL || optHeaders == NULL)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting\n", SYNTHXEX_PRINT_STEM);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int optIndex = 0;
|
||||
int option = 0;
|
||||
|
||||
|
@ -200,194 +132,185 @@ int main(int argc, char **argv)
|
|||
char *pePath = NULL;
|
||||
char *xexfilePath = NULL;
|
||||
|
||||
while((option = getopt_long(argc, argv, "hvlsi:o:t:", longOptions, &optIndex)) != -1)
|
||||
while((option = getopt_long(argc, argv, "hvlsi:o:", longOptions, &optIndex)) != -1)
|
||||
{
|
||||
switch(option)
|
||||
if(option == 'h' || option == '?' || (option == 0 && strcmp(longOptions[optIndex].name, "help") == 0))
|
||||
{
|
||||
dispHelp(argv);
|
||||
return SUCCESS;
|
||||
}
|
||||
else if(option == 'v' || (option == 0 && strcmp(longOptions[optIndex].name, "version") == 0))
|
||||
{
|
||||
case 'v':
|
||||
dispVer();
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
return SUCCESS;
|
||||
|
||||
case 'l':
|
||||
}
|
||||
else if(option == 'l' || (option == 0 && strcmp(longOptions[optIndex].name, "libs") == 0))
|
||||
{
|
||||
dispLibs();
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
return SUCCESS;
|
||||
|
||||
case 's':
|
||||
printf("%s WARNING: Skipping machine ID check.\n", SYNTHXEX_PRINT_STEM);
|
||||
}
|
||||
else if(option == 's' || (option == 0 && strcmp(longOptions[optIndex].name, "skip-machine-check") == 0))
|
||||
{
|
||||
printf("%s WARNING: Skipping machine ID check.\n", PRINT_STEM);
|
||||
skipMachineCheck = true;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
}
|
||||
else if(option == 'i' || (option == 0 && strcmp(longOptions[optIndex].name, "input") == 0))
|
||||
{
|
||||
gotInput = true;
|
||||
pePath = malloc(strlen(optarg) + 1);
|
||||
|
||||
if(pePath == NULL)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
if(xexfilePath != NULL) {free(xexfilePath);}
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(pePath, optarg);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
strncpy(pePath, optarg, strlen(optarg) + 1);
|
||||
}
|
||||
else if(option == 'o' || (option == 0 && strcmp(longOptions[optIndex].name, "output") == 0))
|
||||
{
|
||||
gotOutput = true;
|
||||
xexfilePath = malloc(strlen(optarg) + 1);
|
||||
|
||||
if(xexfilePath == NULL)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
nullAndFree((void **)&pePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
if(pePath != NULL) {free(pePath);}
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(xexfilePath, optarg);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
if(strcmp(optarg, "title") == 0)
|
||||
{ xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE; }
|
||||
else if(strcmp(optarg, "titledll") == 0)
|
||||
{ xexHeader->moduleFlags = XEX_MOD_FLAG_TITLE | XEX_MOD_FLAG_DLL; }
|
||||
else if(strcmp(optarg, "sysdll") == 0)
|
||||
{ xexHeader->moduleFlags = XEX_MOD_FLAG_EXPORTS | XEX_MOD_FLAG_DLL; }
|
||||
else if(strcmp(optarg, "dll") == 0)
|
||||
{ xexHeader->moduleFlags = XEX_MOD_FLAG_DLL; }
|
||||
else
|
||||
{
|
||||
printf("%s ERROR: Invalid type override \"%s\" (valid: title, titledll, sysdll, dll). Aborting.\n",
|
||||
SYNTHXEX_PRINT_STEM, optarg);
|
||||
|
||||
nullAndFree((void **)&pePath);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
default:
|
||||
dispHelp(argv);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData,
|
||||
&optHeaderEntries, &optHeaders);
|
||||
return SUCCESS;
|
||||
strncpy(xexfilePath, optarg, strlen(optarg) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s This is %s. Copyright (c) %s Aiden Isik.\n", SYNTHXEX_PRINT_STEM, SYNTHXEX_VERSION_STRING, SYNTHXEX_COPYRIGHT);
|
||||
printf("%s This program is free/libre software. Run \"%s --version\" for info.\n\n", SYNTHXEX_PRINT_STEM, argv[0]);
|
||||
printf("%s This is %s. Copyright (c) %s Aiden Isik.\n", PRINT_STEM, VERSION_STRING, COPYRIGHT);
|
||||
printf("%s This program is free/libre software. Run \"%s --version\" for info.\n\n", PRINT_STEM, argv[0]);
|
||||
|
||||
// Check we got everything we need
|
||||
if(!gotInput)
|
||||
{
|
||||
if(gotOutput)
|
||||
{ nullAndFree((void **)&xexfilePath); }
|
||||
{
|
||||
free(xexfilePath);
|
||||
}
|
||||
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: PE input expected but not found. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
printf("%s ERROR: PE input expected but not found. Aborting.\n", PRINT_STEM);
|
||||
return -1;
|
||||
}
|
||||
else if(!gotOutput)
|
||||
{
|
||||
if(gotInput)
|
||||
{ nullAndFree((void **)&pePath); }
|
||||
{
|
||||
free(pePath);
|
||||
}
|
||||
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: XEX file output expected but not found. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
printf("%s ERROR: XEX file output expected but not found. Aborting.\n", PRINT_STEM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Opening the files now that they've been validated
|
||||
FILE *pe = fopen(pePath, "rb");
|
||||
|
||||
if(pe == NULL)
|
||||
{
|
||||
printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
nullAndFree((void **)&pePath);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Failed to open PE file. Do you have read permissions? Aborting.\n", PRINT_STEM);
|
||||
free(pePath);
|
||||
free(xexfilePath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nullAndFree((void **)&pePath);
|
||||
free(pePath);
|
||||
|
||||
FILE *xex = fopen(xexfilePath, "wb+");
|
||||
|
||||
if(xex == NULL)
|
||||
{
|
||||
printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
printf("%s ERROR: Failed to create XEX file. Do you have write permissions? Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
free(xexfilePath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Keep xexfilePath for later use
|
||||
// Do NOT free xexfilePath here yet
|
||||
// Don't free this yet. We need it to determine where we can put the mapped basefile.
|
||||
// There *are* ways to get the file path from file pointer, but none of them are portable.
|
||||
//free(xexfilePath);
|
||||
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
printf("%s Validating PE file...\n", SYNTHXEX_PRINT_STEM);
|
||||
struct offsets offsets;
|
||||
struct xexHeader xexHeader;
|
||||
struct secInfoHeader secInfoHeader;
|
||||
struct peData peData;
|
||||
struct optHeaderEntries optHeaderEntries;
|
||||
struct optHeaders optHeaders;
|
||||
|
||||
// Make sure the import library size is initially zero
|
||||
optHeaders.importLibraries.size = 0;
|
||||
|
||||
printf("%s Validating PE file...\n", PRINT_STEM);
|
||||
|
||||
if(!validatePE(pe, skipMachineCheck))
|
||||
{
|
||||
printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Input PE is not Xbox 360 PE. Aborting.\n", PRINT_STEM);
|
||||
free(xexfilePath);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s PE valid!\n", SYNTHXEX_PRINT_STEM);
|
||||
printf("%s PE valid!\n", PRINT_STEM);
|
||||
|
||||
printf("%s Retrieving header data from PE...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = getHdrData(pe, peData, 0);
|
||||
// Reading in header data from PE
|
||||
printf("%s Retrieving header data from PE...\n", PRINT_STEM);
|
||||
ret = getHdrData(pe, &peData, 0);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
if(ret == ERR_UNKNOWN_DATA_REQUEST)
|
||||
{
|
||||
handleError(ret);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Internal error getting data from PE file. THIS IS A BUG, please report it. Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Got header data from PE!\n", SYNTHXEX_PRINT_STEM);
|
||||
|
||||
printf("%s Retrieving import data from PE...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = getImports(pe, peData);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
else if(ret == ERR_FILE_READ)
|
||||
{
|
||||
handleError(ret);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Failed to read data from PE file. Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
else if(ret == ERR_OUT_OF_MEM)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
else if(ret == ERR_MISSING_SECTION_FLAG)
|
||||
{
|
||||
printf("%s ERROR: R/W/X flag missing from PE section. Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
else if(ret == ERR_UNSUPPORTED_STRUCTURE)
|
||||
{
|
||||
printf("%s ERROR: Encountered an unsupported data structure in PE. Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Got import data from PE!\n", SYNTHXEX_PRINT_STEM);
|
||||
printf("%s Got header data from PE!\n", PRINT_STEM);
|
||||
|
||||
printf("%s Creating basefile from PE...\n", SYNTHXEX_PRINT_STEM);
|
||||
// Determine the path we should save the basefile at and open it.
|
||||
printf("%s Creating basefile from PE...\n", PRINT_STEM);
|
||||
char *basefilePath = malloc((strlen(xexfilePath) + strlen(".basefile") + 1) * sizeof(char));
|
||||
|
||||
if(!basefilePath)
|
||||
if(basefilePath == NULL)
|
||||
{
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
free(xexfilePath);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
|
@ -397,138 +320,94 @@ int main(int argc, char **argv)
|
|||
strcat(basefilePath, ".basefile");
|
||||
|
||||
FILE* basefile = fopen(basefilePath, "wb+");
|
||||
nullAndFree((void **)&xexfilePath);
|
||||
nullAndFree((void **)&basefilePath);
|
||||
free(xexfilePath); // *Now* we're done with it.
|
||||
free(basefilePath);
|
||||
|
||||
if(!basefile)
|
||||
if(basefile == NULL)
|
||||
{
|
||||
printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", SYNTHXEX_PRINT_STEM);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Could not create basefile. Do you have write permission? Aborting.\n", PRINT_STEM);
|
||||
fclose(pe);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Map the PE into the basefile (RVAs become offsets)
|
||||
ret = mapPEToBasefile(pe, basefile, peData);
|
||||
ret = mapPEToBasefile(pe, basefile, &peData);
|
||||
fclose(pe);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
if(ret == ERR_OUT_OF_MEM)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Created basefile!\n", SYNTHXEX_PRINT_STEM);
|
||||
printf("%s Created basefile!\n", PRINT_STEM);
|
||||
|
||||
// Setting final XEX data structs
|
||||
printf("%s Building security header...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = setSecInfoHeader(secInfoHeader, peData);
|
||||
printf("%s Building XEX header...\n", PRINT_STEM);
|
||||
setXEXHeader(&xexHeader, &peData);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
printf("%s Building security header...\n", PRINT_STEM);
|
||||
setSecInfoHeader(&secInfoHeader, &peData);
|
||||
|
||||
printf("%s Setting page descriptors...\n", PRINT_STEM);
|
||||
ret = setPageDescriptors(basefile, &peData, &secInfoHeader);
|
||||
|
||||
if(ret == ERR_OUT_OF_MEM)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Setting page descriptors...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = setPageDescriptors(basefile, peData, secInfoHeader);
|
||||
printf("%s Building optional headers...\n", PRINT_STEM);
|
||||
ret = setOptHeaders(&secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
if(ret == ERR_OUT_OF_MEM)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Done with this now
|
||||
freeSectionsStruct(&(peData->sections));
|
||||
// Setting data positions...
|
||||
printf("%s Aligning data...\n", PRINT_STEM);
|
||||
ret = placeStructs(&offsets, &xexHeader, &optHeaderEntries, &secInfoHeader, &optHeaders);
|
||||
|
||||
printf("%s Building optional headers...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = setOptHeaders(secInfoHeader, peData, optHeaderEntries, optHeaders);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
if(ret == ERR_OUT_OF_MEM)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Building XEX header...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = setXEXHeader(xexHeader, optHeaderEntries, peData);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Done with this now
|
||||
freePeImportInfoStruct(&(peData->peImportInfo));
|
||||
|
||||
// Setting data positions
|
||||
printf("%s Aligning data...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = placeStructs(offsets, xexHeader, optHeaderEntries, secInfoHeader, optHeaders);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We're done with this now
|
||||
freePeDataStruct(&peData);
|
||||
|
||||
// Write out all of the XEX data to file
|
||||
printf("%s Writing XEX...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = writeXEX(xexHeader, optHeaderEntries, secInfoHeader, optHeaders, offsets, basefile, xex);
|
||||
printf("%s Writing data to XEX file...\n", PRINT_STEM);
|
||||
ret = writeXEX(&xexHeader, &optHeaderEntries, &secInfoHeader, &optHeaders, &offsets, basefile, xex);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
if(ret == ERR_OUT_OF_MEM)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
printf("%s ERROR: Out of memory. Aborting.\n", PRINT_STEM);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Main data written to XEX file!\n", PRINT_STEM);
|
||||
|
||||
// Final pass (sets & writes header hash)
|
||||
printf("%s Calculating and writing header SHA1...\n", SYNTHXEX_PRINT_STEM);
|
||||
ret = setHeaderSha1(xex);
|
||||
printf("%s Calculating and writing header SHA1...\n", PRINT_STEM);
|
||||
setHeaderSha1(xex);
|
||||
printf("%s Header SHA1 written!\n", PRINT_STEM);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
{
|
||||
handleError(ret);
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
fclose(basefile);
|
||||
fclose(xex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s Header SHA1 written!\n", SYNTHXEX_PRINT_STEM);
|
||||
|
||||
// Free files
|
||||
fclose(xex);
|
||||
fclose(basefile);
|
||||
// Free structs
|
||||
freeAllMainStructs(&offsets, &xexHeader, &secInfoHeader, &peData, &optHeaderEntries, &optHeaders);
|
||||
|
||||
printf("%s XEX built. Have a nice day!\n\n", SYNTHXEX_PRINT_STEM);
|
||||
printf("%s XEX built. Have a nice day!\n\n", PRINT_STEM);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -26,159 +26,63 @@ struct sectionInfo
|
|||
uint32_t offset;
|
||||
};
|
||||
|
||||
// Strips the ordinal flags from IAT entries, and swaps them to big endian
|
||||
// Also adds module indexes to them
|
||||
int xenonifyIAT(FILE *basefile, struct peData *peData)
|
||||
{
|
||||
// Loop through each import table and handle their IAT entries
|
||||
for(uint32_t i = 0; i < peData->peImportInfo.tableCount; i++)
|
||||
{
|
||||
// Seek to the first IAT entry for this table
|
||||
if(fseek(basefile, peData->peImportInfo.tables[i].rva, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
// Loop through each import and handle it's IAT entry
|
||||
for(uint32_t j = 0; j < peData->peImportInfo.tables[i].importCount; j++)
|
||||
{
|
||||
// Read in the current IAT entry
|
||||
uint32_t iatEntry = get32BitFromPE(basefile);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
// Seek back so we can write back to the same place
|
||||
if(fseek(basefile, -0x4, SEEK_CUR) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
// Xenonify the IAT entry
|
||||
iatEntry &= ~PE_IMPORT_ORDINAL_FLAG; // Strip the import by ordinal flag
|
||||
iatEntry |= (i & 0x000000FF) << 16; // Add the module index
|
||||
|
||||
// Write back out as big endian (TODO: make a utility function for this like get32BitFromPE)
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
iatEntry = __builtin_bswap32(iatEntry);
|
||||
#endif
|
||||
|
||||
if(fwrite(&iatEntry, sizeof(uint32_t), 1, basefile) < 1)
|
||||
{ return ERR_FILE_WRITE; }
|
||||
|
||||
// Call file positioning function between reads and writes to the same file.
|
||||
// This is mandated by the C standard.
|
||||
fseek(basefile, 0, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// Maps the PE file into the basefile (RVAs become offsets)
|
||||
int mapPEToBasefile(FILE *pe, FILE *basefile, struct peData *peData)
|
||||
{
|
||||
// Retrieve the section info from PE (TODO: use the data we get from getHdrData for this, right now we're duplicating work)
|
||||
// Retrieve data for each section
|
||||
struct sectionInfo *sectionInfo = malloc(peData->numberOfSections * sizeof(struct sectionInfo));
|
||||
|
||||
if(!sectionInfo)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
// Seek to the first section in the section table at virtualSize
|
||||
if(fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
fseek(pe, (peData->headerSize - 1) + 0x8, SEEK_SET); // Seek to the first section in the section table at virtualSize
|
||||
|
||||
for(uint16_t i = 0; i < peData->numberOfSections; i++)
|
||||
{
|
||||
sectionInfo[i].virtualSize = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
sectionInfo[i].rva = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
sectionInfo[i].rawSize = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
sectionInfo[i].offset = get32BitFromPE(pe);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Seek to the next entry at virtualSize
|
||||
if(fseek(pe, 0x18, SEEK_CUR) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
fseek(pe, 0x18, SEEK_CUR); // Seek to next entry at virtualSize
|
||||
}
|
||||
|
||||
if(fseek(pe, 0, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
// Copy the PE header and section table to the basefile verbatim
|
||||
fseek(pe, 0, SEEK_SET);
|
||||
uint8_t *buffer = malloc(peData->headerSize + peData->sectionTableSize);
|
||||
if(buffer == NULL) {return ERR_OUT_OF_MEM;}
|
||||
|
||||
if(!buffer)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
size_t totalHeader = peData->headerSize + peData->sectionTableSize;
|
||||
|
||||
if(fread(buffer, 1, totalHeader, pe) != totalHeader)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
if(fwrite(buffer, 1, totalHeader, basefile) != totalHeader)
|
||||
{ return ERR_FILE_READ; }
|
||||
fread(buffer, sizeof(uint8_t), peData->headerSize + peData->sectionTableSize, pe);
|
||||
fwrite(buffer, sizeof(uint8_t), peData->headerSize + peData->sectionTableSize, basefile);
|
||||
|
||||
// Now map the sections and write them
|
||||
for(uint16_t i = 0; i < peData->numberOfSections; i++)
|
||||
{
|
||||
buffer = realloc(buffer, sectionInfo[i].rawSize);
|
||||
buffer = realloc(buffer, sectionInfo[i].rawSize * sizeof(uint8_t));
|
||||
if(buffer == NULL) {return ERR_OUT_OF_MEM;}
|
||||
|
||||
if(!buffer)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
fseek(pe, sectionInfo[i].offset, SEEK_SET);
|
||||
fread(buffer, sizeof(uint8_t), sectionInfo[i].rawSize, pe);
|
||||
|
||||
if(fseek(pe, sectionInfo[i].offset, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
if(fread(buffer, 1, sectionInfo[i].rawSize, pe) != sectionInfo[i].rawSize)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
if(fseek(basefile, sectionInfo[i].rva, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
if(fwrite(buffer, 1, sectionInfo[i].rawSize, basefile) != sectionInfo[i].rawSize)
|
||||
{ return ERR_FILE_READ; }
|
||||
fseek(basefile, sectionInfo[i].rva, SEEK_SET);
|
||||
fwrite(buffer, sizeof(uint8_t), sectionInfo[i].rawSize, basefile);
|
||||
}
|
||||
|
||||
// Pad the rest of the final page with zeroes, we can achieve this by seeking
|
||||
// to the end and placing a single zero there (unless the data runs all the way up to the end!)
|
||||
uint32_t currentOffset = ftell(basefile);
|
||||
uint32_t nextAligned = getNextAligned(currentOffset, peData->pageSize) - 1;
|
||||
uint32_t nextAligned = getNextAligned(currentOffset, peData->pageSize) - 0x1;
|
||||
|
||||
if(nextAligned != currentOffset)
|
||||
{
|
||||
buffer = realloc(buffer, 1);
|
||||
|
||||
if(!buffer)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
buffer = realloc(buffer, 1 * sizeof(uint8_t));
|
||||
if(buffer == NULL) {return ERR_OUT_OF_MEM;}
|
||||
buffer[0] = 0;
|
||||
|
||||
if(fseek(basefile, nextAligned, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
if(fwrite(buffer, 1, 1, basefile) != 1)
|
||||
{ return ERR_FILE_READ; }
|
||||
fseek(basefile, nextAligned, SEEK_SET);
|
||||
fwrite(buffer, sizeof(uint8_t), 1, basefile);
|
||||
}
|
||||
|
||||
// Make sure to update the PE (basefile) size
|
||||
peData->size = ftell(basefile);
|
||||
|
||||
// We're done with these now, free them
|
||||
nullAndFree((void **)&buffer);
|
||||
nullAndFree((void **)§ionInfo);
|
||||
// We're done with these, free them
|
||||
free(buffer);
|
||||
free(sectionInfo);
|
||||
|
||||
// While we're writing the basefile, let's do the required modifications to the IAT.
|
||||
// We can skip the return check because this is the last function call.
|
||||
// The outcome of this is the outcome of the whole function.
|
||||
return xenonifyIAT(basefile, peData);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -27,14 +27,9 @@ struct importLibIdcs
|
|||
|
||||
int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHeaderEntries, struct optHeaders *optHeaders, uint32_t *currentOffset, struct importLibIdcs *importLibIdcs)
|
||||
{
|
||||
// Calloc because 0 values will be used to determine if a header is not present.
|
||||
offsets->optHeaders = calloc(optHeaderEntries->count, sizeof(uint32_t));
|
||||
|
||||
if(offsets->optHeaders == NULL)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
// Separate header iterator, i.e. one with it's data outwith the entries
|
||||
uint32_t sepHeader = 0;
|
||||
offsets->optHeaders = calloc(optHeaderEntries->count, sizeof(uint32_t)); // Calloc because 0 values will be used to determine if a header is not present.
|
||||
if(offsets->optHeaders == NULL) {return ERR_OUT_OF_MEM;}
|
||||
uint32_t sepHeader = 0; // Separate header iterator, i.e. one with it's data outwith the entries
|
||||
|
||||
for(uint32_t i = 0; i < optHeaderEntries->count; i++)
|
||||
{
|
||||
|
@ -67,6 +62,7 @@ int setOptHeaderOffsets(struct offsets *offsets, struct optHeaderEntries *optHea
|
|||
return SUCCESS;
|
||||
}
|
||||
|
||||
// Todo in future: implement a dynamic optional header selection mechanism instead of hard-coding the basic 5
|
||||
int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct secInfoHeader *secInfoHeader, struct optHeaders *optHeaders)
|
||||
{
|
||||
// XEX Header
|
||||
|
@ -79,19 +75,22 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op
|
|||
currentOffset += optHeaderEntries->count * sizeof(struct optHeaderEntry);
|
||||
|
||||
// Security header
|
||||
currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers, at least 8 bytes beyond end of optional header entries
|
||||
currentOffset = getNextAligned(currentOffset, 0x8); // 8-byte alignment for these headers etc
|
||||
offsets->secInfoHeader = currentOffset;
|
||||
xexHeader->secInfoOffset = currentOffset;
|
||||
currentOffset += (sizeof(struct secInfoHeader) - sizeof(void*)) + (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor));
|
||||
|
||||
// Optional headers (minus imports)
|
||||
struct importLibIdcs importLibIdcs;
|
||||
uint32_t importLibsIdx; // Entry in opt header entries of import libs
|
||||
int ret = setOptHeaderOffsets(offsets, optHeaderEntries, optHeaders, ¤tOffset, &importLibIdcs);
|
||||
|
||||
if(ret != SUCCESS)
|
||||
{ return ret; }
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
currentOffset += optHeaders->importLibraries.size; // Reserving bytes for imports
|
||||
//currentOffset += optHeaders->importLibraries.size; // Reserving bytes for imports
|
||||
|
||||
// PE basefile
|
||||
currentOffset = getNextAligned(currentOffset, 0x1000); // 4KiB alignment for basefile
|
||||
|
@ -99,11 +98,8 @@ int placeStructs(struct offsets *offsets, struct xexHeader *xexHeader, struct op
|
|||
xexHeader->peOffset = currentOffset;
|
||||
|
||||
// Imports, the end of this header is aligned to the start of the basefile, so they are a special case
|
||||
if(optHeaders->importLibraries.tableCount > 0)
|
||||
{
|
||||
offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size;
|
||||
optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header];
|
||||
}
|
||||
//offsets->optHeaders[importLibIdcs.header] = offsets->basefile - optHeaders->importLibraries.size;
|
||||
//optHeaderEntries->optHeaderEntry[importLibIdcs.entry].dataOrOffset = offsets->optHeaders[importLibIdcs.header];
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// Copyright (c) 2024-25 Aiden Isik
|
||||
// Copyright (c) 2024 Aiden Isik
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -36,255 +36,9 @@ void setTLSInfo(struct tlsInfo *tlsInfo)
|
|||
tlsInfo->rawDataSize = 0x0;
|
||||
}
|
||||
|
||||
int setImportLibsInfo(struct importLibraries *importLibraries, struct peImportInfo *peImportInfo, struct secInfoHeader *secInfoHeader)
|
||||
{
|
||||
// Set table count and allocate enough memory for all tables
|
||||
importLibraries->tableCount = peImportInfo->tableCount;
|
||||
secInfoHeader->importTableCount = peImportInfo->tableCount;
|
||||
|
||||
importLibraries->importTables = calloc(importLibraries->tableCount, sizeof(struct importTable));
|
||||
|
||||
if(!importLibraries->importTables)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
if(peImportInfo->tableCount <= 0 || peImportInfo->tableCount > 65535)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
// Use this to avoid dereferencing an unaligned pointer
|
||||
struct importTable *importTables = importLibraries->importTables;
|
||||
|
||||
// Initialise the size of the import libraries to just the size of the header (- 2 * sizeof(void*) to exclude addresses for internal use only)
|
||||
importLibraries->size = (sizeof(struct importLibraries) + importLibraries->nameTableSize) - (2 * sizeof(void *));
|
||||
|
||||
int ret = ERR_INVALID_IMPORT_NAME;
|
||||
|
||||
// Allocate name list
|
||||
char **names = calloc(importLibraries->tableCount, sizeof(char *));
|
||||
|
||||
if(!names)
|
||||
{ goto cleanup_tables; }
|
||||
|
||||
// Populate each table, then compute it's hash and store in the previous table
|
||||
for(int64_t i = importLibraries->tableCount - 1; i >= 0; i--)
|
||||
{
|
||||
// Set the table index field to the current index
|
||||
importTables[i].tableIndex = i;
|
||||
|
||||
// Extract the name, target, and minimum versions from the name string
|
||||
// Major and minor version are always 2 and 0, respectively
|
||||
// Strings are definitely null-terminated as otherwise they couldn't have been read in
|
||||
const uint8_t majorVer = 2;
|
||||
const uint8_t minorVer = 0;
|
||||
char *targetBuildVerStr = NULL;
|
||||
char *targetHotfixVerStr = NULL;
|
||||
char *minimumBuildVerStr = NULL;
|
||||
char *minimumHotfixVerStr = NULL;
|
||||
uint16_t buildVer = 0;
|
||||
uint8_t hotfixVer = 0;
|
||||
char *strtoulRet = NULL;
|
||||
|
||||
// Get the name
|
||||
uint32_t oldNameLen = strlen(peImportInfo->tables[i].name);
|
||||
names[i] = strtok(peImportInfo->tables[i].name, "@");
|
||||
|
||||
if(!peImportInfo->tables[i].name)
|
||||
{ goto cleanup_names_invalid; } // Encountered '\0', not '@'
|
||||
|
||||
// Target versions first
|
||||
targetBuildVerStr = strtok(NULL, ".");
|
||||
|
||||
if(!targetBuildVerStr)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) == oldNameLen)
|
||||
{ goto cleanup_names_invalid; } // Encountered null terminator instead of '.'
|
||||
|
||||
buildVer = (uint16_t)strtoul(targetBuildVerStr, &strtoulRet, 10);
|
||||
|
||||
if(*strtoulRet != 0 || strtoulRet == targetBuildVerStr)
|
||||
{ goto cleanup_names_invalid; } // Encountered a non-number, or string was empty
|
||||
|
||||
targetHotfixVerStr = strtok(NULL, "+");
|
||||
|
||||
if(!targetHotfixVerStr)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr) == oldNameLen)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
hotfixVer = (uint8_t)strtoul(targetHotfixVerStr, &strtoulRet, 10);
|
||||
|
||||
if(*strtoulRet != 0 || strtoulRet == targetHotfixVerStr)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
// Now pack these into the target version bitfield
|
||||
importTables[i].targetVer =
|
||||
((majorVer & 0xF) << 28) |
|
||||
((minorVer & 0xF) << 24) |
|
||||
(buildVer << 8) |
|
||||
hotfixVer;
|
||||
|
||||
// Now onto the minimum versions, this works much the same
|
||||
minimumBuildVerStr = strtok(NULL, ".");
|
||||
|
||||
if(!minimumBuildVerStr)
|
||||
{ goto cleanup_names_invalid; } // No more tokens
|
||||
|
||||
if(strlen(names[i]) + 1 + strlen(targetBuildVerStr) + 1 + strlen(targetHotfixVerStr)
|
||||
+ 1 + strlen(minimumBuildVerStr) == oldNameLen)
|
||||
{ goto cleanup_names_invalid; } // Encountered null terminator instead of '.'
|
||||
|
||||
buildVer = (uint16_t)strtoul(minimumBuildVerStr, &strtoulRet, 10);
|
||||
|
||||
if(*strtoulRet != 0 || strtoulRet == minimumBuildVerStr)
|
||||
{ goto cleanup_names_invalid; } // Encountered a non-number, or string was empty
|
||||
|
||||
minimumHotfixVerStr = strtok(NULL, "\0");
|
||||
|
||||
if(!minimumHotfixVerStr)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
hotfixVer = (uint8_t)strtoul(minimumHotfixVerStr, &strtoulRet, 10);
|
||||
|
||||
if(*strtoulRet != 0 || strtoulRet == minimumHotfixVerStr)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
// Now pack these into the minimum version bitfield
|
||||
importTables[i].minimumVer =
|
||||
((majorVer & 0xF) << 28) |
|
||||
((minorVer & 0xF) << 24) |
|
||||
(buildVer << 8) |
|
||||
hotfixVer;
|
||||
|
||||
// Hardcode a currently unknown value. TODO: find out how this is calculated.
|
||||
if(strcmp(names[i], "xboxkrnl.exe") == 0)
|
||||
{ importTables[i].unknown = 0x45DC17E0; }
|
||||
else if(strcmp(names[i], "xam.xex") == 0)
|
||||
{ importTables[i].unknown = 0xFCA15C76; }
|
||||
else if(strcmp(names[i], "xbdm.xex") == 0)
|
||||
{ importTables[i].unknown = 0xECEB8109; }
|
||||
else
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
// Determine the number of addresses
|
||||
importTables[i].addressCount = peImportInfo->tables[i].importCount;
|
||||
|
||||
// Allocate enough memory for the addresses
|
||||
importTables[i].addresses = calloc(importTables[i].addressCount, sizeof(uint32_t));
|
||||
|
||||
if(!importTables[i].addresses)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer
|
||||
uint16_t currentAddr = 0;
|
||||
|
||||
// Populate the addresses
|
||||
for(uint16_t j = 0; j < peImportInfo->tables[i].importCount; j++)
|
||||
{
|
||||
if(currentAddr >= importTables[i].addressCount)
|
||||
{ goto cleanup_names_invalid; }
|
||||
|
||||
addresses[currentAddr++] = peImportInfo->tables[i].imports[j].iatAddr;
|
||||
}
|
||||
|
||||
// Determine the total size, in bytes, of the current table (- sizeof(void*) to exclude address to addresses at the end)
|
||||
importTables[i].size = (sizeof(struct importTable) - sizeof(void *) + (importTables[i].addressCount *sizeof(uint32_t)));
|
||||
importLibraries->size += importTables[i].size;
|
||||
|
||||
// Init sha1 hash
|
||||
struct sha1_ctx shaContext;
|
||||
memset(&shaContext, 0, sizeof(shaContext));
|
||||
sha1_init(&shaContext);
|
||||
|
||||
// On little endian this ensures the byteswapped address count doesn't cause any trouble when using it.
|
||||
// On big endian it's pointless but here so the code isn't too complex with differences between endianness.
|
||||
uint16_t addressCount = importTables[i].addressCount;
|
||||
|
||||
// If we're on a little endian system, swap everything into big endian for hashing
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
importTables[i].size = __builtin_bswap32(importTables[i].size);
|
||||
importTables[i].unknown = __builtin_bswap32(importTables[i].unknown);
|
||||
importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer);
|
||||
importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer);
|
||||
importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount);
|
||||
|
||||
// Byteswap the addresses
|
||||
for(uint16_t j = 0; j < addressCount; j++)
|
||||
{ addresses[j] = __builtin_bswap32(addresses[j]); }
|
||||
|
||||
#endif
|
||||
// - sizeof(void*) to exclude the address to the addresses at the end (not part of the XEX format)
|
||||
// +/- sizeof(uint32_t) to exclude table size from hash
|
||||
sha1_update(&shaContext, sizeof(struct importTable) - sizeof(void *) - sizeof(uint32_t), (void *)&importTables[i] + sizeof(uint32_t));
|
||||
sha1_update(&shaContext, addressCount *sizeof(uint32_t), (void *)addresses);
|
||||
|
||||
// If we're on a little endian system, swap everything back into little endian
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
importTables[i].size = __builtin_bswap32(importTables[i].size);
|
||||
importTables[i].unknown = __builtin_bswap32(importTables[i].unknown);
|
||||
importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer);
|
||||
importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer);
|
||||
importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount);
|
||||
|
||||
// Byteswap the addresses
|
||||
for(uint16_t j = 0; j < addressCount; j++)
|
||||
{ addresses[j] = __builtin_bswap32(addresses[j]); }
|
||||
|
||||
#endif
|
||||
|
||||
sha1_digest(&shaContext, 0x14, i != 0 ? importTables[i - 1].sha1 : secInfoHeader->importTableSha1);
|
||||
}
|
||||
|
||||
// Allocate offset table
|
||||
uint32_t *nameOffsets = calloc(importLibraries->tableCount, sizeof(uint32_t));
|
||||
|
||||
if(!nameOffsets)
|
||||
{ goto cleanup_names; }
|
||||
|
||||
for(uint32_t i = 0; i < importLibraries->tableCount; i++)
|
||||
{
|
||||
nameOffsets[i] = importLibraries->nameTableSize;
|
||||
importLibraries->nameTableSize += getNextAligned(strlen(names[i]) + 1, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
importLibraries->size += importLibraries->nameTableSize;
|
||||
importLibraries->nameTable = calloc(importLibraries->nameTableSize, sizeof(char));
|
||||
|
||||
if(!importLibraries->nameTable)
|
||||
{ goto cleanup_offsets; }
|
||||
|
||||
// Use this to avoid dereferencing an unaligned pointer
|
||||
char *nameTable = importLibraries->nameTable;
|
||||
|
||||
// Populate the name table
|
||||
for(uint32_t i = 0; i < importLibraries->tableCount; i++)
|
||||
{ strcpy(&(nameTable[nameOffsets[i]]), names[i]); }
|
||||
|
||||
nullAndFree((void **)&nameOffsets);
|
||||
nullAndFree((void **)&names);
|
||||
return SUCCESS;
|
||||
|
||||
cleanup_offsets:
|
||||
nullAndFree((void **)&nameOffsets);
|
||||
cleanup_names:
|
||||
|
||||
for(uint32_t i = 0; i < importLibraries->tableCount; i++)
|
||||
{ nullAndFree((void **) & (importTables[i].addresses)); }
|
||||
|
||||
cleanup_names_invalid:
|
||||
nullAndFree((void **)&names);
|
||||
cleanup_tables:
|
||||
nullAndFree((void **) & (importLibraries->importTables));
|
||||
importLibraries->importTables = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// STUB. TODO: Dynamically select, and/or allow user to select, flags
|
||||
void setSysFlags(uint32_t *flags)
|
||||
{
|
||||
if(flags == NULL)
|
||||
{ return; }
|
||||
|
||||
*flags = XEX_SYS_GAMEPAD_DISCONNECT |
|
||||
XEX_SYS_INSECURE_SOCKETS |
|
||||
XEX_SYS_XAM_HOOKS |
|
||||
|
@ -294,61 +48,27 @@ void setSysFlags(uint32_t *flags)
|
|||
|
||||
int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, struct optHeaderEntries *optHeaderEntries, struct optHeaders *optHeaders)
|
||||
{
|
||||
bool importsPresent = (peData->peImportInfo.totalImportCount > 0) ? true : false;
|
||||
|
||||
// TODO: Dynamically select optional headers to use, instead of hard-coding
|
||||
optHeaderEntries->count = 4;
|
||||
optHeaderEntries->optHeaderEntry = calloc(5, sizeof(struct optHeaderEntry));
|
||||
if(optHeaderEntries->optHeaderEntry == NULL) {return ERR_OUT_OF_MEM;}
|
||||
|
||||
if(importsPresent)
|
||||
{ optHeaderEntries->count++; }
|
||||
|
||||
optHeaderEntries->optHeaderEntry = calloc(optHeaderEntries->count, sizeof(struct optHeaderEntry));
|
||||
|
||||
if(optHeaderEntries->optHeaderEntry == NULL)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
uint32_t currentHeader = 0;
|
||||
|
||||
// NOTE: Make sure that these headers are handled IN ORDER OF ID. The loader will reject the XEX if they are not.
|
||||
|
||||
// Basefile format (0x003FF)
|
||||
// First optional header (basefile format)
|
||||
setBasefileFormat(&(optHeaders->basefileFormat), secInfoHeader);
|
||||
optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_BASEFILE_FORMAT;
|
||||
currentHeader++;
|
||||
optHeaderEntries->optHeaderEntry[0].id = XEX_OPT_ID_BASEFILE_FORMAT;
|
||||
|
||||
// Entrypoint (0x10100)
|
||||
optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_ENTRYPOINT;
|
||||
optHeaderEntries->optHeaderEntry[currentHeader].dataOrOffset = secInfoHeader->baseAddr + peData->entryPoint;
|
||||
currentHeader++;
|
||||
// Second optional header (entrypoint)
|
||||
optHeaderEntries->optHeaderEntry[1].id = XEX_OPT_ID_ENTRYPOINT;
|
||||
optHeaderEntries->optHeaderEntry[1].dataOrOffset = secInfoHeader->baseAddr + peData->entryPoint;
|
||||
|
||||
// Import libraries (0x103FF)
|
||||
if(importsPresent)
|
||||
{
|
||||
optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_IMPORT_LIBS;
|
||||
int ret = setImportLibsInfo(&(optHeaders->importLibraries), &(peData->peImportInfo), secInfoHeader);
|
||||
// Third optional header (import libs)
|
||||
//optHeaderEntries->optHeaderEntry[2].id = XEX_OPT_ID_IMPORT_LIBS;
|
||||
|
||||
if(ret != SUCCESS)
|
||||
{ return ret; }
|
||||
|
||||
currentHeader++;
|
||||
}
|
||||
|
||||
// TLS info (0x20104)
|
||||
optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_TLS_INFO;
|
||||
// Fourth optional header (tls info)
|
||||
optHeaderEntries->optHeaderEntry[2].id = XEX_OPT_ID_TLS_INFO;
|
||||
setTLSInfo(&(optHeaders->tlsInfo));
|
||||
currentHeader++;
|
||||
|
||||
// System flags (0x30000)
|
||||
optHeaderEntries->optHeaderEntry[currentHeader].id = XEX_OPT_ID_SYS_FLAGS;
|
||||
|
||||
// We're using the name of the first element of the struct for clarity,
|
||||
// it can never be an unaligned access, so ignore that warning.
|
||||
// Also works for Clang.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
|
||||
setSysFlags(&(optHeaderEntries->optHeaderEntry[currentHeader].dataOrOffset));
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
//currentHeader++;
|
||||
|
||||
return SUCCESS;
|
||||
// Fifth optional header (system flags)
|
||||
optHeaderEntries->optHeaderEntry[3].id = XEX_OPT_ID_SYS_FLAGS;
|
||||
setSysFlags(&(optHeaderEntries->optHeaderEntry[3].dataOrOffset));
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "../common/crypto.h"
|
||||
#include "../common/datastorage.h"
|
||||
|
||||
int setOptHeaders(struct secInfoHeader *secInfoHeader, struct peData *peData, struct optHeaderEntries *optHeaderEntries, struct optHeaders *optHeaders);
|
||||
|
|
|
@ -23,9 +23,13 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint3
|
|||
uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount;
|
||||
uint32_t currentOffset = page * pageSize;
|
||||
|
||||
for(int32_t i = peData->sections.count - 1; i >= 0; i--)
|
||||
if(currentOffset >= peData->sections.section[i].rva)
|
||||
{ return peData->sections.section[i].permFlag; }
|
||||
for(int i = peData->sections.count - 1; i >= 0; i--)
|
||||
{
|
||||
if(currentOffset >= peData->sections.sectionPerms[i].rva)
|
||||
{
|
||||
return peData->sections.sectionPerms[i].permFlag;
|
||||
}
|
||||
}
|
||||
|
||||
return XEX_SECTION_RODATA | 0b10000; // We're in the PE header, so RODATA
|
||||
}
|
||||
|
@ -33,57 +37,44 @@ uint8_t getRwx(struct secInfoHeader *secInfoHeader, struct peData *peData, uint3
|
|||
int setPageDescriptors(FILE *pe, struct peData *peData, struct secInfoHeader *secInfoHeader)
|
||||
{
|
||||
uint32_t pageSize = secInfoHeader->peSize / secInfoHeader->pageDescCount;
|
||||
|
||||
secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor));
|
||||
|
||||
if(!secInfoHeader->descriptors)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
struct pageDescriptor *descriptors = secInfoHeader->descriptors; // So we don't dereference an unaligned pointer
|
||||
secInfoHeader->descriptors = calloc(secInfoHeader->pageDescCount, sizeof(struct pageDescriptor)); // The free() for this is called after written to XEX
|
||||
if(secInfoHeader->descriptors == NULL) {return ERR_OUT_OF_MEM;}
|
||||
|
||||
// Setting size/info data and calculating hashes for page descriptors
|
||||
for(int64_t i = secInfoHeader->pageDescCount - 1; i >= 0; i--)
|
||||
{
|
||||
// Get page type (rwx)
|
||||
descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i);
|
||||
secInfoHeader->descriptors[i].sizeAndInfo = getRwx(secInfoHeader, peData, i);
|
||||
|
||||
// Init sha1 hash
|
||||
struct sha1_ctx shaContext;
|
||||
sha1_init(&shaContext);
|
||||
|
||||
if(fseek(pe, i *pageSize, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
uint8_t *page = malloc(pageSize);
|
||||
|
||||
if(!page)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
if(fread(page, 1, pageSize, pe) != pageSize)
|
||||
{
|
||||
nullAndFree((void **)&page);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
fseek(pe, i * pageSize, SEEK_SET);
|
||||
uint8_t page[pageSize];
|
||||
fread(page, sizeof(uint8_t), pageSize, pe);
|
||||
|
||||
// For little endian systems, swap into big endian for hashing, then back (to keep struct endianness consistent)
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo);
|
||||
secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo);
|
||||
#endif
|
||||
|
||||
sha1_update(&shaContext, pageSize, page);
|
||||
sha1_update(&shaContext, 0x18, (uint8_t *)&descriptors[i]);
|
||||
sha1_update(&shaContext, 0x18, (uint8_t*)&secInfoHeader->descriptors[i]);
|
||||
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo);
|
||||
secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo);
|
||||
#endif
|
||||
|
||||
if(i != 0)
|
||||
{ sha1_digest(&shaContext, 0x14, descriptors[i - 1].sha1); }
|
||||
{
|
||||
sha1_digest(&shaContext, 0x14, secInfoHeader->descriptors[i - 1].sha1);
|
||||
}
|
||||
else
|
||||
{ sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1); }
|
||||
|
||||
nullAndFree((void **)&page);
|
||||
{
|
||||
sha1_digest(&shaContext, 0x14, secInfoHeader->imageSha1);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
free(peData->sections.sectionPerms); // Alloc'd in getdata
|
||||
}
|
||||
|
|
|
@ -18,15 +18,14 @@
|
|||
|
||||
#include "populateheaders.h"
|
||||
|
||||
int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct peData *peData)
|
||||
void setXEXHeader(struct xexHeader *xexHeader, struct peData *peData)
|
||||
{
|
||||
// Writing data into XEX header.
|
||||
strncpy(xexHeader->magic, "XEX2", sizeof(char) * 4); // Magic
|
||||
strcpy(xexHeader->magic, "XEX2"); // Magic
|
||||
|
||||
// Module flags (type of executable)
|
||||
// Not sure if this is correct, for DLLs specifically the type overrides should probably be used instead
|
||||
if(xexHeader->moduleFlags == 0)
|
||||
{
|
||||
xexHeader->moduleFlags = 0;
|
||||
|
||||
if(peData->characteristics & PE_CHAR_FLAG_DLL)
|
||||
{
|
||||
xexHeader->moduleFlags |= XEX_MOD_FLAG_DLL; // The executable is a DLL
|
||||
|
@ -40,32 +39,28 @@ int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeader
|
|||
{
|
||||
xexHeader->moduleFlags |= XEX_MOD_FLAG_EXPORTS; // The executable exports functions
|
||||
}
|
||||
|
||||
xexHeader->optHeaderCount = 0x4; // Hard-coding until more optional headers supported, then maybe it can be determined dynamically.
|
||||
}
|
||||
|
||||
xexHeader->optHeaderCount = optHeaderEntries->count;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData)
|
||||
void setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData)
|
||||
{
|
||||
// Writing data into security info header (much of this is derived from info in PE)
|
||||
secInfoHeader->peSize = peData->size;
|
||||
|
||||
// Setting signature (just a SynthXEX version identifier)
|
||||
strcpy(secInfoHeader->signature, SYNTHXEX_VERSION_STRING);
|
||||
// Setting signature. Clearing first to ensure no memory contents are written to file, then adding identifier.
|
||||
memset(secInfoHeader->signature, 0, sizeof(secInfoHeader->signature));
|
||||
strcpy(secInfoHeader->signature, VERSION_STRING);
|
||||
|
||||
secInfoHeader->imageInfoSize = 0x174; // Image info size is always 0x174
|
||||
secInfoHeader->imageFlags = (peData->pageSize == 0x1000 ? XEX_IMG_FLAG_4KIB_PAGES : 0) | XEX_IMG_FLAG_REGION_FREE; // If page size is 4KiB (small pages), set that flag
|
||||
secInfoHeader->baseAddr = peData->baseAddr;
|
||||
//memset(secInfoHeader->mediaID, 0, sizeof(secInfoHeader->mediaID)); // Null media ID (no longer need to use memset, as we use calloc for these structs now)
|
||||
//memset(secInfoHeader->aesKey, 0, sizeof(secInfoHeader->aesKey)); // No encryption, null AES key
|
||||
memset(secInfoHeader->mediaID, 0, sizeof(secInfoHeader->mediaID)); // Null media ID
|
||||
memset(secInfoHeader->aesKey, 0, sizeof(secInfoHeader->aesKey)); // No encryption, null AES key
|
||||
//secInfoHeader->exportTableAddr = TEMPEXPORTADDR;
|
||||
secInfoHeader->exportTableAddr = 0;
|
||||
secInfoHeader->gameRegion = XEX_REG_FLAG_REGION_FREE;
|
||||
secInfoHeader->mediaTypes = 0xFFFFFFFF; // All flags set, can load from any type.
|
||||
secInfoHeader->pageDescCount = secInfoHeader->peSize / peData->pageSize; // Number of page descriptors following security info (same number of pages)
|
||||
secInfoHeader->headerSize = (secInfoHeader->pageDescCount * sizeof(struct pageDescriptor)) + (sizeof(struct secInfoHeader) - sizeof(void*)); // Page descriptor total size + length of rest of secinfo header (subtraction of sizeof void* is to remove pointer at end of struct from calculation)
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -21,5 +21,5 @@
|
|||
#include "../common/common.h"
|
||||
#include "../common/datastorage.h"
|
||||
|
||||
int setXEXHeader(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntries, struct peData *peData);
|
||||
int setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData);
|
||||
void setXEXHeader(struct xexHeader *xexHeader, struct peData *peData);
|
||||
void setSecInfoHeader(struct secInfoHeader *secInfoHeader, struct peData *peData);
|
||||
|
|
|
@ -18,94 +18,71 @@
|
|||
|
||||
#include "headerhash.h"
|
||||
|
||||
// TEMPORARY, TO BE REPLACED ELSEWHERE WHEN IMPORT HEADER IS IMPLEMENTED
|
||||
void setImportsSha1(FILE *xex)
|
||||
{
|
||||
fseek(xex, 0x0, SEEK_SET);
|
||||
while(get32BitFromXEX(xex) != 0x103FF){}
|
||||
fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_SET);
|
||||
|
||||
uint8_t importCount[4];
|
||||
fseek(xex, 0x4, SEEK_CUR);
|
||||
fread(importCount, sizeof(uint8_t), 4, xex);
|
||||
fseek(xex, -0x8, SEEK_CUR);
|
||||
|
||||
fseek(xex, get32BitFromXEX(xex) + 0x4, SEEK_CUR);
|
||||
uint32_t size = get32BitFromXEX(xex) - 0x4;
|
||||
|
||||
uint8_t data[size];
|
||||
fread(data, sizeof(uint8_t), size, xex);
|
||||
|
||||
struct sha1_ctx shaContext;
|
||||
sha1_init(&shaContext);
|
||||
sha1_update(&shaContext, size, data);
|
||||
|
||||
uint8_t sha1[20];
|
||||
sha1_digest(&shaContext, 20, sha1);
|
||||
|
||||
fseek(xex, 0x10, SEEK_SET);
|
||||
fseek(xex, get32BitFromXEX(xex) + 0x128, SEEK_SET);
|
||||
fwrite(importCount, sizeof(uint8_t), 4, xex);
|
||||
fwrite(sha1, sizeof(uint8_t), 20, xex);
|
||||
}
|
||||
|
||||
// So, it would probably be more sensible to endian-swap all of the data back and determine which structure is where
|
||||
// to determine the hash, but reading the file we just created is easier.
|
||||
int setHeaderSha1(FILE *xex)
|
||||
void setHeaderSha1(FILE *xex)
|
||||
{
|
||||
// Get basefile offset
|
||||
if(fseek(xex, 0x8, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
fseek(xex, 0x8, SEEK_SET); // Basefile offset
|
||||
uint32_t basefileOffset = get32BitFromXEX(xex);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
// Get secinfo offset
|
||||
if(fseek(xex, 0x10, SEEK_SET) != 0)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
fseek(xex, 0x10, SEEK_SET); // Secinfo offset
|
||||
uint32_t secInfoOffset = get32BitFromXEX(xex);
|
||||
|
||||
if(errno != SUCCESS)
|
||||
{ return errno; }
|
||||
|
||||
uint32_t endOfImageInfo = secInfoOffset + 0x8 + 0x174; // 0x8 == image info offset in security info, 0x174 == length of that
|
||||
uint32_t remainingSize = basefileOffset - endOfImageInfo; // How much data is between end of image info and basefile (we hash that too)
|
||||
|
||||
// Init sha1 hash
|
||||
struct sha1_ctx shaContext;
|
||||
memset(&shaContext, 0, sizeof(shaContext));
|
||||
sha1_init(&shaContext);
|
||||
|
||||
// Hash first part (remainder of headers is done first, then the start)
|
||||
uint8_t *remainderOfHeaders = malloc(remainingSize);
|
||||
|
||||
if(!remainderOfHeaders)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
memset(remainderOfHeaders, 0, remainingSize);
|
||||
|
||||
if(fseek(xex, endOfImageInfo, SEEK_SET) != 0)
|
||||
{
|
||||
nullAndFree((void **)&remainderOfHeaders);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
if(fread(remainderOfHeaders, 1, remainingSize, xex) != remainingSize)
|
||||
{
|
||||
nullAndFree((void **)&remainderOfHeaders);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
uint8_t remainderOfHeaders[remainingSize];
|
||||
fseek(xex, endOfImageInfo, SEEK_SET);
|
||||
fread(remainderOfHeaders, sizeof(uint8_t), remainingSize, xex);
|
||||
sha1_update(&shaContext, remainingSize, remainderOfHeaders);
|
||||
nullAndFree((void **)&remainderOfHeaders);
|
||||
|
||||
// Hash from start up to image info (0x8 into security header)
|
||||
uint32_t headersLen = secInfoOffset + 0x8;
|
||||
uint8_t *headersStart = malloc(headersLen);
|
||||
uint8_t headersStart[secInfoOffset + 0x8]; // Hash from start up to image info (0x8 into security header)
|
||||
fseek(xex, 0, SEEK_SET);
|
||||
|
||||
if(!headersStart)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
|
||||
memset(headersStart, 0, headersLen);
|
||||
|
||||
if(fseek(xex, 0, SEEK_SET) != 0)
|
||||
{
|
||||
nullAndFree((void **)&headersStart);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
if(fread(headersStart, 1, headersLen, xex) != headersLen)
|
||||
{
|
||||
nullAndFree((void **)&headersStart);
|
||||
return ERR_FILE_READ;
|
||||
}
|
||||
|
||||
sha1_update(&shaContext, headersLen, headersStart);
|
||||
nullAndFree((void **)&headersStart);
|
||||
fread(headersStart, sizeof(uint8_t), secInfoOffset + 0x8, xex);
|
||||
sha1_update(&shaContext, secInfoOffset + 0x8, headersStart);
|
||||
|
||||
// Get final hash
|
||||
uint8_t headerHash[20];
|
||||
memset(headerHash, 0, sizeof(headerHash));
|
||||
sha1_digest(&shaContext, 20, headerHash);
|
||||
|
||||
// Finally, write it out
|
||||
if(fseek(xex, secInfoOffset + 0x164, SEEK_SET) != 0) // 0x164 == offset in secinfo of header hash
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
if(fwrite(headerHash, 1, 20, xex) != 20)
|
||||
{ return ERR_FILE_READ; }
|
||||
|
||||
return SUCCESS;
|
||||
fseek(xex, secInfoOffset + 0x164, SEEK_SET); // 0x164 == offset in secinfo of header hash
|
||||
fwrite(headerHash, sizeof(uint8_t), 20, xex);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// Copyright (c) 2024-25 Aiden Isik
|
||||
// Copyright (c) 2024 Aiden Isik
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -22,4 +22,5 @@
|
|||
#include "../common/crypto.h"
|
||||
#include "../common/datastorage.h"
|
||||
|
||||
int setHeaderSha1(FILE *xex);
|
||||
void setImportsSha1(FILE *xex);
|
||||
void setHeaderSha1(FILE *xex);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// This file is part of SynthXEX, one component of the
|
||||
// FreeChainXenon development toolchain
|
||||
//
|
||||
// Copyright (c) 2024-25 Aiden Isik
|
||||
// Copyright (c) 2024 Aiden Isik
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -35,66 +35,54 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr
|
|||
|
||||
// Optional header entries
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
|
||||
// Endian swap opt header entries
|
||||
for(uint32_t i = 0; i < optHeaderEntries->count; i++)
|
||||
{
|
||||
optHeaderEntries->optHeaderEntry[i].id = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].id);
|
||||
optHeaderEntries->optHeaderEntry[i].dataOrOffset = __builtin_bswap32(optHeaderEntries->optHeaderEntry[i].dataOrOffset);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
fseek(xex, offsets->optHeaderEntries, SEEK_SET);
|
||||
|
||||
for(int i = 0; i < optHeaderEntries->count; i++)
|
||||
{ fwrite(&(optHeaderEntries->optHeaderEntry[i]), sizeof(uint8_t), sizeof(struct optHeaderEntry), xex); }
|
||||
{
|
||||
fwrite(&(optHeaderEntries->optHeaderEntry[i]), sizeof(uint8_t), sizeof(struct optHeaderEntry), xex);
|
||||
}
|
||||
|
||||
free(optHeaderEntries->optHeaderEntry); // Alloc'd in setdata (optheaders). Now we're done with it.
|
||||
|
||||
// Page descriptors
|
||||
fseek(xex, offsets->secInfoHeader + sizeof(struct secInfoHeader) - sizeof(void*), SEEK_SET);
|
||||
|
||||
// So we don't try to dereference an unaligned pointer
|
||||
struct pageDescriptor *descriptors = secInfoHeader->descriptors;
|
||||
|
||||
for(int i = 0; i < secInfoHeader->pageDescCount; i++)
|
||||
{
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
descriptors[i].sizeAndInfo = __builtin_bswap32(descriptors[i].sizeAndInfo);
|
||||
secInfoHeader->descriptors[i].sizeAndInfo = __builtin_bswap32(secInfoHeader->descriptors[i].sizeAndInfo);
|
||||
#endif
|
||||
|
||||
// Writing out current descriptor...
|
||||
fwrite(&(descriptors[i].sizeAndInfo), sizeof(uint32_t), 0x1, xex);
|
||||
fwrite(descriptors[i].sha1, sizeof(uint8_t), 0x14, xex);
|
||||
fwrite(&(secInfoHeader->descriptors[i].sizeAndInfo), sizeof(uint32_t), 0x1, xex);
|
||||
fwrite(secInfoHeader->descriptors[i].sha1, sizeof(uint8_t), 0x14, xex);
|
||||
}
|
||||
|
||||
free(secInfoHeader->descriptors); // calloc'd elsewhere, freeing now
|
||||
|
||||
// Basefile
|
||||
fseek(pe, 0x0, SEEK_SET);
|
||||
fseek(xex, offsets->basefile, SEEK_SET);
|
||||
|
||||
uint16_t readBufSize = 0x1000; // Reading in 4KiB at a time to avoid excessive memory usage
|
||||
uint8_t *buffer = malloc(readBufSize * sizeof(uint8_t));
|
||||
|
||||
if(buffer == NULL)
|
||||
{ return ERR_OUT_OF_MEM; }
|
||||
if(buffer == NULL) {return ERR_OUT_OF_MEM;}
|
||||
|
||||
for(uint32_t i = 0; i < secInfoHeader->peSize; i += readBufSize)
|
||||
{
|
||||
size_t bytesToRead = (secInfoHeader->peSize - i < readBufSize)
|
||||
? secInfoHeader->peSize - i
|
||||
: readBufSize;
|
||||
|
||||
size_t readRet = fread(buffer, sizeof(uint8_t), bytesToRead, pe);
|
||||
|
||||
if(readRet != bytesToRead)
|
||||
{ break; }
|
||||
|
||||
size_t writeRet = fwrite(buffer, sizeof(uint8_t), readRet, xex);
|
||||
|
||||
if(writeRet != readRet)
|
||||
{ break; }
|
||||
fread(buffer, sizeof(uint8_t), readBufSize, pe);
|
||||
fwrite(buffer, sizeof(uint8_t), readBufSize, xex);
|
||||
}
|
||||
|
||||
nullAndFree((void **)&buffer);
|
||||
free(buffer);
|
||||
|
||||
// Security Info
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
|
@ -136,55 +124,14 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr
|
|||
if(optHeaders->importLibraries.size != 0)
|
||||
{
|
||||
fseek(xex, offsets->optHeaders[currentHeader], SEEK_SET);
|
||||
|
||||
// Write the main header first
|
||||
|
||||
// Use these to avoid dereferencing an unaligned pointer
|
||||
char *nameTable = optHeaders->importLibraries.nameTable;
|
||||
|
||||
// Save the values we need to use before byteswapping
|
||||
uint32_t nameTableSize = optHeaders->importLibraries.nameTableSize;
|
||||
uint32_t tableCount = optHeaders->importLibraries.tableCount;
|
||||
uint32_t importLibsSize = optHeaders->importLibraries.size; // Need to use this value, so save it before we endian-swap
|
||||
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
optHeaders->importLibraries.size = __builtin_bswap32(optHeaders->importLibraries.size);
|
||||
optHeaders->importLibraries.nameTableSize = __builtin_bswap32(optHeaders->importLibraries.nameTableSize);
|
||||
optHeaders->importLibraries.tableCount = __builtin_bswap32(optHeaders->importLibraries.tableCount);
|
||||
#endif
|
||||
|
||||
fwrite(&(optHeaders->importLibraries), sizeof(uint8_t), sizeof(struct importLibraries) - (2 * sizeof(void *)), xex);
|
||||
fwrite(nameTable, sizeof(uint8_t), nameTableSize, xex);
|
||||
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
// Restore the table count (we require it to free the import libraries struct later)
|
||||
optHeaders->importLibraries.tableCount = tableCount;
|
||||
#endif
|
||||
|
||||
// Now write each import table
|
||||
// Use this to avoid dereferencing an unaligned pointer
|
||||
struct importTable *importTables = optHeaders->importLibraries.importTables;
|
||||
|
||||
for(uint32_t i = 0; i < tableCount; i++)
|
||||
{
|
||||
uint32_t *addresses = importTables[i].addresses; // Use this to avoid dereferencing an unaligned pointer
|
||||
uint16_t addressCount = importTables[i].addressCount;
|
||||
|
||||
#ifdef LITTLE_ENDIAN_SYSTEM
|
||||
importTables[i].size = __builtin_bswap32(importTables[i].size);
|
||||
importTables[i].unknown = __builtin_bswap32(importTables[i].unknown);
|
||||
importTables[i].targetVer = __builtin_bswap32(importTables[i].targetVer);
|
||||
importTables[i].minimumVer = __builtin_bswap32(importTables[i].minimumVer);
|
||||
importTables[i].addressCount = __builtin_bswap16(importTables[i].addressCount);
|
||||
|
||||
for(uint16_t j = 0; j < addressCount; j++)
|
||||
{ addresses[j] = __builtin_bswap32(addresses[j]); }
|
||||
|
||||
#endif
|
||||
|
||||
fwrite(&(importTables[i]), sizeof(uint8_t), sizeof(struct importTable) - sizeof(void *), xex);
|
||||
fwrite(addresses, sizeof(uint32_t), addressCount, xex);
|
||||
}
|
||||
|
||||
fwrite(&(optHeaders->importLibraries.size), sizeof(uint8_t), 0x4, xex);
|
||||
fwrite(optHeaders->importLibraries.data, sizeof(uint8_t), importLibsSize - 0x4, xex);
|
||||
currentHeader++;
|
||||
}
|
||||
|
||||
|
@ -202,5 +149,6 @@ int writeXEX(struct xexHeader *xexHeader, struct optHeaderEntries *optHeaderEntr
|
|||
fwrite(&(optHeaders->tlsInfo), sizeof(uint8_t), sizeof(struct tlsInfo), xex);
|
||||
}
|
||||
|
||||
free(offsets->optHeaders); // Alloc'd in placer.
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
22
synthxex.nix
22
synthxex.nix
|
@ -1,22 +0,0 @@
|
|||
{ stdenv
|
||||
, cmake
|
||||
, lib
|
||||
, ninja
|
||||
, pkg-config
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "synthxex";
|
||||
allowSubstitutes = false;
|
||||
src = ./.;
|
||||
nativeBuildInputs = [ cmake pkg-config ninja ];
|
||||
|
||||
postUnpack = ''
|
||||
chmod -R +w $sourceRoot
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp -v synthxex $out/bin/synthxex
|
||||
'';
|
||||
}
|
104
synthxex.scm
104
synthxex.scm
|
@ -1,104 +0,0 @@
|
|||
;;; This file is the git repo bundled package definition of SynthXEX for GNU Guix.
|
||||
;;; To build natively (reproducibly), run "guix time-machine -C channels.scm -- build -f synthxex.scm" in the project root.
|
||||
;;; To cross compile (reproducibly), run "guix time-machine -C channels.scm -- build -f synthxex.scm --target=<target triplet>" in the project root.
|
||||
;;; To install, swap "build" for "package" in the above commands
|
||||
;;;
|
||||
;;; Copyright (c) 2025 Aiden Isik
|
||||
;;;
|
||||
;;; This program is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU Affero General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU Affero General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU Affero General Public License
|
||||
;;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
;;; Specify the modules we need for this package definition
|
||||
(define-module (synthxex)
|
||||
#:use-module (guix gexp)
|
||||
#:use-module (guix packages)
|
||||
#:use-module (guix licenses)
|
||||
#:use-module (guix build-system cmake)
|
||||
#:use-module (git)
|
||||
#:use-module (gnu packages version-control)
|
||||
#:use-module (ice-9 regex))
|
||||
|
||||
|
||||
;;; Hardcoded fallback version in case we can't rely on git
|
||||
(define synthxex-fallback-version "v0.0.5")
|
||||
|
||||
|
||||
;;; Determine the version of SynthXEX we are building
|
||||
;;; Returns: version string of SynthXEX being built
|
||||
(define (get-synthxex-version)
|
||||
(cond
|
||||
((< (libgit2-init!) 0)
|
||||
(display "Warning: failed to initialise libgit2. Using fallback version number.\n")
|
||||
synthxex-fallback-version)
|
||||
|
||||
(else
|
||||
(let ((synthxex-dir (dirname (current-filename))))
|
||||
(cond
|
||||
((eq? (openable-repository? synthxex-dir) #f)
|
||||
(display "Note: not a git repository. Using fallback version number.\n")
|
||||
(libgit2-shutdown!)
|
||||
synthxex-fallback-version)
|
||||
|
||||
(else
|
||||
(let ((synthxex-repo (repository-open synthxex-dir)))
|
||||
(cond
|
||||
((repository-empty? synthxex-repo)
|
||||
(display "Warning: git repository is empty. Using fallback version number.\n")
|
||||
(libgit2-shutdown!)
|
||||
synthxex-fallback-version)
|
||||
|
||||
((repository-shallow? synthxex-repo)
|
||||
(display "Warning: git repository is shalllow. Using fallback version number.\n")
|
||||
(libgit2-shutdown!)
|
||||
synthxex-fallback-version)
|
||||
|
||||
((repository-bare? synthxex-repo)
|
||||
(display "Warning: git repository is bare. Using fallback version number.\n")
|
||||
(libgit2-shutdown!)
|
||||
synthxex-fallback-version)
|
||||
|
||||
(else
|
||||
(let* ((synthxex-desc-opts (make-describe-options #:strategy 'tags)) ; Equivalent to git describe --tags
|
||||
(synthxex-description (describe-workdir synthxex-repo synthxex-desc-opts))
|
||||
(synthxex-format-opts (make-describe-format-options #:dirty-suffix "-dirty")) ; Equivalent to git describe --dirty
|
||||
(synthxex-version-str (describe-format synthxex-description synthxex-format-opts)))
|
||||
(libgit2-shutdown!) ; Whatever the result of the regex comparison, we're done with git. Clean up here.
|
||||
|
||||
(cond ; Check if the version string returned is valid. If it isn't, use the fallback.
|
||||
((eq? (string-match "^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+-g[0-9a-f]+(-dirty)?)?$" synthxex-version-str) #f)
|
||||
(display "Warning: version string is invalid. Using fallback version number.\n")
|
||||
synthxex-fallback-version)
|
||||
|
||||
(else
|
||||
synthxex-version-str))))))))))))
|
||||
|
||||
|
||||
;;; Define the SynthXEX package
|
||||
(define-public synthxex
|
||||
(package
|
||||
(name "synthxex")
|
||||
(version (get-synthxex-version))
|
||||
(source (local-file (dirname (current-filename)) #:recursive? #t))
|
||||
(build-system cmake-build-system)
|
||||
(arguments
|
||||
'(#:build-type "Debug" ; This is the version of the package definition bundled in the git repo, so we should build a debug version
|
||||
#:tests? #f)) ; We don't have a test suite
|
||||
(native-inputs
|
||||
(list git)) ; Needed for version number generation by cmake
|
||||
(synopsis "XEX2 generator for the Xbox 360 games console")
|
||||
(description "SynthXEX is an XEX2 generator, the final component of development toolchains for the Xbox 360 games console.")
|
||||
(home-page "https://git.aidenisik.scot/FreeChainXenon/SynthXEX")
|
||||
(license agpl3+)))
|
||||
|
||||
synthxex
|
Loading…
Add table
Add a link
Reference in a new issue