diff --git a/App-Media-Assets/assets/debug_item_icon.png b/App-Media-Assets/assets/debug_item_icon.png
new file mode 100644
index 0000000..78fba5a
Binary files /dev/null and b/App-Media-Assets/assets/debug_item_icon.png differ
diff --git a/App-Media-Assets/assets/heart.png b/App-Media-Assets/assets/heart.png
new file mode 100644
index 0000000..510171a
Binary files /dev/null and b/App-Media-Assets/assets/heart.png differ
diff --git a/App-Media-Assets/itemzflow.gp4 b/App-Media-Assets/itemzflow.gp4
index a181b3d..f004309 100644
--- a/App-Media-Assets/itemzflow.gp4
+++ b/App-Media-Assets/itemzflow.gp4
@@ -17,6 +17,8 @@
+
+
diff --git a/App-Media-Assets/langs/0/lang.ini b/App-Media-Assets/langs/0/lang.ini
index 4990668..4d31478 100644
--- a/App-Media-Assets/langs/0/lang.ini
+++ b/App-Media-Assets/langs/0/lang.ini
@@ -104,7 +104,6 @@ SETTINGS_3=アプリの並べ替え
SETTINGS_4=デフォルトのダッシュボードとして設定
SETTINGS_6=テーマインストーラー
SETTINGS_7=バックグラウンド MP3 パス
-SETTINGS_8=ボタンコントロールを表示
SETTINGS_9=PS4 メニューを開く
SETTINGS_10=PKGインストーラー
SETTINGS_11=コンソールの電源コントロール
@@ -438,7 +437,6 @@ SKIPPING_MSG=スキップしています...
#NEW STRINGS
EXTRA_SETTING_1=カバーをダウンロード
EXTRA_SETTING_2=カスタマイズをリセット
-EXTRA_SETTING_3=反射
EXTRA_SETTING_4=背景画像を変更
EXTRA_SETTING_5=スタートアップ カバー メッセージ
EXTRA_SETTING_6=フォントの変更
diff --git a/App-Media-Assets/langs/1/lang.ini b/App-Media-Assets/langs/1/lang.ini
index 90fa517..2c9c464 100644
--- a/App-Media-Assets/langs/1/lang.ini
+++ b/App-Media-Assets/langs/1/lang.ini
@@ -105,7 +105,6 @@ SETTINGS_3=Sort Apps by
SETTINGS_4=Set as default Dashboard
SETTINGS_6=Theme Installer
SETTINGS_7=Background MP3 Path
-SETTINGS_8=Show Button Controls
SETTINGS_9=Open PS4 Menu
SETTINGS_10=PKG Installer
SETTINGS_11=Console Power Controls
@@ -173,7 +172,7 @@ CLOSE_GAME=Close Open Game
FAILED_MENU_OPEN=Failed to Open Menu
ERROR_CODE=Error code
SELECT_INTERACT=Press X to Interact
-GAME_SAVE_OPTS=Game Save Options
+GAME_SAVES_OPTS=Game Save Options
BACKUP_SAVE=Backup All Saves
IMPORT_SAVE=Import Save
SAVE_DELETE=Delete All Saves
@@ -438,7 +437,6 @@ SKIPPING_MSG=Skipping...
#NEW STRINGS
EXTRA_SETTING_1=Download Covers
EXTRA_SETTING_2=Reset Customizations
-EXTRA_SETTING_3=Reflections
EXTRA_SETTING_4=Change Background Image
EXTRA_SETTING_5=Startup Cover Message
EXTRA_SETTING_6=Change Font
@@ -483,12 +481,21 @@ REFRESH_HOSTAPP=Force Restart Fuse
REFRESH_HOSTAPP_DESC=Forcefully re-connect the last used Remote Share to /hostapp
VAPP_MENU=Virtual App Menu
LNC_TOO_MANY_ROOT_FILES=Too many files in the root of /hostapp/app, TIP: you can move them, then start the game and move them back
+#UPDATE 1.04 STRINGS
+INSTALL_ALL_PKGS=Install All
+INSTALL_ALL_PKGS_DEC=Install all .pkgs in this folder (non-recurrsive)
+FAVORITES=Favorites
+FAVORITES_DESC=Add to your Favorites List
+FAVORITES_ADD=Added to Favorites
+FAVORITES_REMOVED=Removed from Favorites
+FAVORITES_REMOVE=Remove from Favorites List
+SEL_DUMP_VALID1=Dump all + GP4FV
[DUMPER]
DUMP_INFO=Dump info
APP_NAME=App name:
-TITLE_ID=TITLE_ID:
+TITLE_ID=Title ID:
DUMPER_SC0=Dumping sc0(sce_sys) content ...
DUMPING_TROPHIES=Dumping Trophies ...
CREATING_GP4=Creating GP4 ...
@@ -497,3 +504,6 @@ DEL_SEM=Deleting Semaphore ...
EXT_PATCH=Extracting Game Patch Files
EXT_GAME_FILES=Extracting Base Game Files
PROCESSING=Processing
+#UPDATE 1.5 update
+EXT_ADDON=Extracting DLC Files
+DLC_NAME=DLC Name
diff --git a/App-Media-Assets/langs/19/lang.ini b/App-Media-Assets/langs/19/lang.ini
index f88a674..9f826f2 100644
--- a/App-Media-Assets/langs/19/lang.ini
+++ b/App-Media-Assets/langs/19/lang.ini
@@ -106,7 +106,6 @@ SETTINGS_4=Varsayılan Gösterge Tablosu olarak ayarla
SETTINGS_5=Yazı Tipi Yolu
SETTINGS_6=Tema Yükleyici
SETTINGS_7=Arka Plan MP3 Yolu
-SETTINGS_8=Düğme Kontrollerini Göster
SETTINGS_9=PS4 Menüsünü Aç
SETTINGS_10=PKG Yükleyici
SETTINGS_11=Konsol Güç Kontrolleri
diff --git a/App-Media-Assets/langs/25/lang.ini b/App-Media-Assets/langs/25/lang.ini
index 7c44a2d..e3a850e 100644
--- a/App-Media-Assets/langs/25/lang.ini
+++ b/App-Media-Assets/langs/25/lang.ini
@@ -105,7 +105,6 @@ SETTINGS_3=Ταξινόμηση εφαρμογών κατά
SETTINGS_4=Ορισμός ως προεπιλεγμένο Dashboard
SETTINGS_6=Εγκαταστάτης θέματος
SETTINGS_7=Μονοπάτι Background MP3
-SETTINGS_8=Εμφάνιση κουμπιών ελέγχου
SETTINGS_9=Άνοιγμα μενού PS4
SETTINGS_10=Εγκαταστάτης PKG
SETTINGS_11=Κουμπιά Ελέγχου τροφοδοσίας της κονσόλας
diff --git a/App-Media-Assets/langs/27/lang.ini b/App-Media-Assets/langs/27/lang.ini
index cae4ab0..bec523e 100644
--- a/App-Media-Assets/langs/27/lang.ini
+++ b/App-Media-Assets/langs/27/lang.ini
@@ -106,7 +106,6 @@ SETTINGS_4=ตั้งค่าเป็นแดชบอร์ดเริ่
SETTINGS_5=เส้นทางแบบอักษร
SETTINGS_6=ตัวติดตั้งธีม
SETTINGS_7=เส้นทางเพลงพื้นหลัง MP3
-SETTINGS_8=แสดงปุ่มควบคุม
SETTINGS_9=เปิดเมนู PS4
SETTINGS_10=ตัวติดตั้ง PKG
SETTINGS_11=ควบคุมพลังงานคอนโซล
diff --git a/App-Media-Assets/langs/3/lang.ini b/App-Media-Assets/langs/3/lang.ini
index 35a0bcc..d79c3a3 100644
--- a/App-Media-Assets/langs/3/lang.ini
+++ b/App-Media-Assets/langs/3/lang.ini
@@ -150,7 +150,6 @@ SETTINGS_4=Panel por defecto
SETTINGS_5=Ruta de fuente
SETTINGS_6=Instalador de temas
SETTINGS_7=Ruta MP3 de fondo
-SETTINGS_8=Mostrar controles de botones
SETTINGS_9=Abrir menu PS4
SETTINGS_10=Instalador PKG
SETTINGS_11=Controles de energia
@@ -591,7 +590,6 @@ SETTINGS_4=Configurar como Panel por defecto
SETTINGS_5=Ruta de fuente
SETTINGS_6=Instalador de temas
SETTINGS_7=Ruta MP3 de fondo
-SETTINGS_8=Mostrar controles de botones
SETTINGS_9=Abrir menu PS4
SETTINGS_10=Instalador PKG
SETTINGS_11=Controles de energia de la consola
diff --git a/App-Media-Assets/langs/5/lang.ini b/App-Media-Assets/langs/5/lang.ini
index 2c7df6a..c89b645 100644
--- a/App-Media-Assets/langs/5/lang.ini
+++ b/App-Media-Assets/langs/5/lang.ini
@@ -105,7 +105,6 @@ SETTINGS_3=Ordina app per....
SETTINGS_4=Imposta come dashboard predefinita
SETTINGS_6=Programma di installazione del tema
SETTINGS_7=Percorso MP3 di sfondo
-SETTINGS_8=Mostra i controlli dei pulsanti
SETTINGS_9=Apri il menu PS4
SETTINGS_10=Installatore dei PKG
SETTINGS_11=Controlli alimentazione console
diff --git a/App-Media-Assets/sce_sys/param.sfo b/App-Media-Assets/sce_sys/param.sfo
index e7e01e7..90580b7 100644
Binary files a/App-Media-Assets/sce_sys/param.sfo and b/App-Media-Assets/sce_sys/param.sfo differ
diff --git a/README.md b/README.md
index 575068a..482e8fa 100644
--- a/README.md
+++ b/README.md
@@ -5,9 +5,6 @@
## Third-Party Libraries
-- [fuse-nfs](https://github.com/sahlberg/fuse-nfs)
-- [libNFS](https://github.com/orbisdev/orbisdev-liborbisNfs)
-- [libfuse](https://github.com/libfuse/libfuse)
- [FTPS4](https://github.com/xerpi/FTPS4)
- [dr_mp3](https://github.com/mackron/dr_libs/blob/master/dr_mp3.h)
- [liborbisAudio](https://github.com/orbisdev/orbisdev-liborbisAudio)
@@ -23,7 +20,7 @@
- [log.c](https://github.com/rxi/log.c)
- [Game Dumper](https://github.com/Al-Azif/dumper-testing)
- [libjbc](https://github.com/sleirsgoevy/ps4-libjbc)
-- [pugixml](https://github.com/zeux/pugixml)
+- [yaml-cpp](https://github.com/jbeder/yaml-cpp)
- [taglib](https://github.com/taglib/taglib)
- Libz
@@ -36,28 +33,29 @@ the ini file is either loaded by the app dir or from USB0 when the app is booted
#### Settings Layout
-| INI Key | Description |
-|---------------------|-------------------------------------------------------------|
-| `Dumper_option` | 0 = ALL 1 = BASE Game only 2 = Patch only |
-| `Sort_By` | -1 is NA 0 = Alpha order by TID 1 = Alpha order by App Name |
-| `Sort_Cat` | Sort category |
-| `cover_message` | Shows cover message on app startup |
-| `MP3_Path` | FS Path to a folder of MP3s or a single MP3 to play on loop |
-| `Dumper_Path` | Dump path |
-| `TTF_Font` | TTF Font the store will try to use (embedded font on fail) |
-| `Show_Buttons` | Shows IF buttons on the screen |
-| `Enable_Theme` | ONLY Active if you have a theme enabled |
-| `Image_path` | background PNG image |
-| `Reflections` | Enables cover reflections in IF |
-| `Home_Redirection` | Enables the Home Menu Redirect |
-| `Daemon_on_start` | Disables the Daemon from auto connecting with the app |
-| `Image_path` | Background Image |
-| `Show_install_prog` | Enables the Store PKG/APP install Progress |
-| `Fuse_IP` | PC NFS Share in format `nfs://IP/share_name` |
-#### setting.ini example
+| INI Key | Description |
+| ------------- | ------------- |
+| Dumper_option | 0 = ALL 1 = BASE Game only 2 = Patch only |
+| Sort_By | -1 is NA 0 = Alpha order by TID 1 = Alpha order by App Name |
+| Sort_Cat | Sort category |
+| cover_message | Shows cover message on app startup |
+| MP3_Path | FS Path to a folder of MP3s or a single MP3 to play on loop |
+| Dumper_Path | Dump path |
+| TTF_Font | TTF Font the store will try to use (embedded font on fail) |
+| Show_Buttons | Shows IF buttons on the screen |
+| Enable_Theme | ONLY Active if you have a theme enabled |
+| Image_path | background PNG image |
+| Reflections | Enables cover reflections in IF |
+| Home_Redirection | Enables the Home Menu Redirect |
+| Daemon_on_start | Disables the Daemon from auto connecting with the app |
+| Image_path | Background Image |
+| Show_install_prog | Enables the Store PKG/APP install Progress |
-```ini
+
+#### setting.ini example
+
+```
[Settings]
Dumper_option=0
Sort_By=-1
@@ -73,32 +71,25 @@ Reflections=1
Home_Redirection=1
Daemon_on_start=0
Show_install_prog=1
-Fuse_IP=nfs://192.1.2.3/nfs_share
-```
-
-## NFS Share format
-
-the NFS formatted string can be entered in the app using the in-app keyboard or set in the settings.ini file
-
-```
-nfs://PC_IP/share_name
-```
-or if your share name is /
-```
-PC_IP
```
## Itemzflow Log
- If the app crashes or mentions "a FATAL Signal" send this log to us via the discord below or via GH Issues
`/user/app/ITEM00001/logs/itemzflow_app.log`
+
## Daemon
The itemz daemon is installed when you open the app for the first time at `/system/vsh/app/ITEM00002`
-it gets updated ONLY by Itemzflow
+it gets Updated ONLY by the Itemzflwo
-The daemon folder is ONLY for internal use by the Itemzflow devs
-it is hash checked by the loader to ensure the daemon is always up to date with the PKG
+The daemon settings file is ONLY for internal use by the Store devs
+however it also has a ini at `/system/vsh/app/ITEM00002/daemon.ini` with the following ini values
+
+```
+[Daemon]
+version=0x1001// Daemon version for Store, Official version is always > 0x1000
+```
## Dumper details
@@ -115,45 +106,46 @@ it is hash checked by the loader to ensure the daemon is always up to date with
## Themes
-- You can download custom themes released in the [Itemzflow Themes Repo](https://github.com/LightningMods/itemzflow-themes)
+- You can download custom themes released in the [Itemzflow Themes Repo](https://github.com/LightningMods/xxxxxxxx)
- You can also make your own custom themes by following these instructions.
-#### IMPORTANT the Theme files must HAVE the exact filename as listed below
+#### IMPORTENT the Theme files HAVE to have the exact filename as listed below
-| Filename | Description |
-|-------------------|----------------------------|
-| `btn_o.png` | O Button (67x68) |
-| `btn_x.png` | X Button (67x68) |
-| `btn_tri.png` | Triangle Button (67x68) |
-| `btn_sq.png` | square Button (67x68) |
-| `btn_r1.png` | R1 Button (309x152) |
-| `btn_l1.png` | L1 Button (120x59) |
-| `btn_l2.png` | L2 Button (120x105) |
-| `btn_options.png` | Options Button (145x84) |
-| `btn_up.png` | D-Pad Up Button (32x32) |
-| `btn_down.png` | D-Pad Down Button (32x32) |
-| `btn_left.png` | D-Pad Left Button (32x32) |
-| `btn_right.png` | D-Pad Right Button (32x32) |
-| `font.ttf` | Theme Font |
-| `background.png` | Background Image |
-| `shader.bin` | PS4 GLES Compiled Shader |
-| `theme.ini` | Theme Info |
+| Filename | Description |
+| ------------- | ------------- |
+| btn_o.png | O Button (67x68) |
+| btn_x.png | X Button (67x68) |
+| btn_tri.png | Triangle Button (67x68) |
+| btn_sq.png | square Button (67x68) |
+| btn_r1.png | R1 Button (309x152) |
+| btn_l1.png | L1 Button (120x59) |
+| btn_l2.png | L2 Button (120x105) |
+| btn_options.png | Options Button (145x84) |
+| btn_up.png | D-Pad Up Button (32x32) |
+| btn_down.png | D-Pad Down Button (32x32) |
+| btn_left.png | D-Pad Left Button (32x32) |
+| btn_right.png | D-Pad Right Button (32x32) |
+| font.ttf | Theme Font |
+| background.png | Background Image |
+| shader.bin | PS4 GLES Compiled Shader |
+| theme.ini | Theme Info |
#### Theme INI Config
-| INI Key | Description |
-|---------|--------------------------------------------------|
-| Name | Theme Name |
-| Author | Who Made it |
-| Date | Date it was made on |
-| Version | Theme Version Number |
-| Image | 1 if your Theme has a background image, 0 if not |
-| Font | 1 = Theme has font.ttf 0 = It doesnt |
-| Shader | 1 = Has Shader bin 0 = It doesnt |
-#### Example Theme config
+| INI Key | Description |
+| ------------- | ------------- |
+| Name | Theme Name |
+| Author | Who Made it |
+| Date | Date it was made on |
+| Version | Theme Version Number |
+| Image | 1 if your Theme has a background image, 0 if not |
+| Font | 1 = Theme has font.ttf 0 = It doesnt |
+| Shader | 1 = Has Shader bin 0 = It doesnt |
-```ini
+
+#### Example Theme config
+```
[THEME]
Name=Example Theme
Author=Example Author
@@ -167,90 +159,58 @@ Shader=0
## App logs and their paths
-| Service | PS4 Path |
-|--------------------|----------------------------------------------|
-| Itemzflow main app | `/user/app/ITEM00001/logs/itemzflow_app.log` |
-| Dumper | `/user/app/ITEM00001/logs/if_dumper.log` |
-| Itemz Loader | `/user/app/ITEM00001/logs/loader.log` |
-| Itemz Daemon | `/data/itemzflow_daemon/daemon.log` |
-| libfuse | `/data/itemzflow_daemon/libfuse.log` |
+| Service | PS4 Path |
+| ------------- | ------------- |
+| Itemzflow main app | /user/app/ITEM00001/logs/itemzflow_app.log |
+| Dumper | /user/app/ITEM00001/logs/if_dumper.log |
+| Itemz Loader | /user/app/ITEM00001/logs/loader.log |
+| Itemz Daemon | /data/itemzflow_daemon/daemon.log |
+| libfuse | /data/itemzflow_daemon/libfuse.log |
#### On USB (only on failure or crash)
-| Service | USB Path |
-|--------------------|---------------------------------------------|
-| Itemzflow main app | `/mnt/usb/itemzflow/crash.log` |
-| Dumper | `/mnt/usb/itemzflow/dumper.log` |
-| Itemz Loader | `/mnt/usb/itemzflow/loader.log` |
-| Itemz Daemon | `/mnt/usb/itemzflow/daemon.log` |
+| Service | USB Path |
+| ------------- | ------------- |
+| Itemzflow main app | /mnt/usb/itemzflow/crash.log |
+| Dumper | /mnt/usb/itemzflow/dumper.log |
+| Itemz Loader | /usb0/itemzflow/loader.log |
+| Itemz Daemon | /mnt/usb0/itemzflow/daemon.log |
+
+
## Official Discord server
Invite: https://discord.gg/GvzDdx9GTc
## Game Patches
-Custom Game Patches for PlayStation 4 Games.
-### Features
-* `.xml` support
+- Downloads from [online database](https://github.com/illusion0001/console-game-patches) via the app or manual install via [zip file](https://assets.illusion0001.com/patch1.zip)
+- Supported formats: `yaml` filename must be `(TITLE_ID).yml, i.e CUSA00547.yml`
+- Startup patches only supports `eboot.bin` for now. PRs are very welcome to support games that use multple elf.
+- YML per game filepath: `/data/GoldHEN/patches/yml/(TITLE_ID).yml`
-### Usage
+### Patch Syntax
-### Easy Installation
-- Patches can be configured, install/update via:
- - [GoldHEN Cheat Manager](https://github.com/GoldHEN/GoldHEN_Cheat_Manager/releases/latest)
- - [Itemzflow Game Manager](https://github.com/LightningMods/Itemzflow)
-- Run your game.
+Syntax:
+```json
+{ "type": "", "addr": "", "value": "" }
+```
-### Storage
-* Use `FTP` to upload patch files to:
- * `/user/data/GoldHEN/patches/xml/`
-* Naming conversion for app and patch engine to recognize: `(TitleID).{format}`
- * e.g. `CUSA00001.xml`
- * e.g. `CUSA03694.xml`
+Types:
-### Patch types
+- `byte`: 1 byte
+- `bytes16`: 2 bytes
+- `bytes32`: 4 bytes
+- `bytes64`: 8 bytes
+- `bytes`: array of bytes (no spaces)
+- `utf8`: utf8 string
+- `utf16`: utf16 string
-| `type` | Info | Value (example) |
-|-----------|---------------------------|------------------------|
-| `byte` | Hex, 1 byte | `"0x00"` |
-| `bytes16` | Hex, 2 bytes | `"0x0000"` |
-| `bytes32` | Hex, 4 bytes | `"0x00000000"` |
-| `bytes64` | Hex, 8 bytes | `"0x0000000000000000"` |
-| `bytes` | Hex, any size (no spaces) | `"####"` |
-| `float32` | Float, single | `"1.0"` |
-| `float64` | Float, double | `"1.0"` |
-| `utf8` | String, UTF-8* | `"string"` |
-| `utf16` | String, UTF-16* | `"string"` |
+Examples:
-#### Example patch
-
-```xml
-
-
-
-
- EXAMPLE01
- EXAMPLE02
-
-
-
-
-
-
-
-
-
-
+```json
+{ "type": "byte", "addr": "0x01271464", "value": "0x01" },
+{ "type": "bytes", "addr": "0x00402d90", "value": "31c0c3" }
```
## Donations
@@ -258,7 +218,7 @@ Custom Game Patches for PlayStation 4 Games.
We accept the following methods
- [Ko-fi](https://ko-fi.com/lightningmods)
-- BTC: bc1qgclk220glhffjkgraju7d8xjlf7teks3cnwuu9
+- BTC: 3MEuZAaA7gfKxh9ai4UwYgHZr5DVWfR6Lw
if you donate and dont want to the message anymore created this folder after donating ``
@@ -273,10 +233,8 @@ if you donate and dont want to the message anymore created this folder after don
- [Masterzorag](https://twitter.com/masterzorag)
- [Psxdev/BigBoss](https://twitter.com/psxdev)
- [Specter](https://twitter.com/SpecterDev)
-- [SocraticBliss](https://twitter.com/SocraticBliss)
- [Sleirsgoevy](https://github.com/sleirsgoevy/)
- [AlAzif](https://github.com/al-azif)
- BigBoss
- [bucanero](https://github.com/bucanero) (Game Saves)
- [illusion](https://github.com/illusion0001) (Game Patches/Trainers)
-- [Pharaoh2k](https://github.com/Pharaoh2k) (Apps/Games/DLCs Databases Recovery)
diff --git a/external/libdumper/include/common.hpp b/external/libdumper/include/common.hpp
index a027ef4..2f3fd8f 100644
--- a/external/libdumper/include/common.hpp
+++ b/external/libdumper/include/common.hpp
@@ -9,6 +9,8 @@
#include
#include "log.h"
#include
+#include // std::cout
+#include // std::ifstream
#ifndef PAGE_SIZE
#define PAGE_SIZE 0x4000
@@ -32,6 +34,8 @@ extern "C" {
extern int32_t sceMsgDialogProgressBarSetValue(int target, int rate);
}
-void ProgUpdate(int prog, const char* fmt, ...);
+void ProgUpdate(uint32_t prog, std::string fmt);
+std::filesystem::path customRelative(std::filesystem::path from, std::filesystem::path to);
+std::string customFileSize(const std::string& filename);
#endif // COMMON_HPP_
diff --git a/external/libdumper/include/crc32.h b/external/libdumper/include/crc32.h
new file mode 100644
index 0000000..bd642f7
--- /dev/null
+++ b/external/libdumper/include/crc32.h
@@ -0,0 +1,68 @@
+// //////////////////////////////////////////////////////////
+// crc32.h
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#pragma once
+
+//#include "hash.h"
+#include
+
+// define fixed size integer types
+#ifdef _MSC_VER
+// Windows
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int32 uint32_t;
+#else
+// GCC
+#include
+#endif
+
+/// compute CRC32 hash, based on Intel's Slicing-by-8 algorithm
+/** Usage:
+ CRC32 crc32;
+ std::string myHash = crc32("Hello World"); // std::string
+ std::string myHash2 = crc32("How are you", 11); // arbitrary data, 11 bytes
+
+ // or in a streaming fashion:
+
+ CRC32 crc32;
+ while (more data available)
+ crc32.add(pointer to fresh data, number of new bytes);
+ std::string myHash3 = crc32.getHash();
+
+ Note:
+ You can find code for the faster Slicing-by-16 algorithm on my website, too:
+ http://create.stephan-brumme.com/crc32/
+ Its unrolled version is about twice as fast but its look-up table doubled in size as well.
+ */
+class CRC32 //: public Hash
+{
+public:
+ /// hash is 4 bytes long
+ enum { HashBytes = 4 };
+
+ /// same as reset()
+ CRC32();
+
+ /// compute CRC32 of a memory block
+ std::string operator()(const void *data, size_t numBytes);
+ /// compute CRC32 of a string, excluding final zero
+ std::string operator()(const std::string &text);
+
+ /// add arbitrary number of bytes
+ void add(const void *data, size_t numBytes);
+
+ /// return latest hash as 8 hex characters
+ std::string getHash();
+ /// return latest hash as bytes
+ void getHash(unsigned char buffer[HashBytes]);
+
+ /// restart
+ void reset();
+
+private:
+ /// hash
+ uint32_t m_hash;
+};
diff --git a/external/libdumper/include/dump.hpp b/external/libdumper/include/dump.hpp
index 1f271f9..46fcab0 100644
--- a/external/libdumper/include/dump.hpp
+++ b/external/libdumper/include/dump.hpp
@@ -9,6 +9,7 @@
namespace dump {
bool __dump(const std::string& usb_device, const std::string& title_id, Dump_Options opt, const std::string& title);
+bool dump_dlc(const std::string & usb_device, const std::string & title_id);
} // namespace dump
#endif // DUMP_HPP_
diff --git a/external/libdumper/include/dumper.h b/external/libdumper/include/dumper.h
index b22a83b..b576790 100644
--- a/external/libdumper/include/dumper.h
+++ b/external/libdumper/include/dumper.h
@@ -15,6 +15,10 @@ typedef enum Dump_Options {
TOTAL_OF_OPTS = 7
} Dump_Options;
-bool Dumper(std::string &dump_path, std::string &title_id, Dump_Options opt, std::string &title);
+#define MB(x) ((size_t) (x) << 20)
+
+bool Dumper(const std::string &dump_path,const std::string &title_id, Dump_Options opt, const std::string &title);
+std::vector> query_dlc_database(const std::string& title_id);
+bool copy_dir(const std::string& source_dir, const std::string& dest_dir);
#endif // RAYLIB_TILESON_H_
\ No newline at end of file
diff --git a/external/libdumper/include/elf.hpp b/external/libdumper/include/elf.hpp
index 4000871..2a84ebe 100644
--- a/external/libdumper/include/elf.hpp
+++ b/external/libdumper/include/elf.hpp
@@ -117,7 +117,8 @@ namespace elf {
std::vector get_auth_info(const std::string& path);
bool is_valid_decrypt(const std::string& original, const std::string& decrypted);
void zero_section_header(const std::string& path);
- bool decrypt_dir(const std::string& input, const std::string& output);
+ bool decrypt_dir(const std::string& inputPath, const std::string& outputPath, bool fself);
+ bool decrypt_and_dump_self(const char* selfFile, const char* saveFile);
} // namespace elf
#endif // ELF_HPP_
diff --git a/external/libdumper/include/fself.hpp b/external/libdumper/include/fself.hpp
index ea20486..d6d0417 100644
--- a/external/libdumper/include/fself.hpp
+++ b/external/libdumper/include/fself.hpp
@@ -8,7 +8,7 @@
#include
namespace fself {
-bool is_fself(const std::string &path);
+bool is_fself(const std::string& self_path);
void make_fself(const std::string &input, const std::string &output, uint64_t paid, const std::string &ptype, uint64_t app_version, uint64_t fw_version, std::vector auth_info);
}
diff --git a/external/libdumper/include/lang.h b/external/libdumper/include/lang.h
index e0b0487..02dc5cf 100644
--- a/external/libdumper/include/lang.h
+++ b/external/libdumper/include/lang.h
@@ -1,4 +1,10 @@
#pragma once
+
+#include
+#include
+#include
+#include
+
enum Lang_ST {
DUMP_INFO,
APP_NAME,
@@ -11,20 +17,34 @@ enum Lang_ST {
EXT_PATCH,
EXT_GAME_FILES,
PROCESSING,
-
+ EXT_ADDON,
+ DLC_NAME,
//DO NOT DELETE THIS, ALWAYS KEEP AT THE BOTTOM
LANG_NUM_OF_STRINGS
};
+static const char *lang_key[LANG_NUM_OF_STRINGS] = {
+ "DUMP_INFO",
+ "APP_NAME",
+ "TITLE_ID",
+ "DUMPER_SC0",
+ "DUMPING_TROPHIES",
+ "CREATING_GP4",
+ "DEC_BIN",
+ "DEL_SEM",
+ "EXT_PATCH",
+ "EXT_GAME_FILES",
+ "PROCESSING",
+ "EXT_ADDON",
+ "DLC_NAME",
+};
+
typedef struct
{
- char* strings[LANG_NUM_OF_STRINGS];
-} DumperStrings;
-
-
-#define LANG_DIR "/mnt/sandbox/pfsmnt/ITEM00001-app0/langs/%i/lang.ini"
+ std::string strings[LANG_NUM_OF_STRINGS];
+} LangStrings;
bool LoadDumperLangs(int LangCode);
-char* getDumperLangSTR(enum Lang_ST str);
+std::string getDumperLangSTR(Lang_ST str);
int32_t DumperGetLang();
bool load_dumper_embdded_eng();
\ No newline at end of file
diff --git a/external/libdumper/include/md5.h b/external/libdumper/include/md5.h
new file mode 100644
index 0000000..ea39975
--- /dev/null
+++ b/external/libdumper/include/md5.h
@@ -0,0 +1,78 @@
+// //////////////////////////////////////////////////////////
+// md5.h
+// Copyright (c) 2014 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#pragma once
+
+//#include "hash.h"
+#include
+
+// define fixed size integer types
+#ifdef _MSC_VER
+// Windows
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+// GCC
+#include
+#endif
+
+/// compute MD5 hash
+/** Usage:
+ MD5 md5;
+ std::string myHash = md5("Hello World"); // std::string
+ std::string myHash2 = md5("How are you", 11); // arbitrary data, 11 bytes
+
+ // or in a streaming fashion:
+
+ MD5 md5;
+ while (more data available)
+ md5.add(pointer to fresh data, number of new bytes);
+ std::string myHash3 = md5.getHash();
+ */
+class MD5 //: public Hash
+{
+public:
+ /// split into 64 byte blocks (=> 512 bits), hash is 16 bytes long
+ enum { BlockSize = 512 / 8,
+ HashBytes = 16 };
+
+ /// same as reset()
+ MD5();
+
+ /// compute MD5 of a memory block
+ std::string operator()(const void *data, size_t numBytes);
+ /// compute MD5 of a string, excluding final zero
+ std::string operator()(const std::string &text);
+
+ /// add arbitrary number of bytes
+ void add(const void *data, size_t numBytes);
+
+ /// return latest hash as 32 hex characters
+ std::string getHash();
+ /// return latest hash as bytes
+ void getHash(unsigned char buffer[HashBytes]);
+
+ /// restart
+ void reset();
+
+private:
+ /// process 64 bytes
+ void processBlock(const void *data);
+ /// process everything left in the internal buffer
+ void processBuffer();
+
+ /// size of processed data in bytes
+ uint64_t m_numBytes;
+ /// valid bytes in m_buffer
+ size_t m_bufferSize;
+ /// bytes not processed yet
+ uint8_t m_buffer[BlockSize];
+
+ enum { HashValues = HashBytes / 4 };
+ /// hash, stored as integers
+ uint32_t m_hash[HashValues];
+};
diff --git a/external/libdumper/include/pfs.hpp b/external/libdumper/include/pfs.hpp
index 677f665..1d80e2d 100644
--- a/external/libdumper/include/pfs.hpp
+++ b/external/libdumper/include/pfs.hpp
@@ -8,9 +8,9 @@
#include
#include
#include
+#include
#define PFS_MAGIC 0x0B2A330100000000
-
#define PFS_DUMP_BUFFER 0x200000
namespace pfs {
@@ -56,15 +56,8 @@ namespace pfs {
uint32_t entsize;
} pfs_dirent_t;
- extern pfs_header* header;
- extern size_t pfs_size;
- extern size_t pfs_copied;
- extern di_d32* inodes;
-
- void __parse_directory(uint32_t ino, uint32_t lev, const char* parent_name, bool dry_run, char* title = NULL, const char* tid = "CUSA0000");
- void calculate_pfs(uint32_t ino, uint32_t level, const std::string& output_path);
- void dump_pfs(uint32_t ino, uint32_t level, const std::string& output_path, const char* tid = "CUSA0000", char* title = NULL);
- bool extract(const std::string& pfs_path, const std::string& output_path, const char* tid = "CUSA0000", char* title = NULL);
+ bool extract(const std::string& pfs_path, const std::string& output_path, const std::string& tid, const std::string& title);
+ extern Dump_Options dump_op;
} // namespace pfs
#endif // PFS_HPP_
diff --git a/external/libdumper/include/sha1.h b/external/libdumper/include/sha1.h
new file mode 100644
index 0000000..5dd83ac
--- /dev/null
+++ b/external/libdumper/include/sha1.h
@@ -0,0 +1,78 @@
+// //////////////////////////////////////////////////////////
+// sha1.h
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#pragma once
+
+//#include "hash.h"
+#include
+
+// define fixed size integer types
+#ifdef _MSC_VER
+// Windows
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+// GCC
+#include
+#endif
+
+/// compute SHA1 hash
+/** Usage:
+ SHA1 sha1;
+ std::string myHash = sha1("Hello World"); // std::string
+ std::string myHash2 = sha1("How are you", 11); // arbitrary data, 11 bytes
+
+ // or in a streaming fashion:
+
+ SHA1 sha1;
+ while (more data available)
+ sha1.add(pointer to fresh data, number of new bytes);
+ std::string myHash3 = sha1.getHash();
+ */
+class SHA1 //: public Hash
+{
+public:
+ /// split into 64 byte blocks (=> 512 bits), hash is 20 bytes long
+ enum { BlockSize = 512 / 8,
+ HashBytes = 20 };
+
+ /// same as reset()
+ SHA1();
+
+ /// compute SHA1 of a memory block
+ std::string operator()(const void *data, size_t numBytes);
+ /// compute SHA1 of a string, excluding final zero
+ std::string operator()(const std::string &text);
+
+ /// add arbitrary number of bytes
+ void add(const void *data, size_t numBytes);
+
+ /// return latest hash as 40 hex characters
+ std::string getHash();
+ /// return latest hash as bytes
+ void getHash(unsigned char buffer[HashBytes]);
+
+ /// restart
+ void reset();
+
+private:
+ /// process 64 bytes
+ void processBlock(const void *data);
+ /// process everything left in the internal buffer
+ void processBuffer();
+
+ /// size of processed data in bytes
+ uint64_t m_numBytes;
+ /// valid bytes in m_buffer
+ size_t m_bufferSize;
+ /// bytes not processed yet
+ uint8_t m_buffer[BlockSize];
+
+ enum { HashValues = HashBytes / 4 };
+ /// hash, stored as integers
+ uint32_t m_hash[HashValues];
+};
diff --git a/external/libdumper/include/sha256.h b/external/libdumper/include/sha256.h
new file mode 100644
index 0000000..6bb17dd
--- /dev/null
+++ b/external/libdumper/include/sha256.h
@@ -0,0 +1,78 @@
+// //////////////////////////////////////////////////////////
+// sha256.h
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#pragma once
+
+//#include "hash.h"
+#include
+
+// define fixed size integer types
+#ifdef _MSC_VER
+// Windows
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+// GCC
+#include
+#endif
+
+/// compute SHA256 hash
+/** Usage:
+ SHA256 sha256;
+ std::string myHash = sha256("Hello World"); // std::string
+ std::string myHash2 = sha256("How are you", 11); // arbitrary data, 11 bytes
+
+ // or in a streaming fashion:
+
+ SHA256 sha256;
+ while (more data available)
+ sha256.add(pointer to fresh data, number of new bytes);
+ std::string myHash3 = sha256.getHash();
+ */
+class SHA256 //: public Hash
+{
+public:
+ /// split into 64 byte blocks (=> 512 bits), hash is 32 bytes long
+ enum { BlockSize = 512 / 8,
+ HashBytes = 32 };
+
+ /// same as reset()
+ SHA256();
+
+ /// compute SHA256 of a memory block
+ std::string operator()(const void *data, size_t numBytes);
+ /// compute SHA256 of a string, excluding final zero
+ std::string operator()(const std::string &text);
+
+ /// add arbitrary number of bytes
+ void add(const void *data, size_t numBytes);
+
+ /// return latest hash as 64 hex characters
+ std::string getHash();
+ /// return latest hash as bytes
+ void getHash(unsigned char buffer[HashBytes]);
+
+ /// restart
+ void reset();
+
+private:
+ /// process 64 bytes
+ void processBlock(const void *data);
+ /// process everything left in the internal buffer
+ void processBuffer();
+
+ /// size of processed data in bytes
+ uint64_t m_numBytes;
+ /// valid bytes in m_buffer
+ size_t m_bufferSize;
+ /// bytes not processed yet
+ uint8_t m_buffer[BlockSize];
+
+ enum { HashValues = HashBytes / 4 };
+ /// hash, stored as integers
+ uint32_t m_hash[HashValues];
+};
diff --git a/external/libdumper/source/common.cpp b/external/libdumper/source/common.cpp
index 52d4639..a430135 100644
--- a/external/libdumper/source/common.cpp
+++ b/external/libdumper/source/common.cpp
@@ -4,20 +4,149 @@
#include
#include
#include "common.hpp"
+#include
+#include
+#include
+#include
-void ProgUpdate(int prog, const char* fmt, ...)
+
+void ProgUpdate(uint32_t prog, std::string fmt)
{
- char buff[300];
- memset(&buff[0], 0, sizeof(buff));
-
- va_list args;
- va_start(args, fmt);
- vsnprintf(&buff[0], 299, fmt, args);
- va_end(args);
-
- if (sceMsgDialogUpdateStatus() == COMMON_DIALOG_STATUS_RUNNING)
- {
- sceMsgDialogProgressBarSetMsg(0, (const char*)buff);
- sceMsgDialogProgressBarSetValue(0, prog);
- }
+
+ int status = sceMsgDialogUpdateStatus();
+ if (COMMON_DIALOG_STATUS_RUNNING == status) {
+ sceMsgDialogProgressBarSetValue(0, prog);
+ sceMsgDialogProgressBarSetMsg(0, fmt.c_str());
+ }
}
+
+bool copy_dir(const std::string & source_dir,
+ const std::string & dest_dir) {
+ try {
+ std::filesystem::path src_dir_path(source_dir);
+ std::filesystem::path dst_dir_path(dest_dir);
+
+ if (!std::filesystem::exists(src_dir_path)) {
+ log_error("Source directory does not exist: %s", source_dir.c_str());
+ return false;
+ }
+
+ std::filesystem::create_directory(dst_dir_path);
+ for (const auto & entry: std::filesystem::directory_iterator(src_dir_path)) {
+ std::filesystem::path entry_path = entry.path();
+ std::string file_name = entry_path.filename().string();
+
+ if (file_name == "save.ini") {
+ log_error("Save.ini file skipped...");
+ continue;
+ }
+
+ if (file_name == "." || file_name == ".." || file_name.find("nobackup") != std::string::npos) {
+ continue;
+ }
+
+ std::filesystem::path dst_path = dst_dir_path / entry_path.filename();
+
+ if (std::filesystem::is_directory(entry_path)) {
+ copy_dir(entry_path.string(), dst_path.string());
+ } else if (std::filesystem::is_regular_file(entry_path)) {
+ std::filesystem::copy(entry_path, dst_path, std::filesystem::copy_options::overwrite_existing);
+ }
+ }
+ } catch (std::exception & e) {
+ log_error("Exception: %s", e.what());
+ return false;
+ }
+
+ return true;
+}
+// Function that will be called for each row returned by the query
+static int dlc_callback(void* data, int argc, char** argv, char** azColName) {
+ auto* results = static_cast>*>(data);
+
+ if (argc == 3 && argv[0] && argv[1] && argv[2]) {
+ results->emplace_back(argv[0], argv[1], argv[2]);
+ }
+
+ return 0;
+}
+
+std::vector> query_dlc_database(const std::string& title_id) {
+ sqlite3* db;
+ char* zErrMsg = 0;
+ int rc;
+ std::vector> results;
+
+ // Connect to the database
+ rc = sqlite3_open_v2("/system_data/priv/mms/addcont.db", &db, SQLITE_OPEN_READWRITE, NULL);
+ if (rc) {
+ log_info("Can't open database");
+ return results;
+ }
+
+ std::string sql_query = "SELECT dir_name, title, content_id FROM addcont WHERE title_id='" + title_id + "';";
+
+ // Execute SQL statement
+ rc = sqlite3_exec(db, sql_query.c_str(), dlc_callback, &results, &zErrMsg);
+
+ if (rc != SQLITE_OK) {
+ log_error("SQL error: %s", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+
+ sqlite3_close(db);
+
+ return results;
+}
+
+// This function is used because std::fs::relative path crashes on PS4 atleast with the OrbisDev SDK
+// with a UaF heap erro, but this works fone
+std::filesystem::path customRelative(std::filesystem::path from, std::filesystem::path to)
+{
+ // create absolute paths
+ std::filesystem::path p = std::filesystem::absolute(from);
+ std::filesystem::path r = std::filesystem::absolute(to);
+
+ // if root paths are different, return absolute path
+ if( p.root_path() != r.root_path() )
+ return p;
+
+ // initialize relative path
+ std::filesystem::path result;
+
+ // find out where the two paths diverge
+ std::filesystem::path::const_iterator itr_path = p.begin();
+ std::filesystem::path::const_iterator itr_relative_to = r.begin();
+ while( itr_path != p.end() && itr_relative_to != r.end() && *itr_path == *itr_relative_to ) {
+ ++itr_path;
+ ++itr_relative_to;
+ }
+
+ // add "../" for each remaining token in relative_to
+ if( itr_relative_to != r.end() ) {
+ ++itr_relative_to;
+ while( itr_relative_to != r.end() ) {
+ result /= "..";
+ ++itr_relative_to;
+ }
+ }
+
+ // add remaining path
+ while( itr_path != p.end() ) {
+ result /= *itr_path;
+ ++itr_path;
+ }
+
+ log_info("%s: from = %s, to = %s: res %s", __FUNCTION__, from.string().c_str(), to.string().c_str(), result.string().c_str());
+
+ return result;
+}
+
+std::string customFileSize(const std::string& filename) {
+ std::ifstream file(filename, std::ifstream::ate | std::ifstream::binary);
+ if (!file.is_open()) {
+ log_error("Could not open file: %s", filename.c_str());
+ return 0;
+ }
+ return std::to_string(file.tellg());
+}
\ No newline at end of file
diff --git a/external/libdumper/source/crc32.cpp b/external/libdumper/source/crc32.cpp
new file mode 100644
index 0000000..0c277d0
--- /dev/null
+++ b/external/libdumper/source/crc32.cpp
@@ -0,0 +1,408 @@
+// //////////////////////////////////////////////////////////
+// crc32.cpp
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#include "crc32.h"
+
+// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN
+/// same as reset()
+CRC32::CRC32() {
+ reset();
+}
+
+/// restart
+void CRC32::reset() {
+ m_hash = 0;
+}
+
+namespace {
+/// look-up table
+static const uint32_t crc32Lookup[8][256] =
+ {
+ // generated by:
+ //for (uint32_t i = 0; i <= 0xFF; i++)
+ //{
+ // uint32_t crc = i;
+ // for (unsigned int j = 0; j < 8; j++)
+ // crc = (crc >> 1) ^ ((crc & 1) * Polynomial);
+ // crc32Lookup[0][i] = crc;
+ //}
+ // slicing-by-8 algorithm (from Intel):
+ // http://www.intel.com/technology/comms/perfnet/download/CRC_generators.pdf
+ // http://sourceforge.net/projects/slicing-by-8/
+ //for (unsigned int i = 0; i <= 0xFF; i++)
+ //{
+ // crc32Lookup[1][i] = (crc32Lookup[0][i] >> 8) ^ crc32Lookup[0][crc32Lookup[0][i] & 0xFF];
+ // crc32Lookup[2][i] = (crc32Lookup[1][i] >> 8) ^ crc32Lookup[0][crc32Lookup[1][i] & 0xFF];
+ // crc32Lookup[3][i] = (crc32Lookup[2][i] >> 8) ^ crc32Lookup[0][crc32Lookup[2][i] & 0xFF];
+
+ // crc32Lookup[4][i] = (crc32Lookup[3][i] >> 8) ^ crc32Lookup[0][crc32Lookup[3][i] & 0xFF];
+ // crc32Lookup[5][i] = (crc32Lookup[4][i] >> 8) ^ crc32Lookup[0][crc32Lookup[4][i] & 0xFF];
+ // crc32Lookup[6][i] = (crc32Lookup[5][i] >> 8) ^ crc32Lookup[0][crc32Lookup[5][i] & 0xFF];
+ // crc32Lookup[7][i] = (crc32Lookup[6][i] >> 8) ^ crc32Lookup[0][crc32Lookup[6][i] & 0xFF];
+ //}
+ {0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+ 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D},
+
+ {0x00000000, 0x191B3141, 0x32366282, 0x2B2D53C3, 0x646CC504, 0x7D77F445, 0x565AA786, 0x4F4196C7,
+ 0xC8D98A08, 0xD1C2BB49, 0xFAEFE88A, 0xE3F4D9CB, 0xACB54F0C, 0xB5AE7E4D, 0x9E832D8E, 0x87981CCF,
+ 0x4AC21251, 0x53D92310, 0x78F470D3, 0x61EF4192, 0x2EAED755, 0x37B5E614, 0x1C98B5D7, 0x05838496,
+ 0x821B9859, 0x9B00A918, 0xB02DFADB, 0xA936CB9A, 0xE6775D5D, 0xFF6C6C1C, 0xD4413FDF, 0xCD5A0E9E,
+ 0x958424A2, 0x8C9F15E3, 0xA7B24620, 0xBEA97761, 0xF1E8E1A6, 0xE8F3D0E7, 0xC3DE8324, 0xDAC5B265,
+ 0x5D5DAEAA, 0x44469FEB, 0x6F6BCC28, 0x7670FD69, 0x39316BAE, 0x202A5AEF, 0x0B07092C, 0x121C386D,
+ 0xDF4636F3, 0xC65D07B2, 0xED705471, 0xF46B6530, 0xBB2AF3F7, 0xA231C2B6, 0x891C9175, 0x9007A034,
+ 0x179FBCFB, 0x0E848DBA, 0x25A9DE79, 0x3CB2EF38, 0x73F379FF, 0x6AE848BE, 0x41C51B7D, 0x58DE2A3C,
+ 0xF0794F05, 0xE9627E44, 0xC24F2D87, 0xDB541CC6, 0x94158A01, 0x8D0EBB40, 0xA623E883, 0xBF38D9C2,
+ 0x38A0C50D, 0x21BBF44C, 0x0A96A78F, 0x138D96CE, 0x5CCC0009, 0x45D73148, 0x6EFA628B, 0x77E153CA,
+ 0xBABB5D54, 0xA3A06C15, 0x888D3FD6, 0x91960E97, 0xDED79850, 0xC7CCA911, 0xECE1FAD2, 0xF5FACB93,
+ 0x7262D75C, 0x6B79E61D, 0x4054B5DE, 0x594F849F, 0x160E1258, 0x0F152319, 0x243870DA, 0x3D23419B,
+ 0x65FD6BA7, 0x7CE65AE6, 0x57CB0925, 0x4ED03864, 0x0191AEA3, 0x188A9FE2, 0x33A7CC21, 0x2ABCFD60,
+ 0xAD24E1AF, 0xB43FD0EE, 0x9F12832D, 0x8609B26C, 0xC94824AB, 0xD05315EA, 0xFB7E4629, 0xE2657768,
+ 0x2F3F79F6, 0x362448B7, 0x1D091B74, 0x04122A35, 0x4B53BCF2, 0x52488DB3, 0x7965DE70, 0x607EEF31,
+ 0xE7E6F3FE, 0xFEFDC2BF, 0xD5D0917C, 0xCCCBA03D, 0x838A36FA, 0x9A9107BB, 0xB1BC5478, 0xA8A76539,
+ 0x3B83984B, 0x2298A90A, 0x09B5FAC9, 0x10AECB88, 0x5FEF5D4F, 0x46F46C0E, 0x6DD93FCD, 0x74C20E8C,
+ 0xF35A1243, 0xEA412302, 0xC16C70C1, 0xD8774180, 0x9736D747, 0x8E2DE606, 0xA500B5C5, 0xBC1B8484,
+ 0x71418A1A, 0x685ABB5B, 0x4377E898, 0x5A6CD9D9, 0x152D4F1E, 0x0C367E5F, 0x271B2D9C, 0x3E001CDD,
+ 0xB9980012, 0xA0833153, 0x8BAE6290, 0x92B553D1, 0xDDF4C516, 0xC4EFF457, 0xEFC2A794, 0xF6D996D5,
+ 0xAE07BCE9, 0xB71C8DA8, 0x9C31DE6B, 0x852AEF2A, 0xCA6B79ED, 0xD37048AC, 0xF85D1B6F, 0xE1462A2E,
+ 0x66DE36E1, 0x7FC507A0, 0x54E85463, 0x4DF36522, 0x02B2F3E5, 0x1BA9C2A4, 0x30849167, 0x299FA026,
+ 0xE4C5AEB8, 0xFDDE9FF9, 0xD6F3CC3A, 0xCFE8FD7B, 0x80A96BBC, 0x99B25AFD, 0xB29F093E, 0xAB84387F,
+ 0x2C1C24B0, 0x350715F1, 0x1E2A4632, 0x07317773, 0x4870E1B4, 0x516BD0F5, 0x7A468336, 0x635DB277,
+ 0xCBFAD74E, 0xD2E1E60F, 0xF9CCB5CC, 0xE0D7848D, 0xAF96124A, 0xB68D230B, 0x9DA070C8, 0x84BB4189,
+ 0x03235D46, 0x1A386C07, 0x31153FC4, 0x280E0E85, 0x674F9842, 0x7E54A903, 0x5579FAC0, 0x4C62CB81,
+ 0x8138C51F, 0x9823F45E, 0xB30EA79D, 0xAA1596DC, 0xE554001B, 0xFC4F315A, 0xD7626299, 0xCE7953D8,
+ 0x49E14F17, 0x50FA7E56, 0x7BD72D95, 0x62CC1CD4, 0x2D8D8A13, 0x3496BB52, 0x1FBBE891, 0x06A0D9D0,
+ 0x5E7EF3EC, 0x4765C2AD, 0x6C48916E, 0x7553A02F, 0x3A1236E8, 0x230907A9, 0x0824546A, 0x113F652B,
+ 0x96A779E4, 0x8FBC48A5, 0xA4911B66, 0xBD8A2A27, 0xF2CBBCE0, 0xEBD08DA1, 0xC0FDDE62, 0xD9E6EF23,
+ 0x14BCE1BD, 0x0DA7D0FC, 0x268A833F, 0x3F91B27E, 0x70D024B9, 0x69CB15F8, 0x42E6463B, 0x5BFD777A,
+ 0xDC656BB5, 0xC57E5AF4, 0xEE530937, 0xF7483876, 0xB809AEB1, 0xA1129FF0, 0x8A3FCC33, 0x9324FD72},
+
+ {0x00000000, 0x01C26A37, 0x0384D46E, 0x0246BE59, 0x0709A8DC, 0x06CBC2EB, 0x048D7CB2, 0x054F1685,
+ 0x0E1351B8, 0x0FD13B8F, 0x0D9785D6, 0x0C55EFE1, 0x091AF964, 0x08D89353, 0x0A9E2D0A, 0x0B5C473D,
+ 0x1C26A370, 0x1DE4C947, 0x1FA2771E, 0x1E601D29, 0x1B2F0BAC, 0x1AED619B, 0x18ABDFC2, 0x1969B5F5,
+ 0x1235F2C8, 0x13F798FF, 0x11B126A6, 0x10734C91, 0x153C5A14, 0x14FE3023, 0x16B88E7A, 0x177AE44D,
+ 0x384D46E0, 0x398F2CD7, 0x3BC9928E, 0x3A0BF8B9, 0x3F44EE3C, 0x3E86840B, 0x3CC03A52, 0x3D025065,
+ 0x365E1758, 0x379C7D6F, 0x35DAC336, 0x3418A901, 0x3157BF84, 0x3095D5B3, 0x32D36BEA, 0x331101DD,
+ 0x246BE590, 0x25A98FA7, 0x27EF31FE, 0x262D5BC9, 0x23624D4C, 0x22A0277B, 0x20E69922, 0x2124F315,
+ 0x2A78B428, 0x2BBADE1F, 0x29FC6046, 0x283E0A71, 0x2D711CF4, 0x2CB376C3, 0x2EF5C89A, 0x2F37A2AD,
+ 0x709A8DC0, 0x7158E7F7, 0x731E59AE, 0x72DC3399, 0x7793251C, 0x76514F2B, 0x7417F172, 0x75D59B45,
+ 0x7E89DC78, 0x7F4BB64F, 0x7D0D0816, 0x7CCF6221, 0x798074A4, 0x78421E93, 0x7A04A0CA, 0x7BC6CAFD,
+ 0x6CBC2EB0, 0x6D7E4487, 0x6F38FADE, 0x6EFA90E9, 0x6BB5866C, 0x6A77EC5B, 0x68315202, 0x69F33835,
+ 0x62AF7F08, 0x636D153F, 0x612BAB66, 0x60E9C151, 0x65A6D7D4, 0x6464BDE3, 0x662203BA, 0x67E0698D,
+ 0x48D7CB20, 0x4915A117, 0x4B531F4E, 0x4A917579, 0x4FDE63FC, 0x4E1C09CB, 0x4C5AB792, 0x4D98DDA5,
+ 0x46C49A98, 0x4706F0AF, 0x45404EF6, 0x448224C1, 0x41CD3244, 0x400F5873, 0x4249E62A, 0x438B8C1D,
+ 0x54F16850, 0x55330267, 0x5775BC3E, 0x56B7D609, 0x53F8C08C, 0x523AAABB, 0x507C14E2, 0x51BE7ED5,
+ 0x5AE239E8, 0x5B2053DF, 0x5966ED86, 0x58A487B1, 0x5DEB9134, 0x5C29FB03, 0x5E6F455A, 0x5FAD2F6D,
+ 0xE1351B80, 0xE0F771B7, 0xE2B1CFEE, 0xE373A5D9, 0xE63CB35C, 0xE7FED96B, 0xE5B86732, 0xE47A0D05,
+ 0xEF264A38, 0xEEE4200F, 0xECA29E56, 0xED60F461, 0xE82FE2E4, 0xE9ED88D3, 0xEBAB368A, 0xEA695CBD,
+ 0xFD13B8F0, 0xFCD1D2C7, 0xFE976C9E, 0xFF5506A9, 0xFA1A102C, 0xFBD87A1B, 0xF99EC442, 0xF85CAE75,
+ 0xF300E948, 0xF2C2837F, 0xF0843D26, 0xF1465711, 0xF4094194, 0xF5CB2BA3, 0xF78D95FA, 0xF64FFFCD,
+ 0xD9785D60, 0xD8BA3757, 0xDAFC890E, 0xDB3EE339, 0xDE71F5BC, 0xDFB39F8B, 0xDDF521D2, 0xDC374BE5,
+ 0xD76B0CD8, 0xD6A966EF, 0xD4EFD8B6, 0xD52DB281, 0xD062A404, 0xD1A0CE33, 0xD3E6706A, 0xD2241A5D,
+ 0xC55EFE10, 0xC49C9427, 0xC6DA2A7E, 0xC7184049, 0xC25756CC, 0xC3953CFB, 0xC1D382A2, 0xC011E895,
+ 0xCB4DAFA8, 0xCA8FC59F, 0xC8C97BC6, 0xC90B11F1, 0xCC440774, 0xCD866D43, 0xCFC0D31A, 0xCE02B92D,
+ 0x91AF9640, 0x906DFC77, 0x922B422E, 0x93E92819, 0x96A63E9C, 0x976454AB, 0x9522EAF2, 0x94E080C5,
+ 0x9FBCC7F8, 0x9E7EADCF, 0x9C381396, 0x9DFA79A1, 0x98B56F24, 0x99770513, 0x9B31BB4A, 0x9AF3D17D,
+ 0x8D893530, 0x8C4B5F07, 0x8E0DE15E, 0x8FCF8B69, 0x8A809DEC, 0x8B42F7DB, 0x89044982, 0x88C623B5,
+ 0x839A6488, 0x82580EBF, 0x801EB0E6, 0x81DCDAD1, 0x8493CC54, 0x8551A663, 0x8717183A, 0x86D5720D,
+ 0xA9E2D0A0, 0xA820BA97, 0xAA6604CE, 0xABA46EF9, 0xAEEB787C, 0xAF29124B, 0xAD6FAC12, 0xACADC625,
+ 0xA7F18118, 0xA633EB2F, 0xA4755576, 0xA5B73F41, 0xA0F829C4, 0xA13A43F3, 0xA37CFDAA, 0xA2BE979D,
+ 0xB5C473D0, 0xB40619E7, 0xB640A7BE, 0xB782CD89, 0xB2CDDB0C, 0xB30FB13B, 0xB1490F62, 0xB08B6555,
+ 0xBBD72268, 0xBA15485F, 0xB853F606, 0xB9919C31, 0xBCDE8AB4, 0xBD1CE083, 0xBF5A5EDA, 0xBE9834ED},
+
+ {0x00000000, 0xB8BC6765, 0xAA09C88B, 0x12B5AFEE, 0x8F629757, 0x37DEF032, 0x256B5FDC, 0x9DD738B9,
+ 0xC5B428EF, 0x7D084F8A, 0x6FBDE064, 0xD7018701, 0x4AD6BFB8, 0xF26AD8DD, 0xE0DF7733, 0x58631056,
+ 0x5019579F, 0xE8A530FA, 0xFA109F14, 0x42ACF871, 0xDF7BC0C8, 0x67C7A7AD, 0x75720843, 0xCDCE6F26,
+ 0x95AD7F70, 0x2D111815, 0x3FA4B7FB, 0x8718D09E, 0x1ACFE827, 0xA2738F42, 0xB0C620AC, 0x087A47C9,
+ 0xA032AF3E, 0x188EC85B, 0x0A3B67B5, 0xB28700D0, 0x2F503869, 0x97EC5F0C, 0x8559F0E2, 0x3DE59787,
+ 0x658687D1, 0xDD3AE0B4, 0xCF8F4F5A, 0x7733283F, 0xEAE41086, 0x525877E3, 0x40EDD80D, 0xF851BF68,
+ 0xF02BF8A1, 0x48979FC4, 0x5A22302A, 0xE29E574F, 0x7F496FF6, 0xC7F50893, 0xD540A77D, 0x6DFCC018,
+ 0x359FD04E, 0x8D23B72B, 0x9F9618C5, 0x272A7FA0, 0xBAFD4719, 0x0241207C, 0x10F48F92, 0xA848E8F7,
+ 0x9B14583D, 0x23A83F58, 0x311D90B6, 0x89A1F7D3, 0x1476CF6A, 0xACCAA80F, 0xBE7F07E1, 0x06C36084,
+ 0x5EA070D2, 0xE61C17B7, 0xF4A9B859, 0x4C15DF3C, 0xD1C2E785, 0x697E80E0, 0x7BCB2F0E, 0xC377486B,
+ 0xCB0D0FA2, 0x73B168C7, 0x6104C729, 0xD9B8A04C, 0x446F98F5, 0xFCD3FF90, 0xEE66507E, 0x56DA371B,
+ 0x0EB9274D, 0xB6054028, 0xA4B0EFC6, 0x1C0C88A3, 0x81DBB01A, 0x3967D77F, 0x2BD27891, 0x936E1FF4,
+ 0x3B26F703, 0x839A9066, 0x912F3F88, 0x299358ED, 0xB4446054, 0x0CF80731, 0x1E4DA8DF, 0xA6F1CFBA,
+ 0xFE92DFEC, 0x462EB889, 0x549B1767, 0xEC277002, 0x71F048BB, 0xC94C2FDE, 0xDBF98030, 0x6345E755,
+ 0x6B3FA09C, 0xD383C7F9, 0xC1366817, 0x798A0F72, 0xE45D37CB, 0x5CE150AE, 0x4E54FF40, 0xF6E89825,
+ 0xAE8B8873, 0x1637EF16, 0x048240F8, 0xBC3E279D, 0x21E91F24, 0x99557841, 0x8BE0D7AF, 0x335CB0CA,
+ 0xED59B63B, 0x55E5D15E, 0x47507EB0, 0xFFEC19D5, 0x623B216C, 0xDA874609, 0xC832E9E7, 0x708E8E82,
+ 0x28ED9ED4, 0x9051F9B1, 0x82E4565F, 0x3A58313A, 0xA78F0983, 0x1F336EE6, 0x0D86C108, 0xB53AA66D,
+ 0xBD40E1A4, 0x05FC86C1, 0x1749292F, 0xAFF54E4A, 0x322276F3, 0x8A9E1196, 0x982BBE78, 0x2097D91D,
+ 0x78F4C94B, 0xC048AE2E, 0xD2FD01C0, 0x6A4166A5, 0xF7965E1C, 0x4F2A3979, 0x5D9F9697, 0xE523F1F2,
+ 0x4D6B1905, 0xF5D77E60, 0xE762D18E, 0x5FDEB6EB, 0xC2098E52, 0x7AB5E937, 0x680046D9, 0xD0BC21BC,
+ 0x88DF31EA, 0x3063568F, 0x22D6F961, 0x9A6A9E04, 0x07BDA6BD, 0xBF01C1D8, 0xADB46E36, 0x15080953,
+ 0x1D724E9A, 0xA5CE29FF, 0xB77B8611, 0x0FC7E174, 0x9210D9CD, 0x2AACBEA8, 0x38191146, 0x80A57623,
+ 0xD8C66675, 0x607A0110, 0x72CFAEFE, 0xCA73C99B, 0x57A4F122, 0xEF189647, 0xFDAD39A9, 0x45115ECC,
+ 0x764DEE06, 0xCEF18963, 0xDC44268D, 0x64F841E8, 0xF92F7951, 0x41931E34, 0x5326B1DA, 0xEB9AD6BF,
+ 0xB3F9C6E9, 0x0B45A18C, 0x19F00E62, 0xA14C6907, 0x3C9B51BE, 0x842736DB, 0x96929935, 0x2E2EFE50,
+ 0x2654B999, 0x9EE8DEFC, 0x8C5D7112, 0x34E11677, 0xA9362ECE, 0x118A49AB, 0x033FE645, 0xBB838120,
+ 0xE3E09176, 0x5B5CF613, 0x49E959FD, 0xF1553E98, 0x6C820621, 0xD43E6144, 0xC68BCEAA, 0x7E37A9CF,
+ 0xD67F4138, 0x6EC3265D, 0x7C7689B3, 0xC4CAEED6, 0x591DD66F, 0xE1A1B10A, 0xF3141EE4, 0x4BA87981,
+ 0x13CB69D7, 0xAB770EB2, 0xB9C2A15C, 0x017EC639, 0x9CA9FE80, 0x241599E5, 0x36A0360B, 0x8E1C516E,
+ 0x866616A7, 0x3EDA71C2, 0x2C6FDE2C, 0x94D3B949, 0x090481F0, 0xB1B8E695, 0xA30D497B, 0x1BB12E1E,
+ 0x43D23E48, 0xFB6E592D, 0xE9DBF6C3, 0x516791A6, 0xCCB0A91F, 0x740CCE7A, 0x66B96194, 0xDE0506F1},
+
+ {0x00000000, 0x3D6029B0, 0x7AC05360, 0x47A07AD0, 0xF580A6C0, 0xC8E08F70, 0x8F40F5A0, 0xB220DC10,
+ 0x30704BC1, 0x0D106271, 0x4AB018A1, 0x77D03111, 0xC5F0ED01, 0xF890C4B1, 0xBF30BE61, 0x825097D1,
+ 0x60E09782, 0x5D80BE32, 0x1A20C4E2, 0x2740ED52, 0x95603142, 0xA80018F2, 0xEFA06222, 0xD2C04B92,
+ 0x5090DC43, 0x6DF0F5F3, 0x2A508F23, 0x1730A693, 0xA5107A83, 0x98705333, 0xDFD029E3, 0xE2B00053,
+ 0xC1C12F04, 0xFCA106B4, 0xBB017C64, 0x866155D4, 0x344189C4, 0x0921A074, 0x4E81DAA4, 0x73E1F314,
+ 0xF1B164C5, 0xCCD14D75, 0x8B7137A5, 0xB6111E15, 0x0431C205, 0x3951EBB5, 0x7EF19165, 0x4391B8D5,
+ 0xA121B886, 0x9C419136, 0xDBE1EBE6, 0xE681C256, 0x54A11E46, 0x69C137F6, 0x2E614D26, 0x13016496,
+ 0x9151F347, 0xAC31DAF7, 0xEB91A027, 0xD6F18997, 0x64D15587, 0x59B17C37, 0x1E1106E7, 0x23712F57,
+ 0x58F35849, 0x659371F9, 0x22330B29, 0x1F532299, 0xAD73FE89, 0x9013D739, 0xD7B3ADE9, 0xEAD38459,
+ 0x68831388, 0x55E33A38, 0x124340E8, 0x2F236958, 0x9D03B548, 0xA0639CF8, 0xE7C3E628, 0xDAA3CF98,
+ 0x3813CFCB, 0x0573E67B, 0x42D39CAB, 0x7FB3B51B, 0xCD93690B, 0xF0F340BB, 0xB7533A6B, 0x8A3313DB,
+ 0x0863840A, 0x3503ADBA, 0x72A3D76A, 0x4FC3FEDA, 0xFDE322CA, 0xC0830B7A, 0x872371AA, 0xBA43581A,
+ 0x9932774D, 0xA4525EFD, 0xE3F2242D, 0xDE920D9D, 0x6CB2D18D, 0x51D2F83D, 0x167282ED, 0x2B12AB5D,
+ 0xA9423C8C, 0x9422153C, 0xD3826FEC, 0xEEE2465C, 0x5CC29A4C, 0x61A2B3FC, 0x2602C92C, 0x1B62E09C,
+ 0xF9D2E0CF, 0xC4B2C97F, 0x8312B3AF, 0xBE729A1F, 0x0C52460F, 0x31326FBF, 0x7692156F, 0x4BF23CDF,
+ 0xC9A2AB0E, 0xF4C282BE, 0xB362F86E, 0x8E02D1DE, 0x3C220DCE, 0x0142247E, 0x46E25EAE, 0x7B82771E,
+ 0xB1E6B092, 0x8C869922, 0xCB26E3F2, 0xF646CA42, 0x44661652, 0x79063FE2, 0x3EA64532, 0x03C66C82,
+ 0x8196FB53, 0xBCF6D2E3, 0xFB56A833, 0xC6368183, 0x74165D93, 0x49767423, 0x0ED60EF3, 0x33B62743,
+ 0xD1062710, 0xEC660EA0, 0xABC67470, 0x96A65DC0, 0x248681D0, 0x19E6A860, 0x5E46D2B0, 0x6326FB00,
+ 0xE1766CD1, 0xDC164561, 0x9BB63FB1, 0xA6D61601, 0x14F6CA11, 0x2996E3A1, 0x6E369971, 0x5356B0C1,
+ 0x70279F96, 0x4D47B626, 0x0AE7CCF6, 0x3787E546, 0x85A73956, 0xB8C710E6, 0xFF676A36, 0xC2074386,
+ 0x4057D457, 0x7D37FDE7, 0x3A978737, 0x07F7AE87, 0xB5D77297, 0x88B75B27, 0xCF1721F7, 0xF2770847,
+ 0x10C70814, 0x2DA721A4, 0x6A075B74, 0x576772C4, 0xE547AED4, 0xD8278764, 0x9F87FDB4, 0xA2E7D404,
+ 0x20B743D5, 0x1DD76A65, 0x5A7710B5, 0x67173905, 0xD537E515, 0xE857CCA5, 0xAFF7B675, 0x92979FC5,
+ 0xE915E8DB, 0xD475C16B, 0x93D5BBBB, 0xAEB5920B, 0x1C954E1B, 0x21F567AB, 0x66551D7B, 0x5B3534CB,
+ 0xD965A31A, 0xE4058AAA, 0xA3A5F07A, 0x9EC5D9CA, 0x2CE505DA, 0x11852C6A, 0x562556BA, 0x6B457F0A,
+ 0x89F57F59, 0xB49556E9, 0xF3352C39, 0xCE550589, 0x7C75D999, 0x4115F029, 0x06B58AF9, 0x3BD5A349,
+ 0xB9853498, 0x84E51D28, 0xC34567F8, 0xFE254E48, 0x4C059258, 0x7165BBE8, 0x36C5C138, 0x0BA5E888,
+ 0x28D4C7DF, 0x15B4EE6F, 0x521494BF, 0x6F74BD0F, 0xDD54611F, 0xE03448AF, 0xA794327F, 0x9AF41BCF,
+ 0x18A48C1E, 0x25C4A5AE, 0x6264DF7E, 0x5F04F6CE, 0xED242ADE, 0xD044036E, 0x97E479BE, 0xAA84500E,
+ 0x4834505D, 0x755479ED, 0x32F4033D, 0x0F942A8D, 0xBDB4F69D, 0x80D4DF2D, 0xC774A5FD, 0xFA148C4D,
+ 0x78441B9C, 0x4524322C, 0x028448FC, 0x3FE4614C, 0x8DC4BD5C, 0xB0A494EC, 0xF704EE3C, 0xCA64C78C},
+
+ {0x00000000, 0xCB5CD3A5, 0x4DC8A10B, 0x869472AE, 0x9B914216, 0x50CD91B3, 0xD659E31D, 0x1D0530B8,
+ 0xEC53826D, 0x270F51C8, 0xA19B2366, 0x6AC7F0C3, 0x77C2C07B, 0xBC9E13DE, 0x3A0A6170, 0xF156B2D5,
+ 0x03D6029B, 0xC88AD13E, 0x4E1EA390, 0x85427035, 0x9847408D, 0x531B9328, 0xD58FE186, 0x1ED33223,
+ 0xEF8580F6, 0x24D95353, 0xA24D21FD, 0x6911F258, 0x7414C2E0, 0xBF481145, 0x39DC63EB, 0xF280B04E,
+ 0x07AC0536, 0xCCF0D693, 0x4A64A43D, 0x81387798, 0x9C3D4720, 0x57619485, 0xD1F5E62B, 0x1AA9358E,
+ 0xEBFF875B, 0x20A354FE, 0xA6372650, 0x6D6BF5F5, 0x706EC54D, 0xBB3216E8, 0x3DA66446, 0xF6FAB7E3,
+ 0x047A07AD, 0xCF26D408, 0x49B2A6A6, 0x82EE7503, 0x9FEB45BB, 0x54B7961E, 0xD223E4B0, 0x197F3715,
+ 0xE82985C0, 0x23755665, 0xA5E124CB, 0x6EBDF76E, 0x73B8C7D6, 0xB8E41473, 0x3E7066DD, 0xF52CB578,
+ 0x0F580A6C, 0xC404D9C9, 0x4290AB67, 0x89CC78C2, 0x94C9487A, 0x5F959BDF, 0xD901E971, 0x125D3AD4,
+ 0xE30B8801, 0x28575BA4, 0xAEC3290A, 0x659FFAAF, 0x789ACA17, 0xB3C619B2, 0x35526B1C, 0xFE0EB8B9,
+ 0x0C8E08F7, 0xC7D2DB52, 0x4146A9FC, 0x8A1A7A59, 0x971F4AE1, 0x5C439944, 0xDAD7EBEA, 0x118B384F,
+ 0xE0DD8A9A, 0x2B81593F, 0xAD152B91, 0x6649F834, 0x7B4CC88C, 0xB0101B29, 0x36846987, 0xFDD8BA22,
+ 0x08F40F5A, 0xC3A8DCFF, 0x453CAE51, 0x8E607DF4, 0x93654D4C, 0x58399EE9, 0xDEADEC47, 0x15F13FE2,
+ 0xE4A78D37, 0x2FFB5E92, 0xA96F2C3C, 0x6233FF99, 0x7F36CF21, 0xB46A1C84, 0x32FE6E2A, 0xF9A2BD8F,
+ 0x0B220DC1, 0xC07EDE64, 0x46EAACCA, 0x8DB67F6F, 0x90B34FD7, 0x5BEF9C72, 0xDD7BEEDC, 0x16273D79,
+ 0xE7718FAC, 0x2C2D5C09, 0xAAB92EA7, 0x61E5FD02, 0x7CE0CDBA, 0xB7BC1E1F, 0x31286CB1, 0xFA74BF14,
+ 0x1EB014D8, 0xD5ECC77D, 0x5378B5D3, 0x98246676, 0x852156CE, 0x4E7D856B, 0xC8E9F7C5, 0x03B52460,
+ 0xF2E396B5, 0x39BF4510, 0xBF2B37BE, 0x7477E41B, 0x6972D4A3, 0xA22E0706, 0x24BA75A8, 0xEFE6A60D,
+ 0x1D661643, 0xD63AC5E6, 0x50AEB748, 0x9BF264ED, 0x86F75455, 0x4DAB87F0, 0xCB3FF55E, 0x006326FB,
+ 0xF135942E, 0x3A69478B, 0xBCFD3525, 0x77A1E680, 0x6AA4D638, 0xA1F8059D, 0x276C7733, 0xEC30A496,
+ 0x191C11EE, 0xD240C24B, 0x54D4B0E5, 0x9F886340, 0x828D53F8, 0x49D1805D, 0xCF45F2F3, 0x04192156,
+ 0xF54F9383, 0x3E134026, 0xB8873288, 0x73DBE12D, 0x6EDED195, 0xA5820230, 0x2316709E, 0xE84AA33B,
+ 0x1ACA1375, 0xD196C0D0, 0x5702B27E, 0x9C5E61DB, 0x815B5163, 0x4A0782C6, 0xCC93F068, 0x07CF23CD,
+ 0xF6999118, 0x3DC542BD, 0xBB513013, 0x700DE3B6, 0x6D08D30E, 0xA65400AB, 0x20C07205, 0xEB9CA1A0,
+ 0x11E81EB4, 0xDAB4CD11, 0x5C20BFBF, 0x977C6C1A, 0x8A795CA2, 0x41258F07, 0xC7B1FDA9, 0x0CED2E0C,
+ 0xFDBB9CD9, 0x36E74F7C, 0xB0733DD2, 0x7B2FEE77, 0x662ADECF, 0xAD760D6A, 0x2BE27FC4, 0xE0BEAC61,
+ 0x123E1C2F, 0xD962CF8A, 0x5FF6BD24, 0x94AA6E81, 0x89AF5E39, 0x42F38D9C, 0xC467FF32, 0x0F3B2C97,
+ 0xFE6D9E42, 0x35314DE7, 0xB3A53F49, 0x78F9ECEC, 0x65FCDC54, 0xAEA00FF1, 0x28347D5F, 0xE368AEFA,
+ 0x16441B82, 0xDD18C827, 0x5B8CBA89, 0x90D0692C, 0x8DD55994, 0x46898A31, 0xC01DF89F, 0x0B412B3A,
+ 0xFA1799EF, 0x314B4A4A, 0xB7DF38E4, 0x7C83EB41, 0x6186DBF9, 0xAADA085C, 0x2C4E7AF2, 0xE712A957,
+ 0x15921919, 0xDECECABC, 0x585AB812, 0x93066BB7, 0x8E035B0F, 0x455F88AA, 0xC3CBFA04, 0x089729A1,
+ 0xF9C19B74, 0x329D48D1, 0xB4093A7F, 0x7F55E9DA, 0x6250D962, 0xA90C0AC7, 0x2F987869, 0xE4C4ABCC},
+
+ {0x00000000, 0xA6770BB4, 0x979F1129, 0x31E81A9D, 0xF44F2413, 0x52382FA7, 0x63D0353A, 0xC5A73E8E,
+ 0x33EF4E67, 0x959845D3, 0xA4705F4E, 0x020754FA, 0xC7A06A74, 0x61D761C0, 0x503F7B5D, 0xF64870E9,
+ 0x67DE9CCE, 0xC1A9977A, 0xF0418DE7, 0x56368653, 0x9391B8DD, 0x35E6B369, 0x040EA9F4, 0xA279A240,
+ 0x5431D2A9, 0xF246D91D, 0xC3AEC380, 0x65D9C834, 0xA07EF6BA, 0x0609FD0E, 0x37E1E793, 0x9196EC27,
+ 0xCFBD399C, 0x69CA3228, 0x582228B5, 0xFE552301, 0x3BF21D8F, 0x9D85163B, 0xAC6D0CA6, 0x0A1A0712,
+ 0xFC5277FB, 0x5A257C4F, 0x6BCD66D2, 0xCDBA6D66, 0x081D53E8, 0xAE6A585C, 0x9F8242C1, 0x39F54975,
+ 0xA863A552, 0x0E14AEE6, 0x3FFCB47B, 0x998BBFCF, 0x5C2C8141, 0xFA5B8AF5, 0xCBB39068, 0x6DC49BDC,
+ 0x9B8CEB35, 0x3DFBE081, 0x0C13FA1C, 0xAA64F1A8, 0x6FC3CF26, 0xC9B4C492, 0xF85CDE0F, 0x5E2BD5BB,
+ 0x440B7579, 0xE27C7ECD, 0xD3946450, 0x75E36FE4, 0xB044516A, 0x16335ADE, 0x27DB4043, 0x81AC4BF7,
+ 0x77E43B1E, 0xD19330AA, 0xE07B2A37, 0x460C2183, 0x83AB1F0D, 0x25DC14B9, 0x14340E24, 0xB2430590,
+ 0x23D5E9B7, 0x85A2E203, 0xB44AF89E, 0x123DF32A, 0xD79ACDA4, 0x71EDC610, 0x4005DC8D, 0xE672D739,
+ 0x103AA7D0, 0xB64DAC64, 0x87A5B6F9, 0x21D2BD4D, 0xE47583C3, 0x42028877, 0x73EA92EA, 0xD59D995E,
+ 0x8BB64CE5, 0x2DC14751, 0x1C295DCC, 0xBA5E5678, 0x7FF968F6, 0xD98E6342, 0xE86679DF, 0x4E11726B,
+ 0xB8590282, 0x1E2E0936, 0x2FC613AB, 0x89B1181F, 0x4C162691, 0xEA612D25, 0xDB8937B8, 0x7DFE3C0C,
+ 0xEC68D02B, 0x4A1FDB9F, 0x7BF7C102, 0xDD80CAB6, 0x1827F438, 0xBE50FF8C, 0x8FB8E511, 0x29CFEEA5,
+ 0xDF879E4C, 0x79F095F8, 0x48188F65, 0xEE6F84D1, 0x2BC8BA5F, 0x8DBFB1EB, 0xBC57AB76, 0x1A20A0C2,
+ 0x8816EAF2, 0x2E61E146, 0x1F89FBDB, 0xB9FEF06F, 0x7C59CEE1, 0xDA2EC555, 0xEBC6DFC8, 0x4DB1D47C,
+ 0xBBF9A495, 0x1D8EAF21, 0x2C66B5BC, 0x8A11BE08, 0x4FB68086, 0xE9C18B32, 0xD82991AF, 0x7E5E9A1B,
+ 0xEFC8763C, 0x49BF7D88, 0x78576715, 0xDE206CA1, 0x1B87522F, 0xBDF0599B, 0x8C184306, 0x2A6F48B2,
+ 0xDC27385B, 0x7A5033EF, 0x4BB82972, 0xEDCF22C6, 0x28681C48, 0x8E1F17FC, 0xBFF70D61, 0x198006D5,
+ 0x47ABD36E, 0xE1DCD8DA, 0xD034C247, 0x7643C9F3, 0xB3E4F77D, 0x1593FCC9, 0x247BE654, 0x820CEDE0,
+ 0x74449D09, 0xD23396BD, 0xE3DB8C20, 0x45AC8794, 0x800BB91A, 0x267CB2AE, 0x1794A833, 0xB1E3A387,
+ 0x20754FA0, 0x86024414, 0xB7EA5E89, 0x119D553D, 0xD43A6BB3, 0x724D6007, 0x43A57A9A, 0xE5D2712E,
+ 0x139A01C7, 0xB5ED0A73, 0x840510EE, 0x22721B5A, 0xE7D525D4, 0x41A22E60, 0x704A34FD, 0xD63D3F49,
+ 0xCC1D9F8B, 0x6A6A943F, 0x5B828EA2, 0xFDF58516, 0x3852BB98, 0x9E25B02C, 0xAFCDAAB1, 0x09BAA105,
+ 0xFFF2D1EC, 0x5985DA58, 0x686DC0C5, 0xCE1ACB71, 0x0BBDF5FF, 0xADCAFE4B, 0x9C22E4D6, 0x3A55EF62,
+ 0xABC30345, 0x0DB408F1, 0x3C5C126C, 0x9A2B19D8, 0x5F8C2756, 0xF9FB2CE2, 0xC813367F, 0x6E643DCB,
+ 0x982C4D22, 0x3E5B4696, 0x0FB35C0B, 0xA9C457BF, 0x6C636931, 0xCA146285, 0xFBFC7818, 0x5D8B73AC,
+ 0x03A0A617, 0xA5D7ADA3, 0x943FB73E, 0x3248BC8A, 0xF7EF8204, 0x519889B0, 0x6070932D, 0xC6079899,
+ 0x304FE870, 0x9638E3C4, 0xA7D0F959, 0x01A7F2ED, 0xC400CC63, 0x6277C7D7, 0x539FDD4A, 0xF5E8D6FE,
+ 0x647E3AD9, 0xC209316D, 0xF3E12BF0, 0x55962044, 0x90311ECA, 0x3646157E, 0x07AE0FE3, 0xA1D90457,
+ 0x579174BE, 0xF1E67F0A, 0xC00E6597, 0x66796E23, 0xA3DE50AD, 0x05A95B19, 0x34414184, 0x92364A30},
+
+ {0x00000000, 0xCCAA009E, 0x4225077D, 0x8E8F07E3, 0x844A0EFA, 0x48E00E64, 0xC66F0987, 0x0AC50919,
+ 0xD3E51BB5, 0x1F4F1B2B, 0x91C01CC8, 0x5D6A1C56, 0x57AF154F, 0x9B0515D1, 0x158A1232, 0xD92012AC,
+ 0x7CBB312B, 0xB01131B5, 0x3E9E3656, 0xF23436C8, 0xF8F13FD1, 0x345B3F4F, 0xBAD438AC, 0x767E3832,
+ 0xAF5E2A9E, 0x63F42A00, 0xED7B2DE3, 0x21D12D7D, 0x2B142464, 0xE7BE24FA, 0x69312319, 0xA59B2387,
+ 0xF9766256, 0x35DC62C8, 0xBB53652B, 0x77F965B5, 0x7D3C6CAC, 0xB1966C32, 0x3F196BD1, 0xF3B36B4F,
+ 0x2A9379E3, 0xE639797D, 0x68B67E9E, 0xA41C7E00, 0xAED97719, 0x62737787, 0xECFC7064, 0x205670FA,
+ 0x85CD537D, 0x496753E3, 0xC7E85400, 0x0B42549E, 0x01875D87, 0xCD2D5D19, 0x43A25AFA, 0x8F085A64,
+ 0x562848C8, 0x9A824856, 0x140D4FB5, 0xD8A74F2B, 0xD2624632, 0x1EC846AC, 0x9047414F, 0x5CED41D1,
+ 0x299DC2ED, 0xE537C273, 0x6BB8C590, 0xA712C50E, 0xADD7CC17, 0x617DCC89, 0xEFF2CB6A, 0x2358CBF4,
+ 0xFA78D958, 0x36D2D9C6, 0xB85DDE25, 0x74F7DEBB, 0x7E32D7A2, 0xB298D73C, 0x3C17D0DF, 0xF0BDD041,
+ 0x5526F3C6, 0x998CF358, 0x1703F4BB, 0xDBA9F425, 0xD16CFD3C, 0x1DC6FDA2, 0x9349FA41, 0x5FE3FADF,
+ 0x86C3E873, 0x4A69E8ED, 0xC4E6EF0E, 0x084CEF90, 0x0289E689, 0xCE23E617, 0x40ACE1F4, 0x8C06E16A,
+ 0xD0EBA0BB, 0x1C41A025, 0x92CEA7C6, 0x5E64A758, 0x54A1AE41, 0x980BAEDF, 0x1684A93C, 0xDA2EA9A2,
+ 0x030EBB0E, 0xCFA4BB90, 0x412BBC73, 0x8D81BCED, 0x8744B5F4, 0x4BEEB56A, 0xC561B289, 0x09CBB217,
+ 0xAC509190, 0x60FA910E, 0xEE7596ED, 0x22DF9673, 0x281A9F6A, 0xE4B09FF4, 0x6A3F9817, 0xA6959889,
+ 0x7FB58A25, 0xB31F8ABB, 0x3D908D58, 0xF13A8DC6, 0xFBFF84DF, 0x37558441, 0xB9DA83A2, 0x7570833C,
+ 0x533B85DA, 0x9F918544, 0x111E82A7, 0xDDB48239, 0xD7718B20, 0x1BDB8BBE, 0x95548C5D, 0x59FE8CC3,
+ 0x80DE9E6F, 0x4C749EF1, 0xC2FB9912, 0x0E51998C, 0x04949095, 0xC83E900B, 0x46B197E8, 0x8A1B9776,
+ 0x2F80B4F1, 0xE32AB46F, 0x6DA5B38C, 0xA10FB312, 0xABCABA0B, 0x6760BA95, 0xE9EFBD76, 0x2545BDE8,
+ 0xFC65AF44, 0x30CFAFDA, 0xBE40A839, 0x72EAA8A7, 0x782FA1BE, 0xB485A120, 0x3A0AA6C3, 0xF6A0A65D,
+ 0xAA4DE78C, 0x66E7E712, 0xE868E0F1, 0x24C2E06F, 0x2E07E976, 0xE2ADE9E8, 0x6C22EE0B, 0xA088EE95,
+ 0x79A8FC39, 0xB502FCA7, 0x3B8DFB44, 0xF727FBDA, 0xFDE2F2C3, 0x3148F25D, 0xBFC7F5BE, 0x736DF520,
+ 0xD6F6D6A7, 0x1A5CD639, 0x94D3D1DA, 0x5879D144, 0x52BCD85D, 0x9E16D8C3, 0x1099DF20, 0xDC33DFBE,
+ 0x0513CD12, 0xC9B9CD8C, 0x4736CA6F, 0x8B9CCAF1, 0x8159C3E8, 0x4DF3C376, 0xC37CC495, 0x0FD6C40B,
+ 0x7AA64737, 0xB60C47A9, 0x3883404A, 0xF42940D4, 0xFEEC49CD, 0x32464953, 0xBCC94EB0, 0x70634E2E,
+ 0xA9435C82, 0x65E95C1C, 0xEB665BFF, 0x27CC5B61, 0x2D095278, 0xE1A352E6, 0x6F2C5505, 0xA386559B,
+ 0x061D761C, 0xCAB77682, 0x44387161, 0x889271FF, 0x825778E6, 0x4EFD7878, 0xC0727F9B, 0x0CD87F05,
+ 0xD5F86DA9, 0x19526D37, 0x97DD6AD4, 0x5B776A4A, 0x51B26353, 0x9D1863CD, 0x1397642E, 0xDF3D64B0,
+ 0x83D02561, 0x4F7A25FF, 0xC1F5221C, 0x0D5F2282, 0x079A2B9B, 0xCB302B05, 0x45BF2CE6, 0x89152C78,
+ 0x50353ED4, 0x9C9F3E4A, 0x121039A9, 0xDEBA3937, 0xD47F302E, 0x18D530B0, 0x965A3753, 0x5AF037CD,
+ 0xFF6B144A, 0x33C114D4, 0xBD4E1337, 0x71E413A9, 0x7B211AB0, 0xB78B1A2E, 0x39041DCD, 0xF5AE1D53,
+ 0x2C8E0FFF, 0xE0240F61, 0x6EAB0882, 0xA201081C, 0xA8C40105, 0x646E019B, 0xEAE10678, 0x264B06E6}};
+
+inline uint32_t swap(uint32_t x) {
+#if defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap32(x);
+#endif
+#ifdef MSC_VER
+ return _byteswap_ulong(x);
+#endif
+
+ return (x >> 24) |
+ ((x >> 8) & 0x0000FF00) |
+ ((x << 8) & 0x00FF0000) |
+ (x << 24);
+}
+} // namespace
+
+/// add arbitrary number of bytes
+void CRC32::add(const void *data, size_t numBytes) {
+ uint32_t *current = (uint32_t *)data;
+ uint32_t crc = ~m_hash;
+
+ // process eight bytes at once
+ while (numBytes >= 8) {
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
+ uint32_t one = *current++ ^ swap(crc);
+ uint32_t two = *current++;
+ crc = crc32Lookup[7][one >> 24] ^
+ crc32Lookup[6][(one >> 16) & 0xFF] ^
+ crc32Lookup[5][(one >> 8) & 0xFF] ^
+ crc32Lookup[4][one & 0xFF] ^
+ crc32Lookup[3][two >> 24] ^
+ crc32Lookup[2][(two >> 16) & 0xFF] ^
+ crc32Lookup[1][(two >> 8) & 0xFF] ^
+ crc32Lookup[0][two & 0xFF];
+#else
+ uint32_t one = *current++ ^ crc;
+ uint32_t two = *current++;
+ crc = crc32Lookup[7][one & 0xFF] ^
+ crc32Lookup[6][(one >> 8) & 0xFF] ^
+ crc32Lookup[5][(one >> 16) & 0xFF] ^
+ crc32Lookup[4][one >> 24] ^
+ crc32Lookup[3][two & 0xFF] ^
+ crc32Lookup[2][(two >> 8) & 0xFF] ^
+ crc32Lookup[1][(two >> 16) & 0xFF] ^
+ crc32Lookup[0][two >> 24];
+#endif
+ numBytes -= 8;
+ }
+
+ unsigned char *currentChar = (unsigned char *)current;
+ // remaining 1 to 7 bytes (standard CRC table-based algorithm)
+ while (numBytes--)
+ crc = (crc >> 8) ^ crc32Lookup[0][(crc & 0xFF) ^ *currentChar++];
+
+ m_hash = ~crc;
+}
+
+/// return latest hash as 8 hex characters
+std::string CRC32::getHash() {
+ // convert hash to string
+ static const char dec2hex[16 + 1] = "0123456789abcdef";
+
+ char hashBuffer[8 + 1];
+
+ hashBuffer[0] = dec2hex[m_hash >> 28];
+ hashBuffer[1] = dec2hex[(m_hash >> 24) & 15];
+ hashBuffer[2] = dec2hex[(m_hash >> 20) & 15];
+ hashBuffer[3] = dec2hex[(m_hash >> 16) & 15];
+ hashBuffer[4] = dec2hex[(m_hash >> 12) & 15];
+ hashBuffer[5] = dec2hex[(m_hash >> 8) & 15];
+ hashBuffer[6] = dec2hex[(m_hash >> 4) & 15];
+ hashBuffer[7] = dec2hex[m_hash & 15];
+ // zero-terminated string
+ hashBuffer[8] = 0;
+
+ // convert to std::string
+ return hashBuffer;
+}
+
+/// return latest hash as bytes
+void CRC32::getHash(unsigned char buffer[CRC32::HashBytes]) {
+ buffer[0] = (m_hash >> 24) & 0xFF;
+ buffer[1] = (m_hash >> 16) & 0xFF;
+ buffer[2] = (m_hash >> 8) & 0xFF;
+ buffer[3] = m_hash & 0xFF;
+}
+
+/// compute CRC32 of a memory block
+std::string CRC32::operator()(const void *data, size_t numBytes) {
+ reset();
+ add(data, numBytes);
+ return getHash();
+}
+
+/// compute CRC32 of a string, excluding final zero
+std::string CRC32::operator()(const std::string &text) {
+ reset();
+ add(text.c_str(), text.size());
+ return getHash();
+}
diff --git a/external/libdumper/source/dump.cpp b/external/libdumper/source/dump.cpp
index 39d7331..ee07c83 100644
--- a/external/libdumper/source/dump.cpp
+++ b/external/libdumper/source/dump.cpp
@@ -18,36 +18,200 @@
#include
#include
#include "log.h"
-extern "C" {
+#include
+#include
#include "lang.h"
-}
+#include "fself.hpp"
namespace dump {
+
+bool DecryptAndMakeGP4(const std::string &output_path, const std::string &title_id, Dump_Options opt, const std::string& title, const std::string& DLC_Content_id = ""){
+
+ log_info("DecryptAndMakeGP4(%s, %s, %i, %s, %s)", output_path.c_str(), title_id.c_str(), opt, title.c_str(), DLC_Content_id.c_str());
+ std::filesystem::path sce_sys_path(output_path);
+ sce_sys_path /= "sce_sys";
+ // Generate GP4
+ std::filesystem::path sfo_path(sce_sys_path);
+ sfo_path /= "param.sfo";
+
+ std::filesystem::path gp4_path(output_path);
+ gp4_path /= (opt == ADDITIONAL_CONTENT_DATA) ? DLC_Content_id + ".gp4" : title_id + ".gp4";
+
+ std::filesystem::path sb_path("/mnt/sandbox/pfsmnt/");
+ if (opt == BASE_GAME || opt == REMASTER) {
+ sb_path += title_id + "-app0/";
+ }
+ else if (opt == GAME_PATCH) {
+ sb_path += title_id + "-patch0/";
+ }
+ else if (opt == THEME_UNLOCK || opt == ADDITIONAL_CONTENT_DATA || opt == THEME || opt == ADDITIONAL_CONTENT_NO_DATA) {
+ sb_path += title_id + "-ac/";
+ }
+ else {
+ log_error("Invalid OPT");
+ return false;
+ }
+
+
+ ProgUpdate(98, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + title_id + "\n\n" + getDumperLangSTR(DEC_BIN) + " " + sb_path.string());
+ log_debug("Decrypting Bins in %s ...", sb_path.string().c_str());
+
+ bool fself = false;
+ if(fself::is_fself(sb_path.string() + "/eboot.bin")){
+ fself = true;
+ log_info("eboot is an FSELF");
+ }
+
+ elf::decrypt_dir(sb_path.string(), output_path, fself);
+
+
+ProgUpdate(99, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + title_id + "\n\n" + getDumperLangSTR(CREATING_GP4));
+try {
+ std::vector self_files;
+ if (!gp4::generate(sfo_path.string(), output_path, gp4_path.string(), self_files, opt))
+ return false;
+}
+catch (const std::exception& e)
+{
+ log_error("Gerneating GP4 failed");
+ return false;
+}
+
+return true;
+}
+
+bool dump_dlc(const std::string & usb_device, const std::string & title_id) {
+
+ // Vector of strings for locations of SELF files for decryption
+ std::vector self_files;
+ bool ac_wo_data = false;
+ std::string full_content_id;
+
+ std::vector > dlc_info = query_dlc_database(title_id);
+ std::string addcont_path_str;
+ std::string user_addcont_dir = "/user/addcont/" + title_id;
+ std::string ext_addcont_dir = "/mnt/ext0/user/addcont/" + title_id;
+ std::string disc_addcont_dir = "/mnt/disc/addcont/" + title_id;
+
+ if(std::filesystem::exists(user_addcont_dir)){
+ addcont_path_str = user_addcont_dir;
+ }
+ else if(std::filesystem::exists(ext_addcont_dir)){
+ addcont_path_str = ext_addcont_dir;
+ }
+ else {
+ addcont_path_str = disc_addcont_dir;
+ }
+
+ for (const auto & result: dlc_info) {
+ std::filesystem::path pkg_path(addcont_path_str + "/" + std::get<0>(result));
+ std::string title = std::get<1>(result);
+ pkg_path /= "ac.pkg";
+
+ if (!std::filesystem::exists(pkg_path) || !std::filesystem::is_regular_file(pkg_path)) {
+ log_error("Unable to find `pkg_path`");
+ return false;
+ }
+
+ ac_wo_data = std::filesystem::file_size(pkg_path) < MB(5);
+
+ std::string out_path = usb_device + "/" + title_id + "-DLC-" + std::get<0>(result);
+ log_info("output directory %s", out_path.c_str());
+ if (!std::filesystem::is_directory(out_path) && !std::filesystem::create_directories(out_path)) {
+ log_error("Unable to create `out_path` directory");
+ return false;
+ }
+
+ if(!ac_wo_data){
+ // Construct path to pfs_image.dat file
+ std::filesystem::path pfs_path("/mnt/sandbox/pfsmnt/");
+ std::filesystem::path sb_path("/mnt/sandbox/pfsmnt/");
+ full_content_id = std::get<2>(result);
+ pfs_path /= full_content_id + "-ac-nest"; // Use stem to get filename without extension
+ sb_path /= full_content_id + "-ac"; // Use stem to get filename without extension
+ pfs_path /= "pfs_image.dat";
+
+ if (std::filesystem::exists(pfs_path) && std::filesystem::is_regular_file(pfs_path)) {
+ log_info("Found pfs at %s", pfs_path.string().c_str());
+ if (!pfs::extract(pfs_path.string(), out_path, title_id, title)) {
+ log_error("Failed to extract pfs");
+ return false;
+ }
+ }
+ }
+
+ // Found pkg path
+ log_info("Found pkg at %s", pkg_path.string().c_str());
+ ProgUpdate(8, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(DLC_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + title_id + "\n\n" + getDumperLangSTR(DUMPER_SC0));
+ std::filesystem::path sce_sys_path(out_path);
+ sce_sys_path /= "sce_sys";
+ if (!std::filesystem::is_directory(sce_sys_path.string()) && !std::filesystem::create_directories(sce_sys_path.string())) {
+ log_error("Unable to create `sce_sys` directory");
+ return false;
+ }
+ if (!pkg::extract_sc0(pkg_path, sce_sys_path.string(), title_id, title)) {
+ log_error("pkg::extract_sc0(\"%s\",\"%s\") failed", pkg_path.c_str(), sce_sys_path.string().c_str());
+ return false;
+ }
+
+ std::filesystem::path gp4_path(out_path);
+ gp4_path /= full_content_id + "-DLC.gp4";
+ // Generate GP4
+ std::filesystem::path sfo_path(sce_sys_path);
+ sfo_path /= "param.sfo";
+
+
+ if(!DecryptAndMakeGP4(out_path, title_id, ADDITIONAL_CONTENT_DATA, title, full_content_id)){
+ log_error("Decrypting and making GP4 failed");
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool __dump(const std::string &usb_device, const std::string &title_id, Dump_Options opt, const std::string& title) {
-
- if (!LoadDumperLangs(DumperGetLang())) {
- if (!LoadDumperLangs(0x01)) {
- if(!load_dumper_embdded_eng())
- return false;
- }
- else
- log_debug("Loaded the backup, lang %i failed to load", DumperGetLang());
- }
if (opt > TOTAL_OF_OPTS)
{
log_error("OPT is out of range: %i", opt);
return false;
}
-
+
std::string output_directory = title_id;
+
+ // Check for .dumping semaphore
+ std::filesystem::path dumping_semaphore(usb_device);
+ std::filesystem::path complete_semaphore(usb_device);
+ std::ofstream dumping_sem_touch(dumping_semaphore);
+
if (opt == GAME_PATCH) {
output_directory += "-patch"; // Add "-patch" to "patch" types so it doesn't overlap with "base"
- } else if (opt == THEME_UNLOCK) {
+ }
+ else if (opt == THEME_UNLOCK) {
output_directory += "-unlock"; // Add "-unlock" to theme type so we can differentiate between "install" and "unlock" PKGs
}
+
+ dumping_semaphore /= output_directory + ".dumping";
+ if (std::filesystem::exists(dumping_semaphore)) {
+ log_error("This dump is currently dumping or closed unexpectedly! Please delete existing dump to enable dumping.");
+ }
+
+ // Check for .complete semaphore
+ complete_semaphore /= output_directory + ".complete";
+ if (std::filesystem::exists(complete_semaphore)) {
+ log_error("This dump has already been completed! Please delete existing dump to enable dumping.");
+ }
+
+
+ // Create .dumping semaphore
+ dumping_sem_touch.close();
+ if (std::filesystem::exists(dumping_semaphore)) {
+ log_error("Unable to create dumping semaphore!");
+ }
+
// Create base path
std::filesystem::path output_path(usb_device);
output_path /= output_directory;
@@ -58,29 +222,6 @@ bool __dump(const std::string &usb_device, const std::string &title_id, Dump_Opt
return false;
}
- // Check for .dumping semaphore
- std::filesystem::path dumping_semaphore(usb_device);
- dumping_semaphore /= output_directory + ".dumping";
- if (std::filesystem::exists(dumping_semaphore)) {
- log_error("This dump is currently dumping or closed unexpectedly! Please delete existing dump to enable dumping.");
- }
-
- // Check for .complete semaphore
- std::filesystem::path complete_semaphore(usb_device);
- complete_semaphore /= output_directory + ".complete";
- if (std::filesystem::exists(complete_semaphore)) {
- log_error("This dump has already been completed! Please delete existing dump to enable dumping.");
- }
-
-
- // Create .dumping semaphore
- std::ofstream dumping_sem_touch(dumping_semaphore);
- dumping_sem_touch.close();
- if (std::filesystem::exists(dumping_semaphore)) {
- log_error("Unable to create dumping semaphore!");
- }
-
-
// Create "sce_sys" directory in the output directory
std::filesystem::path sce_sys_path(output_path);
sce_sys_path /= "sce_sys";
@@ -142,10 +283,7 @@ bool __dump(const std::string &usb_device, const std::string &title_id, Dump_Opt
pkg_directory_path /= title_id;
pkg_directory_path /= "ac.pkg";
}
- else if (opt == ADDITIONAL_CONTENT_DATA) {
- pkg_directory_path /= "addcont";
- // This regex will match because of the checks at the beginning of the function
- }
+
// Detect if on extended storage and make pkg_path
std::filesystem::path ext_path("/mnt/ext0");
ext_path += pkg_directory_path;
@@ -163,9 +301,10 @@ bool __dump(const std::string &usb_device, const std::string &title_id, Dump_Opt
catch (std::filesystem::filesystem_error& e)
{
log_error("SFO Opts failed");
+ return false;
}
-ProgUpdate(8, "%s\n\n%s %s\n%s %s\n\n %s ...", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME),title.c_str(), getDumperLangSTR(TITLE_ID), title_id.c_str(), getDumperLangSTR(DUMPER_SC0));
+ProgUpdate(8, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + title_id + "\n\n" + getDumperLangSTR(DUMPER_SC0));
try {
if (std::filesystem::exists(pkg_path)) {
if (!pkg::extract_sc0(pkg_path, sce_sys_path, title_id, title))
@@ -185,7 +324,7 @@ catch (std::exception e)
return false;
}
-ProgUpdate(10, "%s\n\n%s %s\n%s %s\n\n %s ...", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME),title.c_str(), getDumperLangSTR(TITLE_ID), title_id.c_str(), getDumperLangSTR(DUMPING_TROPHIES));
+ProgUpdate(10, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + title_id + "\n\n" + getDumperLangSTR(DUMPING_TROPHIES));;
try {
// Trophy "decryption"
@@ -210,8 +349,8 @@ try {
}
dst /= "trophy" + std::string(zerofill, '0') + std::string(entry.trophy_number.data) + ".trp";
- ProgUpdate(11, "%s\n\n%s %s\n%s %s\n\n %s %s...", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME), title.c_str(), getDumperLangSTR(TITLE_ID), title_id.c_str(), getDumperLangSTR(DUMPING_TROPHIES), src.c_str());
- if (std::filesystem::exists(src)) {
+ ProgUpdate(11, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + title_id + "\n\n" + getDumperLangSTR(DUMPING_TROPHIES) + " " + src.string());
+ if (std::filesystem::exists(src)) {
log_debug("[%s] Dumping Trophy %s to %s ...", title_id.c_str(), src.string().c_str(), dst.string().c_str());
if (!std::filesystem::copy_file(src, dst, std::filesystem::copy_options::overwrite_existing)) {
log_error("Unable to copy %s", src.string().c_str());
@@ -248,7 +387,8 @@ catch (std::filesystem::filesystem_error& e)
pfs_path /= "pfs_image.dat";
try {
if (std::filesystem::exists(pfs_path)) {
- if (!pfs::extract(pfs_path.string(), output_path.string(), title_id.c_str(), (char*)title.c_str())) {
+ if (!pfs::extract(pfs_path.string(), output_path.string(), title_id, title)) {
+ log_error("Failed to extract pfs");
return false;
}
}
@@ -263,50 +403,10 @@ catch (std::filesystem::filesystem_error& e)
return false;
}
-
-
- // Vector of strings for locations of SELF files for decryption
- std::vector self_files;
-
- // Generate GP4
-
- std::filesystem::path sfo_path(sce_sys_path);
- sfo_path /= "param.sfo";
-
- std::filesystem::path gp4_path(output_path);
- gp4_path /= output_directory + ".gp4";
-
-
-ProgUpdate(98, "%s\n\n%s %s\n%s %s\n\n %s ...", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME), title.c_str(), getDumperLangSTR(TITLE_ID), title_id.c_str(), getDumperLangSTR(CREATING_GP4));
-try {
- if (!gp4::generate(sfo_path.string(), output_path.string(), gp4_path.string(), self_files, opt))
- return false;
+if(!DecryptAndMakeGP4(output_path.string(), title_id, opt, title)){
+ log_error("Decrypting and making GP4 failed");
+ return false;
}
-catch (const std::exception& e)
-{
- log_error("Gerneating GP4 failed");
-}
-
-
- // Decrypt ELF files and make into FSELFs
-std::string encrypted_path("/mnt/sandbox/pfsmnt/");
-if (opt == BASE_GAME) {
- encrypted_path += title_id + "-app0/";
-}
-else if (opt == GAME_PATCH) {
- encrypted_path += title_id + "-patch0/";
-}
-else if (opt == THEME_UNLOCK) {
- encrypted_path += title_id + "-ac/";
-}
-
-log_info("Step %i", __LINE__);
-std::string decrypted_path(output_path);
-
-ProgUpdate(99, "%s\n\n%s %s\n%s %s\n\n %s %s...", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME), title.c_str(), getDumperLangSTR(TITLE_ID), title_id.c_str(), getDumperLangSTR(DEC_BIN), encrypted_path.c_str());
-log_debug("Decrypting Bins in %s ...", encrypted_path.c_str());
-
-elf::decrypt_dir(encrypted_path, decrypted_path);
// Delete .dumping semaphore
if (!std::filesystem::remove(dumping_semaphore))
diff --git a/external/libdumper/source/elf.cpp b/external/libdumper/source/elf.cpp
index 28f0700..803bb6a 100644
--- a/external/libdumper/source/elf.cpp
+++ b/external/libdumper/source/elf.cpp
@@ -17,9 +17,13 @@
#include
#include
#include
+#include "fself.hpp"
#include
#include
-#include "libshahash.h"
+#include "sha256.h"
+#include
+#include
+#include
#include
#define KB(x) ((size_t) (x) << 10)
@@ -30,11 +34,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return 0;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return 0;
}
// Open path
@@ -42,12 +48,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: ");
+ return 0;
}
// Check to make sure file is a SELF
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
self_input.close();
log_error("Input path is not a SELF!");
+ return 0;
}
// Read SELF header
@@ -57,6 +65,7 @@ namespace elf {
// Should never reach here... will affect coverage %
self_input.close();
log_error("Error reading SELF header!");
+ return 0;
}
// Calculate ELF header offset from the number of SELF segments
@@ -69,6 +78,7 @@ namespace elf {
if (!self_input.good()) {
self_input.close();
log_error("Error reading ELF header!");
+ return 0;
}
self_input.close();
@@ -82,6 +92,7 @@ namespace elf {
if (std::memcmp(elf_header.e_ident, elf_magic, 4) != 0) {
log_error("Error reading ELF magic!");
+ return 0;
}
// Calculate SCE header offset from number of ELF entries
@@ -99,11 +110,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return SceHeader();
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path)) {
log_error("Input path does not exist or is not a file!");
+ return SceHeader();
}
// Open path
@@ -111,12 +124,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return SceHeader();
}
// Check to make sure file is a SELF
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
self_input.close();
log_error("Input path is not a SELF!");
+ return SceHeader();
}
// Calculate SCE header offset from number of ELF entries
@@ -128,6 +143,7 @@ namespace elf {
if (!self_input.good()) {
self_input.close();
log_error("Error reading SCE header!");
+ return SceHeader();
}
return sce_header;
@@ -137,11 +153,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return SceHeaderNpdrm();
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return SceHeaderNpdrm();
}
// Open path
@@ -149,12 +167,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return SceHeaderNpdrm();
}
// Check to make sure file is a SELF
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
self_input.close();
log_error("Input path is not a SELF!");
+ return SceHeaderNpdrm();
}
// Calculate SCE header offset from number of ELF entries
@@ -176,11 +196,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return false;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path)) {
log_error("Input path does not exist or is not a file!");
+ return false;
}
// Read SELF header
@@ -203,11 +225,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return false;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return false;
}
// Open path
@@ -215,12 +239,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return false;
}
// Check if the file is a SELF
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
self_input.close();
log_error("Input path is not a SELF!");
+ return false;
}
// Read SELF header
@@ -230,6 +256,7 @@ namespace elf {
// Should never reach here... will affect coverage %
self_input.close();
log_error("Error reading SELF header!");
+ return false;
}
uint64_t program_type = self_header.program_type;
@@ -249,11 +276,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return "";
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return "";
}
// Open path (To check permissions)
@@ -261,12 +290,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return "";
}
self_input.close();
// Check if the file is a SELF. If it's not it *should* not have a SCE header
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
log_error("Input path is not a SELF!");
+ return "";
}
uint64_t program_type;
@@ -316,11 +347,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return 0;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return 0;
}
// Open path (To check permissions)
@@ -328,12 +361,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return 0;
}
self_input.close();
// Check if the file is a SELF. If it's not it *should* not have a SCE header
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
log_error("Input path is not a SELF!");
+ return 0;
}
if (is_npdrm(path)) {
@@ -347,11 +382,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return 0;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return 0;
}
// Open path (To check permissions)
@@ -359,12 +396,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return 0;
}
self_input.close();
// Check if the file is a SELF. If it's not it *should* not have a SCE header
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
log_error("Input path is not a SELF!");
+ return 0;
}
if (is_npdrm(path)) {
@@ -378,11 +417,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return 0;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return 0;
}
// Open path (To check permissions)
@@ -390,12 +431,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return 0;
}
self_input.close();
// Check if the file is a SELF. If it's not it *should* not have a SCE header
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
log_error("Input path is not a SELF!");
+ return 0;
}
if (is_npdrm(path)) {
@@ -409,11 +452,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return std::vector();
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return std::vector();
}
// Open path (To check permissions)
@@ -421,12 +466,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return std::vector();
}
self_input.close();
// Check if the file is a SELF. If it's not it *should* not have a SCE header
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
log_error("Input path is not a SELF!");
+ return std::vector();
}
std::vector digest;
@@ -451,11 +498,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return std::vector();
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return std::vector();
}
// Open path (To check permissions)
@@ -463,12 +512,14 @@ namespace elf {
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return std::vector();
}
self_input.close();
// Check if the file is a SELF
if (!Check_ELF_Magic(path, SELF_MAGIC)) {
log_error("Input path is not a SELF!");
+ return std::vector();
}
#if defined(__ORBIS__)
@@ -503,11 +554,13 @@ namespace elf {
// Check for empty or pure whitespace path
if (original_path.empty() || std::all_of(original_path.begin(), original_path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty original path argument!");
+ return false;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(original_path.c_str())) {
log_error("Input original path does not exist or is not a file!");
+ return false;
}
// Open path
@@ -515,22 +568,26 @@ namespace elf {
if (!original_self || !original_self.good()) {
original_self.close();
log_error("Cannot open file: %s", original_path.c_str());
+ return false;
}
original_self.close();
// Check if file is a SELF
if (!Check_ELF_Magic(original_path, SELF_MAGIC)) {
log_error("Input original path is not a SELF!");
+ return false;
}
// Check for empty or pure whitespace path
if (decrypted_path.empty() || std::all_of(decrypted_path.begin(), decrypted_path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty decrypted path argument!");
+ return false;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(decrypted_path.c_str())) {
log_error("Input decrypted path does not exist or is not a file!");
+ return false;
}
// Open path
@@ -538,72 +595,47 @@ namespace elf {
if (!decrypted_elf || !decrypted_elf.good()) {
decrypted_elf.close();
log_error("Cannot open file: %s", decrypted_path.c_str());
+ return false;
}
// Check if file is an ELF
if (!Check_ELF_Magic(decrypted_path)) {
decrypted_elf.close();
log_error("Input decrypted path is not an ELF!");
- }
-
- // Read digest
- unsigned char digest[32];
- std::vector original_digest_from_self = get_digest(original_path);
- for (size_t i = 0; i < sizeof(digest); i++) {
- digest[i] = original_digest_from_self[i];
- }
-
- unsigned char calculated_digest[sizeof(digest)];
-
- SHA256_CTX context;
- SHA256_Init(&context);
-
- while (decrypted_elf.good()) {
- unsigned char buffer[PAGE_SIZE];
- decrypted_elf.read((char*)buffer, sizeof(buffer)); // Flawfinder: ignore
-
- SHA256_Update(&context, buffer, decrypted_elf.gcount());
- }
- decrypted_elf.close();
-
- SHA256_Final(&context, calculated_digest);
-#if 1
-
- char sha_digest[33] = { 0 };
- char sha_calcdigest[33] = { 0 };
-
- for (int i = 0; i < 32; ++i) {
- snprintf(&sha_digest[i], 32, "%0x", (unsigned int)digest[i]);
- }
-
-
- for (int i = 0; i < 32; ++i) {
- snprintf(&sha_calcdigest[i], 32, "%0x", (unsigned int)calculated_digest[i]);
- }
-
- log_info("[libshahash][SHA256] ELF Digest Hash: %s Calc'd Digest Hash: %s", sha_digest, sha_calcdigest);
-#endif
-
- if (std::memcmp(calculated_digest, digest, sizeof(digest)) != 0) {
- log_info("File %s has failed validation", decrypted_path.c_str());
return false;
}
- else
- log_info("File %s Successfully validated", decrypted_path.c_str());
- return true;
- }
+ // Read digest
+ std::vector digest = get_digest(original_path);
+ std::vector calculated_digest(digest.size());
+ SHA256 sha256;
+ while (decrypted_elf.good()) {
+ std::vector buffer(PAGE_SIZE);
+ decrypted_elf.read(reinterpret_cast(&buffer[0]), buffer.size()); // Flawfinder: ignore
+ sha256.add(&buffer[0], decrypted_elf.gcount());
+ }
+ decrypted_elf.close();
+ sha256.getHash(&calculated_digest[0]);
+
+ if (std::memcmp(&calculated_digest[0], &digest[0], digest.size()) != 0) {
+ return false;
+ }
+
+ return true;
+}
// This is done in other dumpers but is it necessary?
void zero_section_header(const std::string& path) {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path.c_str())) {
log_error("Input path does not exist or is not a file!");
+ return;
}
// Open path
@@ -611,12 +643,14 @@ namespace elf {
if (!elf_path || !elf_path.good()) {
elf_path.close();
log_error("Cannot open file: %s", path.c_str());
+ return;
}
// Check if file is an ELF
if (!Check_ELF_Magic(path, ELF_MAGIC)) {
elf_path.close();
log_error("Input path is not an ELF!");
+ return;
}
elf_path.seekg(0, elf_path.beg);
@@ -627,6 +661,7 @@ namespace elf {
// Should never reach here... will affect coverage %
elf_path.close();
log_error("Error reading ELF header!");
+ return;
}
elf_path.close();
@@ -641,6 +676,7 @@ namespace elf {
// Should never reach here... will affect coverage %
output_file.close();
log_error("Cannot open file: %s", path.c_str());
+ return;
}
output_file.seekp(0, output_file.beg);
@@ -763,7 +799,7 @@ namespace elf {
return false;
}
- int decrypt_and_dump_self(const char* selfFile, const char* saveFile) {
+ bool decrypt_and_dump_self(const char* selfFile, const char* saveFile) {
int fd = sceKernelOpen(selfFile, O_RDONLY, 0);
if (fd > 0) {
@@ -807,59 +843,61 @@ namespace elf {
// The following code inspired from:
// - https://github.com/AlexAltea/orbital
// - https://github.com/xvortex/ps4-dumper-vtx
- bool decrypt_dir(const std::string& input, const std::string& output) {
+ bool decrypt_dir(const std::string& inputPath, const std::string& outputPath, bool fself = false) {
- DIR* dir;
- struct dirent* dp;
- struct stat info;
- char src_path[1024], dst_path[1024];
+ DIR* dir;
+ struct dirent* dp;
+ struct stat fileInfo;
- dir = opendir(input.c_str());
- if (!dir)
- return true;
+ std::filesystem::path sourcePath;
+ std::filesystem::path destinationPath;
- while ((dp = readdir(dir)) != NULL)
+ dir = opendir(inputPath.c_str());
+ if (!dir) return true;
+
+ while ((dp = readdir(dir)) != NULL)
+ {
+ if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
- if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ sourcePath = std::filesystem::path(inputPath) / dp->d_name;
+ destinationPath = std::filesystem::path(outputPath) / dp->d_name;
+
+ if (!stat(sourcePath.string().c_str(), &fileInfo))
{
- // do nothing (straight logic)
- }
- else
- {
- snprintf(src_path, 1023, "%s/%s", input.c_str(), dp->d_name);
- snprintf(dst_path, 1023, "%s/%s", output.c_str(), dp->d_name);
- if (!stat(src_path, &info))
+ if (S_ISDIR(fileInfo.st_mode))
{
- if (S_ISDIR(info.st_mode))
+ std::filesystem::create_directory(destinationPath);
+ decrypt_dir(sourcePath, destinationPath, fself);
+ }
+ else if (S_ISREG(fileInfo.st_mode))
+ {
+ if (elf::Check_ELF_Magic(sourcePath.string().c_str(), SELF_MAGIC))
{
- mkdir(src_path, 0777);
- decrypt_dir(src_path, dst_path);
- }
- else
- if (S_ISREG(info.st_mode))
- {
-
- if (Check_ELF_Magic(src_path, SELF_MAGIC))
- {
-
- if (decrypt_and_dump_self(src_path, dst_path))
- {
- //if (!is_valid_decrypt(src_path, dst_path))
- log_error("failed to decrypt %s -> %s", src_path, dst_path);
- }
-
+ if (!elf::decrypt_and_dump_self(sourcePath.string().c_str(), destinationPath.string().c_str()))
+ {
+ if (!elf::is_valid_decrypt(sourcePath.string().c_str(), destinationPath.string().c_str())) {
+ log_warn("%s has Invalid ELF decryption!", destinationPath.string().c_str());
}
- else
- log_info("file: %s is NOT a self", src_path);
-
}
+
+ if(fself) {
+ log_info("file: %s is an fself", sourcePath.string().c_str());
+ destinationPath = outputPath / std::filesystem::path(dp->d_name);
+ destinationPath += ".fself";
+ std::filesystem::copy(sourcePath, destinationPath, std::filesystem::copy_options::overwrite_existing);
+ }
+ else {
+ log_info("file: %s is NOT an fself", sourcePath.string().c_str());
+ }
+ }
+ else {
+ log_info("file: %s is NOT a self", sourcePath.string().c_str());
+ }
}
}
}
- closedir(dir);
-
- return true;
-
-
}
+ closedir(dir);
+ return true;
+}
} // namespace elf
diff --git a/external/libdumper/source/fself.cpp b/external/libdumper/source/fself.cpp
index 6a3dd13..12134b9 100644
--- a/external/libdumper/source/fself.cpp
+++ b/external/libdumper/source/fself.cpp
@@ -11,25 +11,29 @@
#include
#include "log.h"
#include
+#include
+#include
namespace fself {
-bool is_fself(const std::string &path) {
- // TODO
- UNUSED(path);
-
- return false;
+bool is_fself(const std::string& self_path){
+ char buf[200];
+ int ret = syscall(607, self_path.c_str(), buf);
+ log_info("res: %i (%s)", ret, (ret == -1) ? strerror(errno) : "No Error");
+ return (ret == 0);
}
void make_fself(const std::string &input, const std::string &output, uint64_t paid, const std::string &ptype, uint64_t app_version, uint64_t fw_version, std::vector auth_info) {
// Check for empty or pure whitespace path
if (input.empty() || std::all_of(input.begin(), input.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty input path argument!");
+ return;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(input)) {
log_error("Input path does not exist or is not a file!");
+ return;
}
// Open path
@@ -37,6 +41,7 @@ void make_fself(const std::string &input, const std::string &output, uint64_t pa
if (!self_input || !self_input.good()) {
self_input.close();
log_error("Cannot open file: %s",input.c_str());
+ return;
}
@@ -44,6 +49,7 @@ void make_fself(const std::string &input, const std::string &output, uint64_t pa
if (output.empty() || std::all_of(output.begin(), output.end(), [](char c) { return std::isspace(c); })) {
self_input.close();
log_error("Empty output path argument!");
+ return;
}
std::filesystem::path output_path(output);
@@ -52,6 +58,7 @@ void make_fself(const std::string &input, const std::string &output, uint64_t pa
if (std::filesystem::exists(output_path) && !std::filesystem::is_regular_file(output_path)) {
self_input.close();
log_error("Oputput object exists but is not a file!");
+ return;
}
// paid, app_version and fw_version are unsigned and any value between 0x0 and 0xFFFFFFFFFFFFFFFF is valid so we do not have to check range
@@ -59,16 +66,19 @@ void make_fself(const std::string &input, const std::string &output, uint64_t pa
if (ptype != "fake" && ptype != "npdrm_exec" && ptype != "npdrm_dynlib" && ptype != "system_exec" && ptype != "system_dynlib" && ptype != "host_kernel" && ptype != "secure_module" && ptype != "secure_kernel") {
self_input.close();
log_error("Invalid ptype!");
+ return;
}
// Should be 0x110 in size and 0-9a-fA-F
if (auth_info.size() != 0x110) {
self_input.close();
log_error("Auth info is invalid length!");
+ return;
}
if (std::all_of(output.begin(), output.end(), [](char c) { return std::isxdigit(c); })) {
self_input.close();
log_error("Auth info is not hex!");
+ return;
}
// Input may not be "correct" but it's valid at this point
diff --git a/external/libdumper/source/gp4.cpp b/external/libdumper/source/gp4.cpp
index 0209e58..0abcaee 100644
--- a/external/libdumper/source/gp4.cpp
+++ b/external/libdumper/source/gp4.cpp
@@ -18,6 +18,13 @@
#include
#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
namespace gp4 {
@@ -25,6 +32,7 @@ void recursive_directory(const std::string &path, pugi::xml_node &node) {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return;
}
for (auto &&p : std::filesystem::directory_iterator(path)) {
@@ -100,17 +108,18 @@ pugi::xml_document make_volume(const std::string& content_id, const std::string&
package_node.append_attribute("storage_type") = "digital50";
package_node.append_attribute("app_type") = "full";
-
+log_info("---------");
// Set c_date
std::string new_time = std::string("actual_datetime");
if (!c_date.empty()) {
new_time = c_date;
if (!c_time.empty()) {
// TODO: Append time to new_time
+ log_info("---------");
}
}
package_node.append_attribute("c_date") = new_time.c_str();
-
+log_info("---------");
return doc;
}
@@ -153,94 +162,116 @@ pugi::xml_document make_playgo(const std::string& playgo_xml) {
return doc;
}
-pugi::xml_document make_files(const std::string& path, std::vector& elf_files, Dump_Options opt) {
- // Check for empty or pure whitespace path
- bool is_illegal = false;
- if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
- log_fatal("Empty path argument!");
- }
- // Generate XML
- pugi::xml_document doc;
- const char* gp4_path;
- pugi::xml_node files_node = doc.append_child("files");
- files_node.append_attribute("img_no") = "0"; // TODO: PlayGo
+pugi::xml_document make_files(const std::string &path, bool validation) {
+ // Check for empty or pure whitespace path
+ if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
+ log_error("Empty path argument!");
+ return pugi::xml_document();
+ }
- // Files to skip when making GP4
- std::vector skip_files = {
- "sce_discmap.plt",
- "sce_discmap_patch.plt",
- "sce_sys/about/right.sprx", // Cannot be included for official PKG tools
- "sce_sys/icon0.dds",
- "sce_sys/license.dat",
- "sce_sys/license.info",
- "sce_sys/pic0.dds",
- "sce_sys/pic1.dds",
- "sce_sys/playgo-chunk.dat",
- "sce_sys/playgo-chunk.sha",
- "sce_sys/playgo-manifest.xml",
- "sce_sys/psreserved.dat",
- "sce_sys/origin-deltainfo.dat"
+ // Generate XML
+ pugi::xml_document doc;
+ pugi::xml_node files_node = doc.append_child("files");
+ files_node.append_attribute("img_no") = "0"; // TODO: PlayGo
+ // Files to skip when making GP4, but not the GP4FV
+ std::vector skip_files = {};
+ if (!validation) {
+ skip_files = {
+ "sce_sys/about/right.sprx", // Cannot be included for official PKG tools
+ "sce_sys/about/",
+ "sce_sys/.digests",
+ "sce_sys/.entry_keys",
+ "sce_sys/.image_key",
+ "sce_sys/.unknown_0x21",
+ "sce_sys/.general_digests",
+ "sce_sys/.unknown_0xC0",
+ "sce_sys/.metas",
+ "sce_sys/.entry_names",
+ "sce_sys/license.dat",
+ "sce_sys/license.info",
+ "sce_sys/selfinfo.dat", // ?
+ "sce_sys/imageinfo.dat", // ?
+ "sce_sys/target-deltainfo.dat", // ?
+ "sce_sys/origin-deltainfo.dat",
+ "sce_sys/psreserved.dat",
+ "sce_sys/playgo-chunk.dat",
+ "sce_sys/playgo-chunk.sha",
+ "sce_sys/playgo-manifest.xml",
+ "sce_sys/pubtoolinfo.dat", // ?
+ "sce_sys/app/playgo-chunk.dat",
+ "sce_sys/app/playgo-chunk.sha",
+ "sce_sys/app/playgo-manifest.xml",
+ "sce_sys/icon0.dds",
+ "sce_sys/pic0.dds",
+ "sce_sys/pic1.dds"
};
-
- for (uint64_t i = 0; i < 100; i++) {
- std::stringstream ss_image;
- ss_image << "sce_sys/icon0_" << std::dec << std::setfill('0') << std::setw(2) << i << ".dds";
- skip_files.push_back(ss_image.str());
- ss_image.str(std::string());
- ss_image << "sce_sys/pic0_" << std::dec << std::setfill('0') << std::setw(2) << i << ".dds";
- skip_files.push_back(ss_image.str());
- ss_image.str(std::string());
- ss_image << "sce_sys/pic1_" << std::dec << std::setfill('0') << std::setw(2) << i << ".dds";
- skip_files.push_back(ss_image.str());
+ for (auto &&f : skip_files) {
+ std::stringstream ss_encrypted;
+ ss_encrypted << f << ".encrypted";
+ skip_files.push_back(ss_encrypted.str());
}
+ for (uint8_t i = 0; i < 31; i++) {
+ std::stringstream ss_image;
+ ss_image << "sce_sys/icon0_" << std::dec << std::setfill('0') << std::setw(2) << i << ".dds";
+ skip_files.push_back(ss_image.str());
+ ss_image.str(std::string());
+ ss_image << "sce_sys/pic1_" << std::dec << std::setfill('0') << std::setw(2) << i << ".dds";
+ skip_files.push_back(ss_image.str());
+ }
+ }
+
+ for (auto &&p : std::filesystem::recursive_directory_iterator(path)) {
+ // Skip files/directories contained in the `skip_files` vector
+ if (std::count(skip_files.begin(), skip_files.end(), customRelative(p.path(), path))) {
+ continue;
+ }
+ else if(customRelative(p.path(), path).string().rfind(".fself") != std::string::npos){
+ continue;
+ }
+ else if (std::filesystem::is_regular_file(p.path())) {
+ std::filesystem::path targ_path = customRelative(p.path(), path);
+ std::filesystem::path orig_path = customRelative(p.path(), path);
+ // TODO:
+ // - Add proper PlayGo options
+ // - Add PFS Compression option
+ pugi::xml_node file_node = files_node.append_child("file");
+ file_node.append_attribute("targ_path") = targ_path.c_str();
- for (auto&& p : std::filesystem::recursive_directory_iterator(path)) {
- if (std::filesystem::is_regular_file(p.path())) {
- bool self;
- if ((self = elf::Check_ELF_Magic(p.path(), SELF_MAGIC))) {
- elf_files.push_back(p.path());
- }
- // 20 is sizeof("/mnt/usb0/CUSA00000")
- // 26 is sizeof("/mnt/usb0/CUSA00000-patch")
- // 27 is sizeof("/mnt/usb0/CUSA00000-unlock")
- // TODO:
- // - Add proper PlayGo options
- // - Add PFS Compression option
- if(opt == GAME_PATCH)
- gp4_path = std::string(p.path()).c_str() + 26;
- else if (opt == THEME_UNLOCK)
- gp4_path = std::string(p.path()).c_str() + 27;
- else
- gp4_path = std::string(p.path()).c_str() + 20;
+ // std::string orig_path_mod = orig_path.c_str();
+ //std::replace(orig_path_mod.begin(), orig_path_mod.end(), '/', '\\');
+ file_node.append_attribute("orig_path") = orig_path.c_str();
+ if (validation) {
+ // Get filesize, crc32, md5, and sha1
+ file_node.append_attribute("size") = customFileSize(p.path()).c_str();
+ CRC32 crc32;
+ MD5 md5;
+ SHA1 sha1;
- if (strstr(gp4_path, "sce_sys") != NULL) {
- for (int i = 0; i < skip_files.size(); i++) {
- if (strcmp(gp4_path, skip_files[i].c_str()) == 0)
- {
- log_info("Found Illegal file %s skipping...", skip_files[i].c_str());
- is_illegal = true;
- break;
- }
- }
- }
- else if (strstr(gp4_path, skip_files[0].c_str()) != NULL || strstr(gp4_path, skip_files[1].c_str()) != NULL)
- { // sce_discmap.plt sce_discmap_path.plt
- // file skip
- log_info("Found Illegal file %s skipping...", gp4_path);
- is_illegal = true;
- }
- if (!is_illegal) {
- log_info("Adding %s to GP4", gp4_path);
- pugi::xml_node file_node = files_node.append_child("file");
- file_node.append_attribute("targ_path") = gp4_path;
- file_node.append_attribute("orig_path") = gp4_path;
- }
- is_illegal = false;
+ std::ifstream hash_data(p.path(), std::ios::in | std::ios::binary);
+ if (!hash_data || !hash_data.good()) {
+ hash_data.close();
+ log_error("Cannot open file: %s", std::string(p.path()).c_str());
+ return doc;
}
- }
- return doc;
+ while (hash_data.good()) {
+ std::vector buffer(PAGE_SIZE);
+ hash_data.read(reinterpret_cast(&buffer[0]), buffer.size()); // Flawfinder: ignore
+ crc32.add(&buffer[0], hash_data.gcount());
+ md5.add(&buffer[0], hash_data.gcount());
+ sha1.add(&buffer[0], hash_data.gcount());
+ }
+ hash_data.close();
+ file_node.append_attribute("crc") = &crc32.getHash()[0];
+ file_node.append_attribute("md5") = &md5.getHash()[0];
+ file_node.append_attribute("sha1") = &sha1.getHash()[0];
+
+ }
+ }
+ }
+
+ return doc;
}
pugi::xml_document make_directories(const std::string& path) {
@@ -396,7 +427,7 @@ void generate(const std::string& sfo_path, const std::string& output_path, const
}
// Generate actual GP4 file
pugi::xml_document volume_xml = make_volume(content_id, content_type, c_date, c_time);
- pugi::xml_document files_xml = make_files(output_path, self_files, opt);
+ pugi::xml_document files_xml = make_files(output_path, false);
pugi::xml_document directories_xml = make_directories(output_path);
pugi::xml_document playgo_xml;
pugi::xml_document assembled_xml;
diff --git a/external/libdumper/source/lang.cpp b/external/libdumper/source/lang.cpp
new file mode 100644
index 0000000..fdbf9a9
--- /dev/null
+++ b/external/libdumper/source/lang.cpp
@@ -0,0 +1,113 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "lang.h"
+#include "log.h"
+#include
+#include
+#include
+#include
+#include
+#include "ini.h"
+
+std::unordered_map dmpstropts;
+static bool lang_is_dumper_initialized = false;
+extern "C" int sceSystemServiceParamGetInt(int, int32_t *);
+//
+// https://github.com/Al-Azif/ps4-payload-guest/blob/main/src/Language.cpp
+//
+int sceSystemServiceParamGetInt(int i, int32_t *lang);
+int32_t DumperGetLang() {
+ int32_t lang;
+
+ if (sceSystemServiceParamGetInt(1, &lang) != 0) {
+ log_error("Unable to qet language code");
+ return 0x1;
+ }
+
+ return lang;
+}
+
+std::string getDumperLangSTR(Lang_ST str)
+{
+ if (lang_is_dumper_initialized)
+ {
+ if (dmpstropts[lang_key[str]].empty() || str > LANG_NUM_OF_STRINGS)
+ return "String NOT found";
+ else
+ return dmpstropts[lang_key[str]];
+ }
+ else
+ return "ERROR: Lang has not loaded";
+}
+
+static int load_lang_dumper_ini(void* user, const char* section, const char* name,
+ const char* value)
+{
+ dmpstropts.insert(std::pair(name, value));
+ //log_info("Loaded %s: %s", name, value);
+
+ return 1;
+
+}
+
+bool exists(const char* path)
+{
+ int dfd = open(path, 0, 0); // try to open dir
+ if (dfd < 0) {
+ log_info("path %s, errno %s", path, strerror(errno));
+ return false;
+ }
+ else
+ close(dfd);
+
+
+ return true;
+}
+
+bool load_dumper_embdded_eng()
+{
+ //mem should already be allocated as this is the last opt
+ if (dmpstropts.empty()) {
+ int error = ini_parse("/user/app/ITEM00001/lang.ini", load_lang_dumper_ini, NULL);
+ if (error) {
+ log_error("Bad config file (first error on line %d)!", error);
+ return false;
+ }
+ else
+ lang_is_dumper_initialized = true;
+ }
+ else
+ return false;
+
+ return true;
+}
+
+bool LoadDumperLangs(int LangCode)
+{
+ std::string dst = "/mnt/sandbox/pfsmnt/ITEM00001-app0/langs/" + std::to_string(LangCode) + "/lang.ini";
+ if (!lang_is_dumper_initialized) {
+ //dont leak or realloc on fail
+ if(!std::filesystem::exists((dst.c_str()))){
+ log_error("Lang file not found! %s", dst.c_str());
+ return false;
+ }
+
+ int error = ini_parse(dst.c_str(), load_lang_dumper_ini, nullptr);
+ if (error < 0)
+ {
+ log_error("Bad config file (first error on line %d)!", error);
+ return false;
+ }
+ else
+ lang_is_dumper_initialized = true;
+ }
+
+ return true;
+}
\ No newline at end of file
diff --git a/external/libdumper/source/main.cpp b/external/libdumper/source/main.cpp
index 5f28cd2..eba6b87 100644
--- a/external/libdumper/source/main.cpp
+++ b/external/libdumper/source/main.cpp
@@ -6,11 +6,13 @@
#include "dumper.h"
#include
#include
+#include "lang.h"
+#include "pfs.hpp"
#define DUMPER_LOG "/user/app/ITEM00001/logs/if_dumper.log"
-bool Dumper(std::string &dump_path, std::string &title_id, Dump_Options opt, std::string &title)
+bool Dumper(const std::string &dump_path, const std::string &title_id, Dump_Options opt, const std::string &title)
{
/*-- INIT LOGGING FUNCS --*/
unlink(DUMPER_LOG);
@@ -21,6 +23,20 @@ bool Dumper(std::string &dump_path, std::string &title_id, Dump_Options opt, std
log_add_fp(fp, LOG_DEBUG);
log_info("LibDumper Started with path: %s tid: %s title: %s opt: %i", dump_path.c_str(), title_id.c_str(), title.c_str(), opt);
- return dump::__dump(dump_path, title_id, opt, title);
-}
+ if (!LoadDumperLangs(DumperGetLang())) {
+ log_debug("Failed to load the language, loading the backup");
+ if (!LoadDumperLangs(0x01)) {
+ log_debug("Failed to load the backup, loading the embedded");
+ if(!load_dumper_embdded_eng()){
+ log_debug("Failed to load the embedded, exiting");
+ return false;
+ }
+ }
+ else
+ log_debug("Loaded the backup, lang %i failed to load", DumperGetLang());
+ }
+
+ pfs::dump_op = opt;
+ return (opt == ADDITIONAL_CONTENT_DATA) ? dump::dump_dlc(dump_path, title_id) : dump::__dump(dump_path, title_id, opt, title);
+}
diff --git a/external/libdumper/source/md5.cpp b/external/libdumper/source/md5.cpp
new file mode 100644
index 0000000..84dc513
--- /dev/null
+++ b/external/libdumper/source/md5.cpp
@@ -0,0 +1,351 @@
+// //////////////////////////////////////////////////////////
+// md5.cpp
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#include "md5.h"
+
+
+/// same as reset()
+MD5::MD5() {
+ reset();
+}
+
+/// restart
+void MD5::reset() {
+ m_numBytes = 0;
+ m_bufferSize = 0;
+
+ // according to RFC 1321
+ m_hash[0] = 0x67452301;
+ m_hash[1] = 0xefcdab89;
+ m_hash[2] = 0x98badcfe;
+ m_hash[3] = 0x10325476;
+}
+
+namespace {
+// mix functions for processBlock()
+inline uint32_t f1(uint32_t b, uint32_t c, uint32_t d) {
+ return d ^ (b & (c ^ d)); // original: f = (b & c) | ((~b) & d);
+}
+
+inline uint32_t f2(uint32_t b, uint32_t c, uint32_t d) {
+ return c ^ (d & (b ^ c)); // original: f = (b & d) | (c & (~d));
+}
+
+inline uint32_t f3(uint32_t b, uint32_t c, uint32_t d) {
+ return b ^ c ^ d;
+}
+
+inline uint32_t f4(uint32_t b, uint32_t c, uint32_t d) {
+ return c ^ (b | ~d);
+}
+
+inline uint32_t rotate(uint32_t a, uint32_t c) {
+ return (a << c) | (a >> (32 - c));
+}
+
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
+inline uint32_t swap(uint32_t x) {
+#if defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap32(x);
+#endif
+#ifdef MSC_VER
+ return _byteswap_ulong(x);
+#endif
+
+ return (x >> 24) |
+ ((x >> 8) & 0x0000FF00) |
+ ((x << 8) & 0x00FF0000) |
+ (x << 24);
+}
+#endif
+} // namespace
+
+/// process 64 bytes
+void MD5::processBlock(const void *data) {
+ // get last hash
+ uint32_t a = m_hash[0];
+ uint32_t b = m_hash[1];
+ uint32_t c = m_hash[2];
+ uint32_t d = m_hash[3];
+
+ // data represented as 16x 32-bit words
+ const uint32_t *words = (uint32_t *)data;
+
+ // computations are little endian, swap data if necessary
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
+#define LITTLEENDIAN(x) swap(x)
+#else
+#define LITTLEENDIAN(x) (x)
+#endif
+
+ // first round
+ uint32_t word0 = LITTLEENDIAN(words[0]);
+ a = rotate(a + f1(b, c, d) + word0 + 0xd76aa478, 7) + b;
+ uint32_t word1 = LITTLEENDIAN(words[1]);
+ d = rotate(d + f1(a, b, c) + word1 + 0xe8c7b756, 12) + a;
+ uint32_t word2 = LITTLEENDIAN(words[2]);
+ c = rotate(c + f1(d, a, b) + word2 + 0x242070db, 17) + d;
+ uint32_t word3 = LITTLEENDIAN(words[3]);
+ b = rotate(b + f1(c, d, a) + word3 + 0xc1bdceee, 22) + c;
+
+ uint32_t word4 = LITTLEENDIAN(words[4]);
+ a = rotate(a + f1(b, c, d) + word4 + 0xf57c0faf, 7) + b;
+ uint32_t word5 = LITTLEENDIAN(words[5]);
+ d = rotate(d + f1(a, b, c) + word5 + 0x4787c62a, 12) + a;
+ uint32_t word6 = LITTLEENDIAN(words[6]);
+ c = rotate(c + f1(d, a, b) + word6 + 0xa8304613, 17) + d;
+ uint32_t word7 = LITTLEENDIAN(words[7]);
+ b = rotate(b + f1(c, d, a) + word7 + 0xfd469501, 22) + c;
+
+ uint32_t word8 = LITTLEENDIAN(words[8]);
+ a = rotate(a + f1(b, c, d) + word8 + 0x698098d8, 7) + b;
+ uint32_t word9 = LITTLEENDIAN(words[9]);
+ d = rotate(d + f1(a, b, c) + word9 + 0x8b44f7af, 12) + a;
+ uint32_t word10 = LITTLEENDIAN(words[10]);
+ c = rotate(c + f1(d, a, b) + word10 + 0xffff5bb1, 17) + d;
+ uint32_t word11 = LITTLEENDIAN(words[11]);
+ b = rotate(b + f1(c, d, a) + word11 + 0x895cd7be, 22) + c;
+
+ uint32_t word12 = LITTLEENDIAN(words[12]);
+ a = rotate(a + f1(b, c, d) + word12 + 0x6b901122, 7) + b;
+ uint32_t word13 = LITTLEENDIAN(words[13]);
+ d = rotate(d + f1(a, b, c) + word13 + 0xfd987193, 12) + a;
+ uint32_t word14 = LITTLEENDIAN(words[14]);
+ c = rotate(c + f1(d, a, b) + word14 + 0xa679438e, 17) + d;
+ uint32_t word15 = LITTLEENDIAN(words[15]);
+ b = rotate(b + f1(c, d, a) + word15 + 0x49b40821, 22) + c;
+
+ // second round
+ a = rotate(a + f2(b, c, d) + word1 + 0xf61e2562, 5) + b;
+ d = rotate(d + f2(a, b, c) + word6 + 0xc040b340, 9) + a;
+ c = rotate(c + f2(d, a, b) + word11 + 0x265e5a51, 14) + d;
+ b = rotate(b + f2(c, d, a) + word0 + 0xe9b6c7aa, 20) + c;
+
+ a = rotate(a + f2(b, c, d) + word5 + 0xd62f105d, 5) + b;
+ d = rotate(d + f2(a, b, c) + word10 + 0x02441453, 9) + a;
+ c = rotate(c + f2(d, a, b) + word15 + 0xd8a1e681, 14) + d;
+ b = rotate(b + f2(c, d, a) + word4 + 0xe7d3fbc8, 20) + c;
+
+ a = rotate(a + f2(b, c, d) + word9 + 0x21e1cde6, 5) + b;
+ d = rotate(d + f2(a, b, c) + word14 + 0xc33707d6, 9) + a;
+ c = rotate(c + f2(d, a, b) + word3 + 0xf4d50d87, 14) + d;
+ b = rotate(b + f2(c, d, a) + word8 + 0x455a14ed, 20) + c;
+
+ a = rotate(a + f2(b, c, d) + word13 + 0xa9e3e905, 5) + b;
+ d = rotate(d + f2(a, b, c) + word2 + 0xfcefa3f8, 9) + a;
+ c = rotate(c + f2(d, a, b) + word7 + 0x676f02d9, 14) + d;
+ b = rotate(b + f2(c, d, a) + word12 + 0x8d2a4c8a, 20) + c;
+
+ // third round
+ a = rotate(a + f3(b, c, d) + word5 + 0xfffa3942, 4) + b;
+ d = rotate(d + f3(a, b, c) + word8 + 0x8771f681, 11) + a;
+ c = rotate(c + f3(d, a, b) + word11 + 0x6d9d6122, 16) + d;
+ b = rotate(b + f3(c, d, a) + word14 + 0xfde5380c, 23) + c;
+
+ a = rotate(a + f3(b, c, d) + word1 + 0xa4beea44, 4) + b;
+ d = rotate(d + f3(a, b, c) + word4 + 0x4bdecfa9, 11) + a;
+ c = rotate(c + f3(d, a, b) + word7 + 0xf6bb4b60, 16) + d;
+ b = rotate(b + f3(c, d, a) + word10 + 0xbebfbc70, 23) + c;
+
+ a = rotate(a + f3(b, c, d) + word13 + 0x289b7ec6, 4) + b;
+ d = rotate(d + f3(a, b, c) + word0 + 0xeaa127fa, 11) + a;
+ c = rotate(c + f3(d, a, b) + word3 + 0xd4ef3085, 16) + d;
+ b = rotate(b + f3(c, d, a) + word6 + 0x04881d05, 23) + c;
+
+ a = rotate(a + f3(b, c, d) + word9 + 0xd9d4d039, 4) + b;
+ d = rotate(d + f3(a, b, c) + word12 + 0xe6db99e5, 11) + a;
+ c = rotate(c + f3(d, a, b) + word15 + 0x1fa27cf8, 16) + d;
+ b = rotate(b + f3(c, d, a) + word2 + 0xc4ac5665, 23) + c;
+
+ // fourth round
+ a = rotate(a + f4(b, c, d) + word0 + 0xf4292244, 6) + b;
+ d = rotate(d + f4(a, b, c) + word7 + 0x432aff97, 10) + a;
+ c = rotate(c + f4(d, a, b) + word14 + 0xab9423a7, 15) + d;
+ b = rotate(b + f4(c, d, a) + word5 + 0xfc93a039, 21) + c;
+
+ a = rotate(a + f4(b, c, d) + word12 + 0x655b59c3, 6) + b;
+ d = rotate(d + f4(a, b, c) + word3 + 0x8f0ccc92, 10) + a;
+ c = rotate(c + f4(d, a, b) + word10 + 0xffeff47d, 15) + d;
+ b = rotate(b + f4(c, d, a) + word1 + 0x85845dd1, 21) + c;
+
+ a = rotate(a + f4(b, c, d) + word8 + 0x6fa87e4f, 6) + b;
+ d = rotate(d + f4(a, b, c) + word15 + 0xfe2ce6e0, 10) + a;
+ c = rotate(c + f4(d, a, b) + word6 + 0xa3014314, 15) + d;
+ b = rotate(b + f4(c, d, a) + word13 + 0x4e0811a1, 21) + c;
+
+ a = rotate(a + f4(b, c, d) + word4 + 0xf7537e82, 6) + b;
+ d = rotate(d + f4(a, b, c) + word11 + 0xbd3af235, 10) + a;
+ c = rotate(c + f4(d, a, b) + word2 + 0x2ad7d2bb, 15) + d;
+ b = rotate(b + f4(c, d, a) + word9 + 0xeb86d391, 21) + c;
+
+ // update hash
+ m_hash[0] += a;
+ m_hash[1] += b;
+ m_hash[2] += c;
+ m_hash[3] += d;
+}
+
+/// add arbitrary number of bytes
+void MD5::add(const void *data, size_t numBytes) {
+ const uint8_t *current = (const uint8_t *)data;
+
+ if (m_bufferSize > 0) {
+ while (numBytes > 0 && m_bufferSize < BlockSize) {
+ m_buffer[m_bufferSize++] = *current++;
+ numBytes--;
+ }
+ }
+
+ // full buffer
+ if (m_bufferSize == BlockSize) {
+ processBlock(m_buffer);
+ m_numBytes += BlockSize;
+ m_bufferSize = 0;
+ }
+
+ // no more data ?
+ if (numBytes == 0)
+ return;
+
+ // process full blocks
+ while (numBytes >= BlockSize) {
+ processBlock(current);
+ current += BlockSize;
+ m_numBytes += BlockSize;
+ numBytes -= BlockSize;
+ }
+
+ // keep remaining bytes in buffer
+ while (numBytes > 0) {
+ m_buffer[m_bufferSize++] = *current++;
+ numBytes--;
+ }
+}
+
+/// process final block, less than 64 bytes
+void MD5::processBuffer() {
+ // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte
+
+ // - append "1" bit to message
+ // - append "0" bits until message length in bit mod 512 is 448
+ // - append length as 64 bit integer
+
+ // number of bits
+ size_t paddedLength = m_bufferSize * 8;
+
+ // plus one bit set to 1 (always appended)
+ paddedLength++;
+
+ // number of bits must be (numBits % 512) = 448
+ size_t lower11Bits = paddedLength & 511;
+ if (lower11Bits <= 448)
+ paddedLength += 448 - lower11Bits;
+ else
+ paddedLength += 512 + 448 - lower11Bits;
+ // convert from bits to bytes
+ paddedLength /= 8;
+
+ // only needed if additional data flows over into a second block
+ unsigned char extra[BlockSize];
+
+ // append a "1" bit, 128 => binary 10000000
+ if (m_bufferSize < BlockSize)
+ m_buffer[m_bufferSize] = 128;
+ else
+ extra[0] = 128;
+
+ size_t i;
+ for (i = m_bufferSize + 1; i < BlockSize; i++)
+ m_buffer[i] = 0;
+ for (; i < paddedLength; i++)
+ extra[i - BlockSize] = 0;
+
+ // add message length in bits as 64 bit number
+ uint64_t msgBits = 8 * (m_numBytes + m_bufferSize);
+ // find right position
+ unsigned char *addLength;
+ if (paddedLength < BlockSize)
+ addLength = m_buffer + paddedLength;
+ else
+ addLength = extra + paddedLength - BlockSize;
+
+ // must be little endian
+ *addLength++ = msgBits & 0xFF;
+ msgBits >>= 8;
+ *addLength++ = msgBits & 0xFF;
+ msgBits >>= 8;
+ *addLength++ = msgBits & 0xFF;
+ msgBits >>= 8;
+ *addLength++ = msgBits & 0xFF;
+ msgBits >>= 8;
+ *addLength++ = msgBits & 0xFF;
+ msgBits >>= 8;
+ *addLength++ = msgBits & 0xFF;
+ msgBits >>= 8;
+ *addLength++ = msgBits & 0xFF;
+ msgBits >>= 8;
+ *addLength++ = msgBits & 0xFF;
+
+ // process blocks
+ processBlock(m_buffer);
+ // flowed over into a second block ?
+ if (paddedLength > BlockSize)
+ processBlock(extra);
+}
+
+/// return latest hash as 32 hex characters
+std::string MD5::getHash() {
+ // compute hash (as raw bytes)
+ unsigned char rawHash[HashBytes];
+ getHash(rawHash);
+
+ // convert to hex string
+ std::string result;
+ result.reserve(2 * HashBytes);
+ for (int i = 0; i < HashBytes; i++) {
+ static const char dec2hex[16 + 1] = "0123456789abcdef";
+ result += dec2hex[(rawHash[i] >> 4) & 15];
+ result += dec2hex[rawHash[i] & 15];
+ }
+
+ return result;
+}
+
+/// return latest hash as bytes
+void MD5::getHash(unsigned char buffer[MD5::HashBytes]) {
+ // save old hash if buffer is partially filled
+ uint32_t oldHash[HashValues];
+ for (int i = 0; i < HashValues; i++)
+ oldHash[i] = m_hash[i];
+
+ // process remaining bytes
+ processBuffer();
+
+ unsigned char *current = buffer;
+ for (int i = 0; i < HashValues; i++) {
+ *current++ = m_hash[i] & 0xFF;
+ *current++ = (m_hash[i] >> 8) & 0xFF;
+ *current++ = (m_hash[i] >> 16) & 0xFF;
+ *current++ = (m_hash[i] >> 24) & 0xFF;
+
+ // restore old hash
+ m_hash[i] = oldHash[i];
+ }
+}
+
+/// compute MD5 of a memory block
+std::string MD5::operator()(const void *data, size_t numBytes) {
+ reset();
+ add(data, numBytes);
+ return getHash();
+}
+
+/// compute MD5 of a string, excluding final zero
+std::string MD5::operator()(const std::string &text) {
+ reset();
+ add(text.c_str(), text.size());
+ return getHash();
+}
diff --git a/external/libdumper/source/npbind.cpp b/external/libdumper/source/npbind.cpp
index 1f70364..b67bb2c 100644
--- a/external/libdumper/source/npbind.cpp
+++ b/external/libdumper/source/npbind.cpp
@@ -1,29 +1,34 @@
-// Copyright (c) 2021 Al Azif
+// Copyright (c) 2021-2022 Al Azif
// License: GPLv3
#include "npbind.hpp"
-#include "common.hpp"
#include
+#include
#include
#include
#include
-#include
#include
+#include
#include
-#include "log.h"
-#include "libshahash.h"
+
+#include "common.hpp"
+
+#include
+
namespace npbind {
std::vector read(const std::string &path) { // Flawfinder: ignore
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return std::vector();
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path)) {
log_error("Input path does not exist or is not a file!");
+ return std::vector();
}
// Open path
@@ -31,6 +36,7 @@ std::vector read(const std::string &path) { // Flawfinder:
if (!npbind_input || !npbind_input.good()) {
npbind_input.close();
log_error("Cannot open file: %s", path.c_str());
+ return std::vector();
}
// Check file magic (Read in whole header)
@@ -39,10 +45,13 @@ std::vector read(const std::string &path) { // Flawfinder:
if (!npbind_input.good()) {
npbind_input.close();
log_error("Error reading header!");
+ return std::vector();
+
}
if (__builtin_bswap32(header.magic) != NPBIND_MAGIC) {
npbind_input.close();
log_error("Input path is not a npbind.dat!");
+ return std::vector();
}
// Read in body(s)
@@ -53,24 +62,24 @@ std::vector read(const std::string &path) { // Flawfinder:
if (!npbind_input.good()) {
npbind_input.close();
log_error("Error reading entries!");
+ return std::vector();
}
entries.push_back(temp_entry);
}
// Read digest
- unsigned char digest[20];
- npbind_input.seekg(-sizeof(digest), npbind_input.end); // Make sure we are in the right place
- npbind_input.read((char *)&digest, sizeof(digest)); // Flawfinder: ignore
+ std::vector digest(SHA1::HashBytes);
+ npbind_input.seekg(-digest.size(), npbind_input.end); // Make sure we are in the right place
+ npbind_input.read(reinterpret_cast(&digest[0]), digest.size()); // Flawfinder: ignore
if (!npbind_input.good()) {
// Should never reach here... will affect coverage %
npbind_input.close();
log_error("Error reading digest!");
+ return std::vector();
}
npbind_input.close();
- // Check digest
- unsigned char calculated_digest[sizeof(digest)];
-
+// Check digest
std::stringstream ss;
ss.write(reinterpret_cast(&header), sizeof(header));
@@ -78,34 +87,22 @@ std::vector read(const std::string &path) { // Flawfinder:
ss.write(reinterpret_cast(&entries[i]), sizeof(NpBindEntry));
}
- unsigned char data_to_hash[ss.str().size()];
+ std::vector data_to_hash;
for (size_t i = 0; i < ss.str().size(); i++) {
- data_to_hash[i] = ss.str().c_str()[i];
+ data_to_hash.push_back(ss.str().c_str()[i]);
}
- Sha1Context context;
- Sha1BlockInit(&context);
- Sha1BlockUpdate(&context, data_to_hash, ss.str().size());
- Sha1BlockResult(&context, calculated_digest);
+ std::vector calculated_digest(digest.size());
-#if 0
- log_info("\[SHA1] Digest: ");
+ SHA1 sha1;
+ sha1(&data_to_hash[0], ss.str().size());
+ sha1.getHash(&calculated_digest[0]);
- for (int i = 0; i < 20; i++)
- log_info("%x\n", digest[i]);
- log_info("[SHA1] calculated_digest: ");
-
- for (int i = 0; i < 20; i++)
- log_info("%x", calculated_digest[i]);
-
- log_info("\n");
-#endif
-
- if (std::memcmp(calculated_digest, digest, sizeof(digest)) != 0) {
- log_info("[SHA1] File %s has failed validation", path.c_str());
+ if (std::memcmp(&calculated_digest[0], &digest[0], digest.size()) != 0) {
+ log_error("Digests do not match! Aborting...");
}
- else
- log_info("[SHA1] File %s Successfully validated", path.c_str());
+ else
+ log_info("Digests match!");
return entries;
}
diff --git a/external/libdumper/source/pfs.cpp b/external/libdumper/source/pfs.cpp
index 46ea001..1fc876e 100644
--- a/external/libdumper/source/pfs.cpp
+++ b/external/libdumper/source/pfs.cpp
@@ -4,9 +4,7 @@
#include "pkg.hpp"
#include "common.hpp"
#include "log.h"
-extern "C" {
#include "lang.h"
-}
#include
#include
@@ -15,211 +13,182 @@ extern "C" {
#include
#include "pfs.hpp"
#include
-
-uint32_t pfs_progress = 10;
-
+#include
namespace pfs {
- pfs_header* header;
- uint64_t pfs_size;
- uint64_t pfs_copied;
- di_d32* inodes;
+ std::unique_ptr header;
+ uint64_t pfs_size = 0;
+ uint64_t pfs_copied = 0;
+ std::vector inodes;
+ std::unique_ptr copy_buffer;
+ int pfs = -1;
+ uint32_t pfs_progress = 10;
+ Dump_Options dump_op;
- char* copy_buffer = NULL;
- int pfs;
-
- void memcpy_to_file(const char* fname, uint64_t ptr, uint64_t size, char* title, const char* tid)
+ void memcpy_to_file(const std::string& fname, uint64_t ptr, uint64_t size, const std::string& title, const std::string& tid)
{
uint64_t bytes;
uint64_t ix = 0;
-
- int fd = sceKernelOpen(fname, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0777);
- log_info("---- name: %s, ptr %p, sz %zu, FD: %i", fname, ptr, size, fd);
+ if(std::filesystem::exists(fname)){
+ log_debug("File already exists: %s", fname.c_str());
+ size -= bytes;
+ ix++;
+ pfs_copied += bytes;
+ if (pfs_copied > pfs_size) pfs_copied = pfs_size;
+ pfs_progress = (uint64_t)(((float)pfs_copied / pfs_size) * 100.f);
+ return;
+ }
+ int fd = sceKernelOpen(fname.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0777);
+ log_info("---- name: %s, ptr %p, sz %zu, FD: %i", fname.c_str(), ptr, size, fd);
if (fd > 0)
{
while (size > 0)
{
bytes = (size > PFS_DUMP_BUFFER) ? PFS_DUMP_BUFFER : size;
- log_info("copying %llu bytes ix: %i size: %llu pfs prog: %llu/%llu", bytes, ix, size, pfs_copied, pfs_size);
+ // log_info("copying %llu bytes ix: %i size: %llu pfs prog: %llu/%llu", bytes, ix, size, pfs_copied, pfs_size);
sceKernelLseek(pfs, ptr + ix * PFS_DUMP_BUFFER, SEEK_SET);
- sceKernelRead(pfs, copy_buffer, bytes);
- sceKernelWrite(fd, copy_buffer, bytes);
+ sceKernelRead(pfs, copy_buffer.get(), bytes);
+ sceKernelWrite(fd, copy_buffer.get(), bytes);
size -= bytes;
ix++;
pfs_copied += bytes;
if (pfs_copied > pfs_size) pfs_copied = pfs_size;
pfs_progress = (uint64_t)(((float)pfs_copied / pfs_size) * 100.f);
- if (strstr(fname, "patch"))
- ProgUpdate(pfs_progress, "%s\n\n%s %s\n%s %s\n\n%s\n %s...\n", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME), title, getDumperLangSTR(TITLE_ID), tid, getDumperLangSTR(EXT_PATCH), fname);
- else
- ProgUpdate(pfs_progress, "%s\n\n%s %s\n%s %s\n\n%s\n %s...\n", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME), title, getDumperLangSTR(TITLE_ID), tid, getDumperLangSTR(EXT_GAME_FILES), fname);
-
+ switch(pfs::dump_op){
+ case BASE_GAME:
+ ProgUpdate(pfs_progress, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + tid + "\n\n" + getDumperLangSTR(EXT_GAME_FILES) + "\n" + fname);
+ break;
+ case GAME_PATCH:
+ ProgUpdate(pfs_progress, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + tid + "\n\n" + getDumperLangSTR(EXT_PATCH) + "\n" + fname);
+ break;
+ case ADDITIONAL_CONTENT_DATA:
+ ProgUpdate(pfs_progress, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(DLC_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + tid + "\n\n" + getDumperLangSTR(EXT_ADDON) + "\n" + fname);
+ break;
+ default:
+ break;
+ }
}
log_info("Close(fd): %x", sceKernelClose(fd));
}
else
{
- log_error("Cannot copy file |%s|\n\nError: %x!", fname, fd);
+ log_error("Cannot copy file |%s|\n\nError: %x!", fname.c_str(), fd);
}
}
- void __parse_directory(uint32_t ino, uint32_t lev, const char* parent_name, bool dry_run, char* title, const char* tid)
- {
- for (uint32_t z = 0; z < inodes[ino].blocks; z++)
- {
- uint32_t db = inodes[ino].db[0] + z;
- uint64_t pos = (uint64_t)header->blocksz * db;
- uint64_t size = inodes[ino].size;
- uint64_t top = pos + size;
+ void __parse_directory(uint32_t ino, uint32_t lev, const std::string& parent_name, bool dry_run, const std::string& title = "dry", const std::string& tid = "run") {
- while (pos < top)
- {
- pfs_dirent_t* ent = (pfs_dirent_t*)malloc(sizeof(pfs_dirent_t));
- sceKernelLseek(pfs, pos, SEEK_SET);
- sceKernelRead(pfs, ent, sizeof(pfs_dirent_t));
+ for (uint32_t z = 0; z < inodes[ino].blocks; z++) {
+ uint32_t db = inodes[ino].db[0] + z;
+ uint64_t pos = static_cast(header->blocksz) * db;
+ uint64_t size = inodes[ino].size;
+ uint64_t top = pos + size;
- if (ent->type == 0)
- {
- free(ent);
- break;
- }
+ while (pos < top) {
+ std::unique_ptr ent = std::make_unique();
+ sceKernelLseek(pfs, pos, SEEK_SET);
+ sceKernelRead(pfs, ent.get(), sizeof(pfs_dirent_t));
- char* name = (char*)malloc(ent->namelen + 1);
- memset(name, 0, ent->namelen + 1);
- if (lev > 0)
- {
- sceKernelLseek(pfs, pos + sizeof(pfs_dirent_t), SEEK_SET);
- sceKernelRead(pfs, name, ent->namelen);
- }
-
-
- char* fname = (char*)malloc(strlen(parent_name) + ent->namelen + 2);
- if (parent_name != NULL)
- sprintf(fname, "%s/%s", parent_name, name);
- else
- sprintf(fname, "%s", name);
-
- if ((ent->type == 2) && (lev > 0))
- {
- if (!dry_run) {
- if (strstr(fname, "patch"))
- ProgUpdate(pfs_progress, "%s\n\n%s %s\n%s %s\n\n%s\n %s...\n", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME), title, getDumperLangSTR(TITLE_ID), tid, getDumperLangSTR(EXT_PATCH), fname);
- else
- ProgUpdate(pfs_progress, "%s\n\n%s %s\n%s %s\n\n%s\n %s...\n", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME), title, getDumperLangSTR(TITLE_ID), tid, getDumperLangSTR(EXT_GAME_FILES), fname);
- }
- if (dry_run)
- pfs_size += inodes[ent->ino].size;
- else
- memcpy_to_file(fname, (uint64_t)header->blocksz * inodes[ent->ino].db[0], inodes[ent->ino].size, title, tid);
-
- log_info("%s -- prog %i", fname, pfs_progress);
- }
- else
- if (ent->type == 3)
- {
- log_info(">scan dir %s", name);
- mkdir(fname, 0777);
- __parse_directory(ent->ino, lev + 1, fname, dry_run, title, tid);
- }
-
- pos += ent->entsize;
-
- free(ent);
- free(name);
- free(fname);
+ if (ent->type == 0) {
+ break;
}
+
+ std::unique_ptr name = std::make_unique(ent->namelen + 1);
+ memset(name.get(), 0, ent->namelen + 1);
+ if (lev > 0) {
+ sceKernelLseek(pfs, pos + sizeof(pfs_dirent_t), SEEK_SET);
+ sceKernelRead(pfs, name.get(), ent->namelen);
+ }
+ std::string fname;
+ if (parent_name.empty()) {
+ fname = name.get();
+ } else {
+ fname = parent_name + "/" + name.get();
+ }
+
+ if ((ent->type == 2) && (lev > 0)) {
+ if (!dry_run) {
+ pfs_progress = (uint64_t)(((float)pfs_copied / pfs_size) * 100.f);
+ switch(pfs::dump_op){
+ case BASE_GAME:
+ ProgUpdate(pfs_progress, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + tid + "\n\n" + getDumperLangSTR(EXT_GAME_FILES) + "\n" + fname);
+ break;
+ case GAME_PATCH:
+ ProgUpdate(pfs_progress, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(APP_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + tid + "\n\n" + getDumperLangSTR(EXT_PATCH) + "\n" + fname);
+ break;
+ case ADDITIONAL_CONTENT_DATA:
+ ProgUpdate(pfs_progress, getDumperLangSTR(DUMP_INFO) + "\n\n" + getDumperLangSTR(DLC_NAME) + ": " + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + tid + "\n\n" + getDumperLangSTR(EXT_ADDON) + "\n" + fname);
+ break;
+ default:
+ break;
+ }
+ }
+ if (dry_run) {
+ pfs_size += inodes[ent->ino].size;
+ } else {
+ memcpy_to_file(fname, static_cast(header->blocksz) * inodes[ent->ino].db[0], inodes[ent->ino].size, title, tid);
+ }
+
+ // log_info("%s -- prog %i", fname.c_str(), pfs_progress);
+ } else if (ent->type == 3) {
+ log_info(">scan dir %s", name.get());
+ mkdir(fname.c_str(), 0777);
+ __parse_directory(ent->ino, lev + 1, fname, dry_run, title, tid);
+ }
+
+ pos += ent->entsize;
}
}
-
- void dump_pfs(uint32_t ino, uint32_t level, const std::string& output_path, const char* tid, char* title) {
- __parse_directory(ino, level, output_path.c_str(), false, title, tid);
+}
+ void dump_pfs(uint32_t ino, uint32_t level, const std::string& output_path, const std::string& tid, const std::string& title) {
+ __parse_directory(ino, level, output_path, false, title, tid);
}
void calculate_pfs(uint32_t ino, uint32_t level, const std::string& output_path) {
- __parse_directory(ino, level, output_path.c_str(), true);
+ __parse_directory(ino, level, output_path, true);
}
- bool extract(const std::string& pfs_path, const std::string& output_path, const char* tid, char* title)
+ bool extract(const std::string& pfs_path, const std::string& output_path, const std::string& tid, const std::string& title)
{
pfs_progress = 11;
+ // clean output path
- pfs = sceKernelOpen(pfs_path.c_str(), O_RDONLY, 0);
- if (pfs < 0) {
- log_fatal("could not open %s", pfs_path.c_str());
- return false;
- }
-
-
- if ((copy_buffer = (char*)malloc(PFS_DUMP_BUFFER)) != NULL)
- {
-
- if ((header = (pfs_header*)malloc(sizeof(pfs_header))) != NULL)
- {
- sceKernelLseek(pfs, 0, SEEK_SET);
- sceKernelRead(pfs, header, sizeof(pfs_header));
-
- if (__builtin_bswap64(header->magic) != PFS_MAGIC) {
- log_fatal("MAGIC != PFS_MAGIC");
- sceKernelClose(pfs);
- free(copy_buffer);
- free(header);
- return false;
- }
-
- if ((inodes = (di_d32*)malloc(sizeof(di_d32) * header->ndinode)) != NULL)
- {
-
- uint32_t ix = 0;
-
- ProgUpdate(20, "%s\n\n%s %s\n%s %s\n\n %s ...", getDumperLangSTR(DUMP_INFO), getDumperLangSTR(APP_NAME), title, getDumperLangSTR(TITLE_ID), tid, getDumperLangSTR(PROCESSING));
-
- for (uint32_t i = 0; i < header->ndinodeblock; i++)
- {
- for (uint32_t j = 0; (j < (header->blocksz / sizeof(di_d32))) && (ix < header->ndinode); j++)
- {
- sceKernelLseek(pfs, (uint64_t)header->blocksz * (i + 1) + sizeof(di_d32) * j, SEEK_SET);
- sceKernelRead(pfs, &inodes[ix], sizeof(di_d32));
-
- ix++;
- }
- }
-
- pfs_size = 0;
- pfs_copied = 0;
-
- calculate_pfs((uint32_t)header->superroot_ino, (uint32_t)0, output_path);
- dump_pfs((uint32_t)header->superroot_ino, (uint32_t)0, output_path, tid, title);
-
- free(header);
- free(inodes);
- free(copy_buffer);
- sceKernelClose(pfs);
- }
- else
- {
- free(copy_buffer);
- free(header);
- sceKernelClose(pfs);
- log_fatal("Buffer is NULL");
- return false;
- }
- }
- else
- {
- free(copy_buffer);
- log_fatal("Buffer is NULL");
+ pfs = sceKernelOpen(pfs_path.c_str(), O_RDONLY, 0);
+ if (pfs < 0) {
+ log_fatal("could not open %s", pfs_path.c_str());
return false;
}
+
+ copy_buffer = std::make_unique(PFS_DUMP_BUFFER);
+
+ header = std::make_unique();
+ sceKernelLseek(pfs, 0, SEEK_SET);
+ sceKernelRead(pfs, header.get(), sizeof(pfs_header));
+
+ if (__builtin_bswap64(header->magic) != PFS_MAGIC) {
+ log_fatal("MAGIC != PFS_MAGIC");
+ sceKernelClose(pfs);
+ return false;
+ }
+ inodes.resize(header->ndinode);
+ uint32_t ix = 0;
+
+ ProgUpdate(20, getDumperLangSTR(DUMP_INFO) + "\n\n" + title + "\n" + getDumperLangSTR(TITLE_ID) + " " + tid + "\n\n" + getDumperLangSTR(PROCESSING));
+ for (uint32_t i = 0; i < header->ndinodeblock; i++) {
+ for (uint32_t j = 0; (j < (header->blocksz / sizeof(di_d32))) && (ix < header->ndinode); j++) {
+ sceKernelLseek(pfs, (uint64_t)header->blocksz * (i + 1) + sizeof(di_d32) * j, SEEK_SET);
+ sceKernelRead(pfs, &inodes[ix], sizeof(di_d32));
+
+ ix++;
+ }
+ }
+
+ pfs_size = 0;
+ pfs_copied = 0;
+
+ calculate_pfs((uint32_t)header->superroot_ino, (uint32_t)0, output_path);
+ dump_pfs((uint32_t)header->superroot_ino, (uint32_t)0, output_path, tid, title);
+ sceKernelClose(pfs);
+
+ return true;
}
- else
- {
-
- log_fatal("Buffer is NULL");
- return false;
- }
-
- return true;
- }
-
-} // namespace pfs
-
-
+} // namespace pfs
\ No newline at end of file
diff --git a/external/libdumper/source/pkg.cpp b/external/libdumper/source/pkg.cpp
index e7c4734..966599b 100644
--- a/external/libdumper/source/pkg.cpp
+++ b/external/libdumper/source/pkg.cpp
@@ -14,20 +14,37 @@
#include
#include
#include
+#include
#include
namespace pkg {
std::string get_entry_name_by_type(uint32_t type) {
std::stringstream ss;
- if (type == 0x0400) {
+ if (type == 0x0001) {
+ ss << ".digests";
+ } else if (type == 0x0010) {
+ ss << ".entry_keys";
+ } else if (type == 0x0020) {
+ ss << ".image_key";
+ } else if (type == 0x0021) {
+ ss << ".unknown_0x21";
+ } else if (type == 0x0080) {
+ ss << ".general_digests";
+ } else if (type == 0x00C0) {
+ ss << ".unknown_0xC0";
+ } else if (type == 0x0100) {
+ ss << ".metas";
+ } else if (type == 0x0200) {
+ ss << ".entry_names";
+ } else if (type == 0x0400) {
ss << "license.dat";
} else if (type == 0x0401) {
ss << "license.info";
} else if (type == 0x0402) {
- ss << "nptitle.dat.encrypted";
+ ss << "nptitle.dat";
} else if (type == 0x0403) {
- ss << "npbind.dat.encrypted";
+ ss << "npbind.dat";
} else if (type == 0x0404) {
ss << "selfinfo.dat";
} else if (type == 0x0406) {
@@ -93,7 +110,7 @@ std::string get_entry_name_by_type(uint32_t type) {
} else if ((type >= 0x12C1) && (type <= 0x12DF)) {
ss << "pic1_" << std::setfill('0') << std::setw(2) << type - 0x12C1 << ".dds";
} else if ((type >= 0x1400) && (type <= 0x147F)) {
- ss << "trophy/trophy" << std::setfill('0') << std::setw(2) << type - 0x1400 << ".trp.encrypted";
+ ss << "trophy/trophy" << std::setfill('0') << std::setw(2) << type - 0x1400 << ".trp";
} else if ((type >= 0x1600) && (type <= 0x1609)) {
ss << "keymap_rp/" << std::setfill('0') << std::setw(3) << type - 0x15FF << ".png";
} else if ((type >= 0x1610) && (type <= 0x16F5)) {
@@ -107,6 +124,7 @@ bool extract_sc0(const std::string &pkg_path, const std::string &output_path, co
// Check for empty or pure whitespace path
if (pkg_path.empty() || std::all_of(pkg_path.begin(), pkg_path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty input path argument!");
+ return false;
}
// Check if file exists and is file
@@ -120,6 +138,7 @@ bool extract_sc0(const std::string &pkg_path, const std::string &output_path, co
if (!pkg_input || !pkg_input.good()) {
pkg_input.close();
log_error("Cannot open input file: %s" ,pkg_path.c_str());
+ return false;
}
// Check file magic (Read in whole header)
@@ -170,23 +189,18 @@ bool extract_sc0(const std::string &pkg_path, const std::string &output_path, co
// Extract sc0 entries
for (auto &&entry : entries) {
std::string entry_name = get_entry_name_by_type(__builtin_bswap32(entry.id));
- if (entry_name.empty()){//is_PKG_entry_needed(entry_name)) {
+ if (!entry_name.empty()){//is_PKG_entry_needed(entry_name)) {
+ bool entry_encrpyted((__builtin_bswap32(entry.flags1) & 0x80000000) != 0);
+ uint32_t entry_key_index = (__builtin_bswap32(entry.flags2) & 0xF000) >> 12;
std::filesystem::path temp_output_path(output_path);
temp_output_path /= entry_name;
pkg_input.seekg(__builtin_bswap32(entry.offset), pkg_input.beg);
- char* buf = (char*)malloc(__builtin_bswap32(entry.size));
- if (buf != nullptr)
- {
- pkg_input.read(buf, __builtin_bswap32(entry.size)); // Flawfinder: ignore
- if (!pkg_input.good()) {
- pkg_input.close();
- log_error("Error reading entry data!");
- return false;
- }
- }
- else {
- log_error("buf null\n");
+ std::unique_ptr buf = std::make_unique(__builtin_bswap32(entry.size));
+ pkg_input.read(buf.get(), __builtin_bswap32(entry.size)); // Flawfinder: ignore
+ if (!pkg_input.good()) {
+ pkg_input.close();
+ log_error("Error reading entry data!");
return false;
}
@@ -204,6 +218,14 @@ bool extract_sc0(const std::string &pkg_path, const std::string &output_path, co
log_error("Unable to open/create output subdirectory");
}
+ if (entry_encrpyted) {
+ // Only have key at index 3
+ if (entry_key_index == 3) {
+ // TODO: Decrypt and save as `temp_output_path`
+ }
+ temp_output_path += ".encrypted";
+ }
+
// Write to file
//unlink anyways idc
@@ -211,7 +233,7 @@ bool extract_sc0(const std::string &pkg_path, const std::string &output_path, co
int fd = open(temp_output_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (fd >= 0) {
- write(fd, buf, __builtin_bswap32(entry.size));
+ write(fd, buf.get(), __builtin_bswap32(entry.size));
close(fd);
}
else {
@@ -221,9 +243,6 @@ bool extract_sc0(const std::string &pkg_path, const std::string &output_path, co
return false;
}
-
- free(buf);
-
}
}
pkg_input.close();
diff --git a/external/libdumper/source/sfo.cpp b/external/libdumper/source/sfo.cpp
index a66b9ce..7deb933 100644
--- a/external/libdumper/source/sfo.cpp
+++ b/external/libdumper/source/sfo.cpp
@@ -17,11 +17,13 @@ bool is_sfo(const std::string &path) {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return false;
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path)) {
log_error("Input path does not exist or is not a file!");
+ return false;
}
// Open path
@@ -29,6 +31,7 @@ bool is_sfo(const std::string &path) {
if (!sfo_input || !sfo_input.good()) {
sfo_input.close();
log_error("Cannot open file: %s" ,path.c_str());
+ return false;
}
// Read SFO header
@@ -52,11 +55,13 @@ std::vector read(const std::string &path) { // Flawfinder: ignore
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return std::vector();
}
// Check if file exists and is file
if (!std::filesystem::is_regular_file(path)) {
log_error("Input path does not exist or is not a file!");
+ return std::vector();
}
// Open path
@@ -64,12 +69,14 @@ std::vector read(const std::string &path) { // Flawfinder: ignore
if (!sfo_input || !sfo_input.good()) {
sfo_input.close();
log_error("Cannot open file: %s",path.c_str());
+ return std::vector();
}
// Check to make sure file is a SFO
if (!is_sfo(path)) {
sfo_input.close();
log_error("Input path is not a SFO!");
+ return std::vector();
}
// Read SFO header
@@ -79,6 +86,7 @@ std::vector read(const std::string &path) { // Flawfinder: ignore
// Should never reach here... will affect coverage %
sfo_input.close();
log_error("Error reading SFO header!");
+ return std::vector();
}
std::vector data;
@@ -91,26 +99,31 @@ std::vector read(const std::string &path) { // Flawfinder: ignore
if (!sfo_input.good()) {
sfo_input.close();
log_error("Error reading entry key offset!");
+ return std::vector();
}
sfo_input.read((char *)&temp_data.format, sizeof(temp_data.format)); // Flawfinder: ignore
if (!sfo_input.good()) {
sfo_input.close();
log_error("Error reading entry format!");
+ return std::vector();
}
sfo_input.read((char *)&temp_data.length, sizeof(temp_data.length)); // Flawfinder: ignore
if (!sfo_input.good()) {
sfo_input.close();
log_error("Error reading entry length!");
+ return std::vector();
}
sfo_input.read((char *)&temp_data.max_length, sizeof(temp_data.max_length)); // Flawfinder: ignore
if (!sfo_input.good()) {
sfo_input.close();
log_error("Error reading entry max length!");
+ return std::vector();
}
sfo_input.read((char *)&temp_data.data_offset, sizeof(temp_data.data_offset)); // Flawfinder: ignore
if (!sfo_input.good()) {
sfo_input.close();
log_error("Error reading entry data offset!");
+ return std::vector();
}
data.push_back(temp_data);
}
@@ -313,6 +326,7 @@ std::vector add_pubtool_data(const SfoPubtoolinfoIndex &data_to_add, co
new_value.pop_back(); // Remove trailing comma
if (new_value.size() > 0x200) {
log_error("New PUBTOOLINFO key is too large (> 0x200)!");
+ return new_data;
}
SfoData new_entry = build_data("PUBTOOLINFO", "utf-8", new_value.size(), 0x200, new_value);
@@ -377,6 +391,7 @@ void write(const std::vector &data, const std::string &path) {
// Check for empty or pure whitespace path
if (path.empty() || std::all_of(path.begin(), path.end(), [](char c) { return std::isspace(c); })) {
log_error("Empty path argument!");
+ return;
}
std::filesystem::path output_path(path);
@@ -384,6 +399,7 @@ void write(const std::vector &data, const std::string &path) {
// Exists, but is not a file
if (std::filesystem::exists(output_path) && !std::filesystem::is_regular_file(output_path)) {
log_error("Output path exists, but is not a file!");
+ return;
}
// Open path
@@ -391,6 +407,7 @@ void write(const std::vector &data, const std::string &path) {
if (!output_file || !output_file.good()) {
output_file.close();
log_error("Cannot open output file: %s",path.c_str());
+ return;
}
// Check for duplicate key_name
@@ -408,14 +425,17 @@ void write(const std::vector &data, const std::string &path) {
if (entry.format != 0x0004 && entry.format != 0x0204 && entry.format != 0x0404) {
output_file.close();
log_error("Unknown SFO format type!");
+ return;
}
if (entry.length > entry.max_length) {
output_file.close();
log_error("Input `length` of SFO entry must be <= input `max_length`!");
+ return;
}
if (entry.data.size() > entry.length) {
output_file.close();
log_error("Input SFO data is larger than the `length`");
+ return;
}
SfoData temp_data = entry;
diff --git a/external/libdumper/source/sha1.cpp b/external/libdumper/source/sha1.cpp
new file mode 100644
index 0000000..f0d4786
--- /dev/null
+++ b/external/libdumper/source/sha1.cpp
@@ -0,0 +1,307 @@
+// //////////////////////////////////////////////////////////
+// sha1.cpp
+// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#include "sha1.h"
+
+// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN
+
+/// same as reset()
+SHA1::SHA1() {
+ reset();
+}
+
+/// restart
+void SHA1::reset() {
+ m_numBytes = 0;
+ m_bufferSize = 0;
+
+ // according to RFC 1321
+ m_hash[0] = 0x67452301;
+ m_hash[1] = 0xefcdab89;
+ m_hash[2] = 0x98badcfe;
+ m_hash[3] = 0x10325476;
+ m_hash[4] = 0xc3d2e1f0;
+}
+
+namespace {
+// mix functions for processBlock()
+inline uint32_t f1(uint32_t b, uint32_t c, uint32_t d) {
+ return d ^ (b & (c ^ d)); // original: f = (b & c) | ((~b) & d);
+}
+
+inline uint32_t f2(uint32_t b, uint32_t c, uint32_t d) {
+ return b ^ c ^ d;
+}
+
+inline uint32_t f3(uint32_t b, uint32_t c, uint32_t d) {
+ return (b & c) | (b & d) | (c & d);
+}
+
+inline uint32_t rotate(uint32_t a, uint32_t c) {
+ return (a << c) | (a >> (32 - c));
+}
+
+inline uint32_t swap(uint32_t x) {
+#if defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap32(x);
+#endif
+#ifdef MSC_VER
+ return _byteswap_ulong(x);
+#endif
+
+ return (x >> 24) |
+ ((x >> 8) & 0x0000FF00) |
+ ((x << 8) & 0x00FF0000) |
+ (x << 24);
+}
+} // namespace
+
+/// process 64 bytes
+void SHA1::processBlock(const void *data) {
+ // get last hash
+ uint32_t a = m_hash[0];
+ uint32_t b = m_hash[1];
+ uint32_t c = m_hash[2];
+ uint32_t d = m_hash[3];
+ uint32_t e = m_hash[4];
+
+ // data represented as 16x 32-bit words
+ const uint32_t *input = (uint32_t *)data;
+ // convert to big endian
+ uint32_t words[80];
+ for (int i = 0; i < 16; i++)
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
+ words[i] = input[i];
+#else
+ words[i] = swap(input[i]);
+#endif
+
+ // extend to 80 words
+ for (int i = 16; i < 80; i++)
+ words[i] = rotate(words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16], 1);
+
+ // first round
+ for (int i = 0; i < 4; i++) {
+ int offset = 5 * i;
+ e += rotate(a, 5) + f1(b, c, d) + words[offset] + 0x5a827999;
+ b = rotate(b, 30);
+ d += rotate(e, 5) + f1(a, b, c) + words[offset + 1] + 0x5a827999;
+ a = rotate(a, 30);
+ c += rotate(d, 5) + f1(e, a, b) + words[offset + 2] + 0x5a827999;
+ e = rotate(e, 30);
+ b += rotate(c, 5) + f1(d, e, a) + words[offset + 3] + 0x5a827999;
+ d = rotate(d, 30);
+ a += rotate(b, 5) + f1(c, d, e) + words[offset + 4] + 0x5a827999;
+ c = rotate(c, 30);
+ }
+
+ // second round
+ for (int i = 4; i < 8; i++) {
+ int offset = 5 * i;
+ e += rotate(a, 5) + f2(b, c, d) + words[offset] + 0x6ed9eba1;
+ b = rotate(b, 30);
+ d += rotate(e, 5) + f2(a, b, c) + words[offset + 1] + 0x6ed9eba1;
+ a = rotate(a, 30);
+ c += rotate(d, 5) + f2(e, a, b) + words[offset + 2] + 0x6ed9eba1;
+ e = rotate(e, 30);
+ b += rotate(c, 5) + f2(d, e, a) + words[offset + 3] + 0x6ed9eba1;
+ d = rotate(d, 30);
+ a += rotate(b, 5) + f2(c, d, e) + words[offset + 4] + 0x6ed9eba1;
+ c = rotate(c, 30);
+ }
+
+ // third round
+ for (int i = 8; i < 12; i++) {
+ int offset = 5 * i;
+ e += rotate(a, 5) + f3(b, c, d) + words[offset] + 0x8f1bbcdc;
+ b = rotate(b, 30);
+ d += rotate(e, 5) + f3(a, b, c) + words[offset + 1] + 0x8f1bbcdc;
+ a = rotate(a, 30);
+ c += rotate(d, 5) + f3(e, a, b) + words[offset + 2] + 0x8f1bbcdc;
+ e = rotate(e, 30);
+ b += rotate(c, 5) + f3(d, e, a) + words[offset + 3] + 0x8f1bbcdc;
+ d = rotate(d, 30);
+ a += rotate(b, 5) + f3(c, d, e) + words[offset + 4] + 0x8f1bbcdc;
+ c = rotate(c, 30);
+ }
+
+ // fourth round
+ for (int i = 12; i < 16; i++) {
+ int offset = 5 * i;
+ e += rotate(a, 5) + f2(b, c, d) + words[offset] + 0xca62c1d6;
+ b = rotate(b, 30);
+ d += rotate(e, 5) + f2(a, b, c) + words[offset + 1] + 0xca62c1d6;
+ a = rotate(a, 30);
+ c += rotate(d, 5) + f2(e, a, b) + words[offset + 2] + 0xca62c1d6;
+ e = rotate(e, 30);
+ b += rotate(c, 5) + f2(d, e, a) + words[offset + 3] + 0xca62c1d6;
+ d = rotate(d, 30);
+ a += rotate(b, 5) + f2(c, d, e) + words[offset + 4] + 0xca62c1d6;
+ c = rotate(c, 30);
+ }
+
+ // update hash
+ m_hash[0] += a;
+ m_hash[1] += b;
+ m_hash[2] += c;
+ m_hash[3] += d;
+ m_hash[4] += e;
+}
+
+/// add arbitrary number of bytes
+void SHA1::add(const void *data, size_t numBytes) {
+ const uint8_t *current = (const uint8_t *)data;
+
+ if (m_bufferSize > 0) {
+ while (numBytes > 0 && m_bufferSize < BlockSize) {
+ m_buffer[m_bufferSize++] = *current++;
+ numBytes--;
+ }
+ }
+
+ // full buffer
+ if (m_bufferSize == BlockSize) {
+ processBlock((void *)m_buffer);
+ m_numBytes += BlockSize;
+ m_bufferSize = 0;
+ }
+
+ // no more data ?
+ if (numBytes == 0)
+ return;
+
+ // process full blocks
+ while (numBytes >= BlockSize) {
+ processBlock(current);
+ current += BlockSize;
+ m_numBytes += BlockSize;
+ numBytes -= BlockSize;
+ }
+
+ // keep remaining bytes in buffer
+ while (numBytes > 0) {
+ m_buffer[m_bufferSize++] = *current++;
+ numBytes--;
+ }
+}
+
+/// process final block, less than 64 bytes
+void SHA1::processBuffer() {
+ // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte
+
+ // - append "1" bit to message
+ // - append "0" bits until message length in bit mod 512 is 448
+ // - append length as 64 bit integer
+
+ // number of bits
+ size_t paddedLength = m_bufferSize * 8;
+
+ // plus one bit set to 1 (always appended)
+ paddedLength++;
+
+ // number of bits must be (numBits % 512) = 448
+ size_t lower11Bits = paddedLength & 511;
+ if (lower11Bits <= 448)
+ paddedLength += 448 - lower11Bits;
+ else
+ paddedLength += 512 + 448 - lower11Bits;
+ // convert from bits to bytes
+ paddedLength /= 8;
+
+ // only needed if additional data flows over into a second block
+ unsigned char extra[BlockSize];
+
+ // append a "1" bit, 128 => binary 10000000
+ if (m_bufferSize < BlockSize)
+ m_buffer[m_bufferSize] = 128;
+ else
+ extra[0] = 128;
+
+ size_t i;
+ for (i = m_bufferSize + 1; i < BlockSize; i++)
+ m_buffer[i] = 0;
+ for (; i < paddedLength; i++)
+ extra[i - BlockSize] = 0;
+
+ // add message length in bits as 64 bit number
+ uint64_t msgBits = 8 * (m_numBytes + m_bufferSize);
+ // find right position
+ unsigned char *addLength;
+ if (paddedLength < BlockSize)
+ addLength = m_buffer + paddedLength;
+ else
+ addLength = extra + paddedLength - BlockSize;
+
+ // must be big endian
+ *addLength++ = (unsigned char)((msgBits >> 56) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 48) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 40) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 32) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 24) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 16) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 8) & 0xFF);
+ *addLength = (unsigned char)(msgBits & 0xFF);
+
+ // process blocks
+ processBlock(m_buffer);
+ // flowed over into a second block ?
+ if (paddedLength > BlockSize)
+ processBlock(extra);
+}
+
+/// return latest hash as 40 hex characters
+std::string SHA1::getHash() {
+ // compute hash (as raw bytes)
+ unsigned char rawHash[HashBytes];
+ getHash(rawHash);
+
+ // convert to hex string
+ std::string result;
+ result.reserve(2 * HashBytes);
+ for (int i = 0; i < HashBytes; i++) {
+ static const char dec2hex[16 + 1] = "0123456789abcdef";
+ result += dec2hex[(rawHash[i] >> 4) & 15];
+ result += dec2hex[rawHash[i] & 15];
+ }
+
+ return result;
+}
+
+/// return latest hash as bytes
+void SHA1::getHash(unsigned char buffer[SHA1::HashBytes]) {
+ // save old hash if buffer is partially filled
+ uint32_t oldHash[HashValues];
+ for (int i = 0; i < HashValues; i++)
+ oldHash[i] = m_hash[i];
+
+ // process remaining bytes
+ processBuffer();
+
+ unsigned char *current = buffer;
+ for (int i = 0; i < HashValues; i++) {
+ *current++ = (m_hash[i] >> 24) & 0xFF;
+ *current++ = (m_hash[i] >> 16) & 0xFF;
+ *current++ = (m_hash[i] >> 8) & 0xFF;
+ *current++ = m_hash[i] & 0xFF;
+
+ // restore old hash
+ m_hash[i] = oldHash[i];
+ }
+}
+
+/// compute SHA1 of a memory block
+std::string SHA1::operator()(const void *data, size_t numBytes) {
+ reset();
+ add(data, numBytes);
+ return getHash();
+}
+
+/// compute SHA1 of a string, excluding final zero
+std::string SHA1::operator()(const std::string &text) {
+ reset();
+ add(text.c_str(), text.size());
+ return getHash();
+}
diff --git a/external/libdumper/source/sha256.cpp b/external/libdumper/source/sha256.cpp
new file mode 100644
index 0000000..34f4164
--- /dev/null
+++ b/external/libdumper/source/sha256.cpp
@@ -0,0 +1,586 @@
+// //////////////////////////////////////////////////////////
+// sha256.cpp
+// Copyright (c) 2014,2015,2021 Stephan Brumme. All rights reserved.
+// see http://create.stephan-brumme.com/disclaimer.html
+//
+
+#include "sha256.h"
+
+// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN\
+
+//#define SHA2_224_SEED_VECTOR
+
+/// same as reset()
+SHA256::SHA256() {
+ reset();
+}
+
+/// restart
+void SHA256::reset() {
+ m_numBytes = 0;
+ m_bufferSize = 0;
+
+ // according to RFC 1321
+ // "These words were obtained by taking the first thirty-two bits of the
+ // fractional parts of the square roots of the first eight prime numbers"
+ m_hash[0] = 0x6a09e667;
+ m_hash[1] = 0xbb67ae85;
+ m_hash[2] = 0x3c6ef372;
+ m_hash[3] = 0xa54ff53a;
+ m_hash[4] = 0x510e527f;
+ m_hash[5] = 0x9b05688c;
+ m_hash[6] = 0x1f83d9ab;
+ m_hash[7] = 0x5be0cd19;
+
+#ifdef SHA2_224_SEED_VECTOR
+ // if you want SHA2-224 instead then use these seeds
+ // and throw away the last 32 bits of getHash
+ m_hash[0] = 0xc1059ed8;
+ m_hash[1] = 0x367cd507;
+ m_hash[2] = 0x3070dd17;
+ m_hash[3] = 0xf70e5939;
+ m_hash[4] = 0xffc00b31;
+ m_hash[5] = 0x68581511;
+ m_hash[6] = 0x64f98fa7;
+ m_hash[7] = 0xbefa4fa4;
+#endif
+}
+
+namespace {
+inline uint32_t rotate(uint32_t a, uint32_t c) {
+ return (a >> c) | (a << (32 - c));
+}
+
+inline uint32_t swap(uint32_t x) {
+#if defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap32(x);
+#endif
+#ifdef MSC_VER
+ return _byteswap_ulong(x);
+#endif
+
+ return (x >> 24) |
+ ((x >> 8) & 0x0000FF00) |
+ ((x << 8) & 0x00FF0000) |
+ (x << 24);
+}
+
+// mix functions for processBlock()
+inline uint32_t f1(uint32_t e, uint32_t f, uint32_t g) {
+ uint32_t term1 = rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25);
+ uint32_t term2 = (e & f) ^ (~e & g); //(g ^ (e & (f ^ g)))
+ return term1 + term2;
+}
+
+inline uint32_t f2(uint32_t a, uint32_t b, uint32_t c) {
+ uint32_t term1 = rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22);
+ uint32_t term2 = ((a | b) & c) | (a & b); //(a & (b ^ c)) ^ (b & c);
+ return term1 + term2;
+}
+} // namespace
+
+/// process 64 bytes
+void SHA256::processBlock(const void *data) {
+ // get last hash
+ uint32_t a = m_hash[0];
+ uint32_t b = m_hash[1];
+ uint32_t c = m_hash[2];
+ uint32_t d = m_hash[3];
+ uint32_t e = m_hash[4];
+ uint32_t f = m_hash[5];
+ uint32_t g = m_hash[6];
+ uint32_t h = m_hash[7];
+
+ // data represented as 16x 32-bit words
+ const uint32_t *input = (uint32_t *)data;
+ // convert to big endian
+ uint32_t words[64];
+ int i;
+ for (i = 0; i < 16; i++)
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
+ words[i] = input[i];
+#else
+ words[i] = swap(input[i]);
+#endif
+
+ uint32_t x, y; // temporaries
+
+ // first round
+ x = h + f1(e, f, g) + 0x428a2f98 + words[0];
+ y = f2(a, b, c);
+ d += x;
+ h = x + y;
+ x = g + f1(d, e, f) + 0x71374491 + words[1];
+ y = f2(h, a, b);
+ c += x;
+ g = x + y;
+ x = f + f1(c, d, e) + 0xb5c0fbcf + words[2];
+ y = f2(g, h, a);
+ b += x;
+ f = x + y;
+ x = e + f1(b, c, d) + 0xe9b5dba5 + words[3];
+ y = f2(f, g, h);
+ a += x;
+ e = x + y;
+ x = d + f1(a, b, c) + 0x3956c25b + words[4];
+ y = f2(e, f, g);
+ h += x;
+ d = x + y;
+ x = c + f1(h, a, b) + 0x59f111f1 + words[5];
+ y = f2(d, e, f);
+ g += x;
+ c = x + y;
+ x = b + f1(g, h, a) + 0x923f82a4 + words[6];
+ y = f2(c, d, e);
+ f += x;
+ b = x + y;
+ x = a + f1(f, g, h) + 0xab1c5ed5 + words[7];
+ y = f2(b, c, d);
+ e += x;
+ a = x + y;
+
+ // secound round
+ x = h + f1(e, f, g) + 0xd807aa98 + words[8];
+ y = f2(a, b, c);
+ d += x;
+ h = x + y;
+ x = g + f1(d, e, f) + 0x12835b01 + words[9];
+ y = f2(h, a, b);
+ c += x;
+ g = x + y;
+ x = f + f1(c, d, e) + 0x243185be + words[10];
+ y = f2(g, h, a);
+ b += x;
+ f = x + y;
+ x = e + f1(b, c, d) + 0x550c7dc3 + words[11];
+ y = f2(f, g, h);
+ a += x;
+ e = x + y;
+ x = d + f1(a, b, c) + 0x72be5d74 + words[12];
+ y = f2(e, f, g);
+ h += x;
+ d = x + y;
+ x = c + f1(h, a, b) + 0x80deb1fe + words[13];
+ y = f2(d, e, f);
+ g += x;
+ c = x + y;
+ x = b + f1(g, h, a) + 0x9bdc06a7 + words[14];
+ y = f2(c, d, e);
+ f += x;
+ b = x + y;
+ x = a + f1(f, g, h) + 0xc19bf174 + words[15];
+ y = f2(b, c, d);
+ e += x;
+ a = x + y;
+
+ // extend to 24 words
+ for (; i < 24; i++)
+ words[i] = words[i - 16] +
+ (rotate(words[i - 15], 7) ^ rotate(words[i - 15], 18) ^ (words[i - 15] >> 3)) +
+ words[i - 7] +
+ (rotate(words[i - 2], 17) ^ rotate(words[i - 2], 19) ^ (words[i - 2] >> 10));
+
+ // third round
+ x = h + f1(e, f, g) + 0xe49b69c1 + words[16];
+ y = f2(a, b, c);
+ d += x;
+ h = x + y;
+ x = g + f1(d, e, f) + 0xefbe4786 + words[17];
+ y = f2(h, a, b);
+ c += x;
+ g = x + y;
+ x = f + f1(c, d, e) + 0x0fc19dc6 + words[18];
+ y = f2(g, h, a);
+ b += x;
+ f = x + y;
+ x = e + f1(b, c, d) + 0x240ca1cc + words[19];
+ y = f2(f, g, h);
+ a += x;
+ e = x + y;
+ x = d + f1(a, b, c) + 0x2de92c6f + words[20];
+ y = f2(e, f, g);
+ h += x;
+ d = x + y;
+ x = c + f1(h, a, b) + 0x4a7484aa + words[21];
+ y = f2(d, e, f);
+ g += x;
+ c = x + y;
+ x = b + f1(g, h, a) + 0x5cb0a9dc + words[22];
+ y = f2(c, d, e);
+ f += x;
+ b = x + y;
+ x = a + f1(f, g, h) + 0x76f988da + words[23];
+ y = f2(b, c, d);
+ e += x;
+ a = x + y;
+
+ // extend to 32 words
+ for (; i < 32; i++)
+ words[i] = words[i - 16] +
+ (rotate(words[i - 15], 7) ^ rotate(words[i - 15], 18) ^ (words[i - 15] >> 3)) +
+ words[i - 7] +
+ (rotate(words[i - 2], 17) ^ rotate(words[i - 2], 19) ^ (words[i - 2] >> 10));
+
+ // fourth round
+ x = h + f1(e, f, g) + 0x983e5152 + words[24];
+ y = f2(a, b, c);
+ d += x;
+ h = x + y;
+ x = g + f1(d, e, f) + 0xa831c66d + words[25];
+ y = f2(h, a, b);
+ c += x;
+ g = x + y;
+ x = f + f1(c, d, e) + 0xb00327c8 + words[26];
+ y = f2(g, h, a);
+ b += x;
+ f = x + y;
+ x = e + f1(b, c, d) + 0xbf597fc7 + words[27];
+ y = f2(f, g, h);
+ a += x;
+ e = x + y;
+ x = d + f1(a, b, c) + 0xc6e00bf3 + words[28];
+ y = f2(e, f, g);
+ h += x;
+ d = x + y;
+ x = c + f1(h, a, b) + 0xd5a79147 + words[29];
+ y = f2(d, e, f);
+ g += x;
+ c = x + y;
+ x = b + f1(g, h, a) + 0x06ca6351 + words[30];
+ y = f2(c, d, e);
+ f += x;
+ b = x + y;
+ x = a + f1(f, g, h) + 0x14292967 + words[31];
+ y = f2(b, c, d);
+ e += x;
+ a = x + y;
+
+ // extend to 40 words
+ for (; i < 40; i++)
+ words[i] = words[i - 16] +
+ (rotate(words[i - 15], 7) ^ rotate(words[i - 15], 18) ^ (words[i - 15] >> 3)) +
+ words[i - 7] +
+ (rotate(words[i - 2], 17) ^ rotate(words[i - 2], 19) ^ (words[i - 2] >> 10));
+
+ // fifth round
+ x = h + f1(e, f, g) + 0x27b70a85 + words[32];
+ y = f2(a, b, c);
+ d += x;
+ h = x + y;
+ x = g + f1(d, e, f) + 0x2e1b2138 + words[33];
+ y = f2(h, a, b);
+ c += x;
+ g = x + y;
+ x = f + f1(c, d, e) + 0x4d2c6dfc + words[34];
+ y = f2(g, h, a);
+ b += x;
+ f = x + y;
+ x = e + f1(b, c, d) + 0x53380d13 + words[35];
+ y = f2(f, g, h);
+ a += x;
+ e = x + y;
+ x = d + f1(a, b, c) + 0x650a7354 + words[36];
+ y = f2(e, f, g);
+ h += x;
+ d = x + y;
+ x = c + f1(h, a, b) + 0x766a0abb + words[37];
+ y = f2(d, e, f);
+ g += x;
+ c = x + y;
+ x = b + f1(g, h, a) + 0x81c2c92e + words[38];
+ y = f2(c, d, e);
+ f += x;
+ b = x + y;
+ x = a + f1(f, g, h) + 0x92722c85 + words[39];
+ y = f2(b, c, d);
+ e += x;
+ a = x + y;
+
+ // extend to 48 words
+ for (; i < 48; i++)
+ words[i] = words[i - 16] +
+ (rotate(words[i - 15], 7) ^ rotate(words[i - 15], 18) ^ (words[i - 15] >> 3)) +
+ words[i - 7] +
+ (rotate(words[i - 2], 17) ^ rotate(words[i - 2], 19) ^ (words[i - 2] >> 10));
+
+ // sixth round
+ x = h + f1(e, f, g) + 0xa2bfe8a1 + words[40];
+ y = f2(a, b, c);
+ d += x;
+ h = x + y;
+ x = g + f1(d, e, f) + 0xa81a664b + words[41];
+ y = f2(h, a, b);
+ c += x;
+ g = x + y;
+ x = f + f1(c, d, e) + 0xc24b8b70 + words[42];
+ y = f2(g, h, a);
+ b += x;
+ f = x + y;
+ x = e + f1(b, c, d) + 0xc76c51a3 + words[43];
+ y = f2(f, g, h);
+ a += x;
+ e = x + y;
+ x = d + f1(a, b, c) + 0xd192e819 + words[44];
+ y = f2(e, f, g);
+ h += x;
+ d = x + y;
+ x = c + f1(h, a, b) + 0xd6990624 + words[45];
+ y = f2(d, e, f);
+ g += x;
+ c = x + y;
+ x = b + f1(g, h, a) + 0xf40e3585 + words[46];
+ y = f2(c, d, e);
+ f += x;
+ b = x + y;
+ x = a + f1(f, g, h) + 0x106aa070 + words[47];
+ y = f2(b, c, d);
+ e += x;
+ a = x + y;
+
+ // extend to 56 words
+ for (; i < 56; i++)
+ words[i] = words[i - 16] +
+ (rotate(words[i - 15], 7) ^ rotate(words[i - 15], 18) ^ (words[i - 15] >> 3)) +
+ words[i - 7] +
+ (rotate(words[i - 2], 17) ^ rotate(words[i - 2], 19) ^ (words[i - 2] >> 10));
+
+ // seventh round
+ x = h + f1(e, f, g) + 0x19a4c116 + words[48];
+ y = f2(a, b, c);
+ d += x;
+ h = x + y;
+ x = g + f1(d, e, f) + 0x1e376c08 + words[49];
+ y = f2(h, a, b);
+ c += x;
+ g = x + y;
+ x = f + f1(c, d, e) + 0x2748774c + words[50];
+ y = f2(g, h, a);
+ b += x;
+ f = x + y;
+ x = e + f1(b, c, d) + 0x34b0bcb5 + words[51];
+ y = f2(f, g, h);
+ a += x;
+ e = x + y;
+ x = d + f1(a, b, c) + 0x391c0cb3 + words[52];
+ y = f2(e, f, g);
+ h += x;
+ d = x + y;
+ x = c + f1(h, a, b) + 0x4ed8aa4a + words[53];
+ y = f2(d, e, f);
+ g += x;
+ c = x + y;
+ x = b + f1(g, h, a) + 0x5b9cca4f + words[54];
+ y = f2(c, d, e);
+ f += x;
+ b = x + y;
+ x = a + f1(f, g, h) + 0x682e6ff3 + words[55];
+ y = f2(b, c, d);
+ e += x;
+ a = x + y;
+
+ // extend to 64 words
+ for (; i < 64; i++)
+ words[i] = words[i - 16] +
+ (rotate(words[i - 15], 7) ^ rotate(words[i - 15], 18) ^ (words[i - 15] >> 3)) +
+ words[i - 7] +
+ (rotate(words[i - 2], 17) ^ rotate(words[i - 2], 19) ^ (words[i - 2] >> 10));
+
+ // eigth round
+ x = h + f1(e, f, g) + 0x748f82ee + words[56];
+ y = f2(a, b, c);
+ d += x;
+ h = x + y;
+ x = g + f1(d, e, f) + 0x78a5636f + words[57];
+ y = f2(h, a, b);
+ c += x;
+ g = x + y;
+ x = f + f1(c, d, e) + 0x84c87814 + words[58];
+ y = f2(g, h, a);
+ b += x;
+ f = x + y;
+ x = e + f1(b, c, d) + 0x8cc70208 + words[59];
+ y = f2(f, g, h);
+ a += x;
+ e = x + y;
+ x = d + f1(a, b, c) + 0x90befffa + words[60];
+ y = f2(e, f, g);
+ h += x;
+ d = x + y;
+ x = c + f1(h, a, b) + 0xa4506ceb + words[61];
+ y = f2(d, e, f);
+ g += x;
+ c = x + y;
+ x = b + f1(g, h, a) + 0xbef9a3f7 + words[62];
+ y = f2(c, d, e);
+ f += x;
+ b = x + y;
+ x = a + f1(f, g, h) + 0xc67178f2 + words[63];
+ y = f2(b, c, d);
+ e += x;
+ a = x + y;
+
+ // update hash
+ m_hash[0] += a;
+ m_hash[1] += b;
+ m_hash[2] += c;
+ m_hash[3] += d;
+ m_hash[4] += e;
+ m_hash[5] += f;
+ m_hash[6] += g;
+ m_hash[7] += h;
+}
+
+/// add arbitrary number of bytes
+void SHA256::add(const void *data, size_t numBytes) {
+ const uint8_t *current = (const uint8_t *)data;
+
+ if (m_bufferSize > 0) {
+ while (numBytes > 0 && m_bufferSize < BlockSize) {
+ m_buffer[m_bufferSize++] = *current++;
+ numBytes--;
+ }
+ }
+
+ // full buffer
+ if (m_bufferSize == BlockSize) {
+ processBlock(m_buffer);
+ m_numBytes += BlockSize;
+ m_bufferSize = 0;
+ }
+
+ // no more data ?
+ if (numBytes == 0)
+ return;
+
+ // process full blocks
+ while (numBytes >= BlockSize) {
+ processBlock(current);
+ current += BlockSize;
+ m_numBytes += BlockSize;
+ numBytes -= BlockSize;
+ }
+
+ // keep remaining bytes in buffer
+ while (numBytes > 0) {
+ m_buffer[m_bufferSize++] = *current++;
+ numBytes--;
+ }
+}
+
+/// process final block, less than 64 bytes
+void SHA256::processBuffer() {
+ // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte
+
+ // - append "1" bit to message
+ // - append "0" bits until message length in bit mod 512 is 448
+ // - append length as 64 bit integer
+
+ // number of bits
+ size_t paddedLength = m_bufferSize * 8;
+
+ // plus one bit set to 1 (always appended)
+ paddedLength++;
+
+ // number of bits must be (numBits % 512) = 448
+ size_t lower11Bits = paddedLength & 511;
+ if (lower11Bits <= 448)
+ paddedLength += 448 - lower11Bits;
+ else
+ paddedLength += 512 + 448 - lower11Bits;
+ // convert from bits to bytes
+ paddedLength /= 8;
+
+ // only needed if additional data flows over into a second block
+ unsigned char extra[BlockSize];
+
+ // append a "1" bit, 128 => binary 10000000
+ if (m_bufferSize < BlockSize)
+ m_buffer[m_bufferSize] = 128;
+ else
+ extra[0] = 128;
+
+ size_t i;
+ for (i = m_bufferSize + 1; i < BlockSize; i++)
+ m_buffer[i] = 0;
+ for (; i < paddedLength; i++)
+ extra[i - BlockSize] = 0;
+
+ // add message length in bits as 64 bit number
+ uint64_t msgBits = 8 * (m_numBytes + m_bufferSize);
+ // find right position
+ unsigned char *addLength;
+ if (paddedLength < BlockSize)
+ addLength = m_buffer + paddedLength;
+ else
+ addLength = extra + paddedLength - BlockSize;
+
+ // must be big endian
+ *addLength++ = (unsigned char)((msgBits >> 56) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 48) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 40) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 32) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 24) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 16) & 0xFF);
+ *addLength++ = (unsigned char)((msgBits >> 8) & 0xFF);
+ *addLength = (unsigned char)(msgBits & 0xFF);
+
+ // process blocks
+ processBlock(m_buffer);
+ // flowed over into a second block ?
+ if (paddedLength > BlockSize)
+ processBlock(extra);
+}
+
+/// return latest hash as 64 hex characters
+std::string SHA256::getHash() {
+ // compute hash (as raw bytes)
+ unsigned char rawHash[HashBytes];
+ getHash(rawHash);
+
+ // convert to hex string
+ std::string result;
+ result.reserve(2 * HashBytes);
+ for (int i = 0; i < HashBytes; i++) {
+ static const char dec2hex[16 + 1] = "0123456789abcdef";
+ result += dec2hex[(rawHash[i] >> 4) & 15];
+ result += dec2hex[rawHash[i] & 15];
+ }
+
+ return result;
+}
+
+/// return latest hash as bytes
+void SHA256::getHash(unsigned char buffer[SHA256::HashBytes]) {
+ // save old hash if buffer is partially filled
+ uint32_t oldHash[HashValues];
+ for (int i = 0; i < HashValues; i++)
+ oldHash[i] = m_hash[i];
+
+ // process remaining bytes
+ processBuffer();
+
+ unsigned char *current = buffer;
+ for (int i = 0; i < HashValues; i++) {
+ *current++ = (m_hash[i] >> 24) & 0xFF;
+ *current++ = (m_hash[i] >> 16) & 0xFF;
+ *current++ = (m_hash[i] >> 8) & 0xFF;
+ *current++ = m_hash[i] & 0xFF;
+
+ // restore old hash
+ m_hash[i] = oldHash[i];
+ }
+}
+
+/// compute SHA256 of a memory block
+std::string SHA256::operator()(const void *data, size_t numBytes) {
+ reset();
+ add(data, numBytes);
+ return getHash();
+}
+
+/// compute SHA256 of a string, excluding final zero
+std::string SHA256::operator()(const std::string &text) {
+ reset();
+ add(text.c_str(), text.size());
+ return getHash();
+}
diff --git a/external/libfuse/source/helper.c b/external/libfuse/source/helper.c
index a3521b8..1bece04 100644
--- a/external/libfuse/source/helper.c
+++ b/external/libfuse/source/helper.c
@@ -347,13 +347,13 @@ static int fuse_main_common(int argc, char *argv[],
fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint,
&multithreaded, NULL, user_data, compat);
if (fuse == NULL)
- return 1;
+ return -1;
res = fuse_loop_mt(fuse);
fuse_teardown_common(fuse, mountpoint);
if (res == -1)
- return 1;
+ return -1;
return res;
}
diff --git a/external/libfuse/source/mount_bsd.c b/external/libfuse/source/mount_bsd.c
index 41f03f8..0dd28f4 100644
--- a/external/libfuse/source/mount_bsd.c
+++ b/external/libfuse/source/mount_bsd.c
@@ -221,7 +221,7 @@ static int fuse_mount_core(const char *mountpoint, const char *opts, const struc
close(dev_trunk), dev_trunk = -1;
- if(strstr(mountpoint, "/host") != NULL) {
+ if(strcmp(mountpoint, "/host") == 0 ) {
fd = open("/dev/fuse0", O_RDWR);
libfuse_print("Devpath: /dev/fuse0 fd: %d, mountpoint: %s", fd, mountpoint);
if (fd == -1) {
@@ -235,7 +235,7 @@ static int fuse_mount_core(const char *mountpoint, const char *opts, const struc
return -1;
}
}
- else if(strstr(mountpoint, "/hostapp") != NULL) {
+ else if(strcmp(mountpoint, "/hostapp") == 0) {
fd = open("/dev/fuse1", O_RDWR);
libfuse_print("Devpath: /dev/fuse1 fd: %d, mountpoint: %s", fd, mountpoint);
if (fd == -1) {
diff --git a/itemz-daemon/Makefile b/itemz-daemon/Makefile
index 3d42ded..db7c6d7 100644
--- a/itemz-daemon/Makefile
+++ b/itemz-daemon/Makefile
@@ -1,3 +1,4 @@
+
ifndef ORBISDEV
$(error ORBISDEV, is not set)
endif
@@ -7,10 +8,11 @@ TargetFile=daemon.elf
include $(ORBISDEV)/make/ps4sdk.mk
-LinkerFlags += -lfuse -lkernel_stub -lSceLibcInternal_stub -lSceSysmodule_stub -lSceSystemService_stub -lSceNet_stub -lSceUserService_stub -lScePigletv2VSH_stub -lSceVideoOut_stub -lSceImeDialog_stub -lSceGnmDriver_stub -lorbis -lorbisGl -lScePad_stub -lSceCommonDialog_stub -lSceMsgDialog_stub -lSceAudioOut_stub -lSceIme_stub -lSceAppInstUtil_stub -lSceBgft_stub -lSceSysUtil_stub -lSceHttp_stub -lSceSsl_stub -lSceNetCtl_stub -lfreetype-gl -lSceFreeTypeOl_stub -lkernel_sys_stub -lSceLncUtil_stub -lSQLite -lSceSysCore_stub
+LinkerFlags += -lfuse -lkernel_stub -lc++ -lc++abi -lc++experimental -lSceImeDialog_stub -lSceGnmDriver_stub -lorbis -lorbisGl -lScePad_stub -lSceCommonDialog_stub -lSceMsgDialog_stub -lSceAudioOut_stub -lSceIme_stub -lSceAppInstUtil_stub -lSceBgft_stub -lSceSysUtil_stub -lSceHttp_stub -lSceSsl_stub -lSceNetCtl_stub -lfreetype-gl -lSceFreeTypeOl_stub -lkernel_sys_stub -lSceLncUtil_stub -lSQLite -lSceSysCore_stub -lSceLncUtil_stub -lSceNetCtl_stub -lSceLibcInternal_stub -lSceSysmodule_stub -lSceSystemService_stub -lSceNet_stub -lSceUserService_stub -lSceAppInstUtil_stub -lSceBgft_stub -lSceSysUtil_stub -lSceLncUtil_stub -lSQLite
CompilerFlags += -Wno-implicit-function-declaration -Wno-int-to-pointer-cast -D_FILE_OFFSET_BITS=64 -D__Freebsd__ -D__PS4__ -D__ORBIS__ -fstack-protector-all
-
+CompilerFlagsCpp += -fstack-protector-all -Wall -Werror -Wno-excess-initializers -DFMT_HEADER_ONLY -Wno-reorder-init-list # -DHAVE_SHADER_COMPILER
+CompilerFlagsCpp += -std=c++17 -D_FILE_OFFSET_BITS=64 -Wno-c++11-narrowing -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -D__ORBIS__=1 -D_ORBIS_ -DNDEBUG -D_BSD_SOURCE -fno-exceptions -frtti
IncludePath += -I$(ORBISDEV)/usr/include -I$(ORBISDEV)/usr/include/c++/v1 -I$(ORBISDEV)/usr/include/orbis
IncludePath += -I$(ORBISDEV)/usr/include/orbis/freetype-gl
diff --git a/itemz-daemon/Makefile.save b/itemz-daemon/Makefile.save
new file mode 100644
index 0000000..bd33d97
--- /dev/null
+++ b/itemz-daemon/Makefile.save
@@ -0,0 +1,28 @@
+ifndef ORBISDEV
+$(error ORBISDEV, is not set)
+endif
+
+target ?= ps4_elf_sce
+TargetFile=daemon.elf
+
+include $(ORBISDEV)/make/ps4sdk.mk
+
+LinkerFlags += -lfuse -lkernel_stub -lc++ -lc++abi -lc++experimental -luser_mem_sys -lSceNetCtl_stub -lSceLibcInternal_stub -lSceSysmodule_stub -lSceSystemService_stub -lSceNet_stub -lSceUserService_stub -lorbis -lSceAppInstUtil_stub -lSceLncUtil_stub-lSceBgft_stub -lSceSysUtil_stub -lSceLncUtil_stub -lSQLite
+
+CompilerFlags += -Wno-implicit-function-declaration -Wno-int-to-pointer-cast -D_FILE_OFFSET_BITS=64 -D__Freebsd__ -D__PS4__ -D__ORBIS__ -fstack-protector-all
+CompilerFlagsCpp += -fstack-protector-all -Wall -Werror -Wno-excess-initializers -DFMT_HEADER_ONLY -Wno-reorder-init-list # -DHAVE_SHADER_COMPILER
+CompilerFlagsCpp += -std=c++17 -D_FILE_OFFSET_BITS=64 -Wno-c++11-narrowing -DHAVE_CONFIG_H -D_U_="__attribute__((unused))" -D__ORBIS__=1 -D_ORBIS_ -DNDEBUG -D_BSD_SOURCE -fno-exceptions -frtti
+IncludePath += -I$(ORBISDEV)/usr/include -I$(ORBISDEV)/usr/include/c++/v1 -I$(ORBISDEV)/usr/include/orbis
+IncludePath += -I$(ORBISDEV)/usr/include/orbis/freetype-gl
+
+
+oelf:
+ orbis-elf-create bin/daemon.elf bin/daemon.oelf >/dev/null 2>&1
+
+
+AUTH_INFO = 000000000000000000000000001C004000FF000000000080000000000000000000000000000000000000008000400040000000000000008000000000000000080040FFFF000000F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+eboot:
+ python2 $(ORBISDEV)/bin/make_fself.py --auth-info $(AUTH_INFO) bin/daemon.oelf bin/eboot.bin
+pkg_build:
+ cp bin/eboot.bin pkg/eboot.bin && cd pkg && pkgTool pkg_build Project.gp4 . && cp *.pkg ../bin/
diff --git a/itemz-daemon/include/defines.h b/itemz-daemon/include/defines.h
index cd68452..179900c 100644
--- a/itemz-daemon/include/defines.h
+++ b/itemz-daemon/include/defines.h
@@ -19,9 +19,7 @@
#include
#include
#include
-#include "net.h"
#include
-#include
#include
#include
#include
@@ -179,4 +177,35 @@
(VERSION_MINOR + '0')
#endif
-#define ULPMGR_THREAD_PRIO_BASE (903)
\ No newline at end of file
+#define ULPMGR_THREAD_PRIO_BASE (903)
+
+enum IPC_Errors
+{
+ INVALID = -1,
+ NO_ERROR = 0,
+ OPERATION_FAILED = 1,
+ INVALID_ARGUMENT = 2,
+ FUSE_IP_ALREADY_SET = 3,
+ FUSE_IP_NOT_SET = 4,
+ FUSE_FW_NOT_SUPPORTED = 5,
+};
+
+enum cmd
+{
+
+ MACGUFFIN_CMD = 69,
+ SHUTDOWN_DAEMON = 1337,
+ CONNECTION_TEST = 1,
+ ENABLE_HOME_REDIRECT = 2,
+ DISABLE_HOME_REDIRECT = 3,
+ GAME_GET_MINS = 4,
+ GAME_GET_START_TIME = 5,
+ CRITICAL_SUSPEND = 6,
+ INSTALL_IF_UPDATE = 7,
+ RESTART_FTP_SERVICE = 8,
+ FUSE_SET_SESSION_IP = 9,
+ FUSE_SET_DEBUG_FLAG = 10,
+ FUSE_START_W_PATH = 11,
+ RESTART_FUSE_FS = 12,
+ DEAMON_UPDATE = 100,
+};
diff --git a/itemz-daemon/include/jsmn.h b/itemz-daemon/include/jsmn.h
index a2a0b6c..19431cb 100644
--- a/itemz-daemon/include/jsmn.h
+++ b/itemz-daemon/include/jsmn.h
@@ -36,6 +36,9 @@ extern "C" {
#define JSMN_API extern
#endif
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+
/**
* JSON type identifier. Basic types are:
* o Object
diff --git a/itemz-daemon/include/log.h b/itemz-daemon/include/log.h
index 99d9e52..6422cc0 100644
--- a/itemz-daemon/include/log.h
+++ b/itemz-daemon/include/log.h
@@ -13,8 +13,11 @@
#include
#include
-#define LOG_VERSION "0.1.0"
+#define LOG_VERSION "0.1.0"
+#ifdef __cplusplus
+extern "C" {
+#endif
typedef struct {
va_list ap;
const char* fmt;
@@ -45,5 +48,7 @@ int log_add_callback(log_LogFn fn, void* udata, int level);
int log_add_fp(FILE* fp, int level);
void log_log(int level, const char* file, int line, const char* fmt, ...);
-
+#ifdef __cplusplus
+}
+#endif
#endif
\ No newline at end of file
diff --git a/itemz-daemon/include/net.hpp b/itemz-daemon/include/net.hpp
new file mode 100644
index 0000000..050e4d8
--- /dev/null
+++ b/itemz-daemon/include/net.hpp
@@ -0,0 +1,448 @@
+#ifndef _SCE_SSL_H_
+#define _SCE_SSL_H_
+
+#pragma once
+#include
+#include "defines.h"
+#include
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ORBIS_NET_CTL_INFO_DEVICE 1
+#define ORBIS_NET_CTL_INFO_ETHER_ADDR 2
+#define ORBIS_NET_CTL_INFO_MTU 3
+#define ORBIS_NET_CTL_INFO_LINK 4
+#define ORBIS_NET_CTL_INFO_BSSID 5
+#define ORBIS_NET_CTL_INFO_SSID 6
+#define ORBIS_NET_CTL_INFO_WIFI_SECURITY 7
+#define ORBIS_NET_CTL_INFO_RSSI_DBM 8
+#define ORBIS_NET_CTL_INFO_RSSI_PERCENTAGE 9
+#define ORBIS_NET_CTL_INFO_CHANNEL 10
+#define ORBIS_NET_CTL_INFO_IP_CONFIG 11
+#define ORBIS_NET_CTL_INFO_DHCP_HOSTNAME 12
+#define ORBIS_NET_CTL_INFO_PPPOE_AUTH_NAME 13
+#define ORBIS_NET_CTL_INFO_IP_ADDRESS 14
+#define ORBIS_NET_CTL_INFO_NETMASK 15
+#define ORBIS_NET_CTL_INFO_DEFAULT_ROUTE 16
+#define ORBIS_NET_CTL_INFO_PRIMARY_DNS 17
+#define ORBIS_NET_CTL_INFO_SECONDARY_DNS 18
+#define ORBIS_NET_CTL_INFO_HTTP_PROXY_CONFIG 19
+#define ORBIS_NET_CTL_INFO_HTTP_PROXY_SERVER 20
+#define ORBIS_NET_CTL_INFO_HTTP_PROXY_PORT 21
+#define ORBIS_NET_CTL_INFO_RESERVED1 22
+#define ORBIS_NET_CTL_INFO_RESERVED2 23
+
+typedef union OrbisNetCtlInfo {
+ uint32_t device;
+ char ether_addr[6];
+ uint32_t mtu;
+ uint32_t link;
+ char bssid[6];
+ char ssid[33];
+ uint32_t wifi_security;
+ uint8_t rssi_dbm;
+ uint8_t rssi_percentage;
+ uint8_t channel;
+ uint32_t ip_config;
+ char dhcp_hostname[256];
+ char pppoe_auth_name[128];
+ char ip_address[16];
+ char netmask[16];
+ char default_route[16];
+ char primary_dns[16];
+ char secondary_dns[16];
+ uint32_t http_proxy_config;
+ char http_proxy_server[256];
+ uint16_t http_proxy_port;
+} OrbisNetCtlInfo;
+
+int sceNetCtlGetInfo(int, void *);
+int sceNetCtlInit();
+
+typedef void* OrbisHttpEpollHandle;
+
+#define ORBIS_HTTP_NB_EVENT_IN 0x00000001
+#define ORBIS_HTTP_NB_EVENT_OUT 0x00000002
+#define ORBIS_HTTP_NB_EVENT_SOCK_ERR 0x00000008
+#define ORBIS_HTTP_NB_EVENT_HUP 0x00000010
+#define ORBIS_HTTP_NB_EVENT_ICM 0x00000020
+#define ORBIS_HTTP_NB_EVENT_RESOLVED 0x00010000
+#define ORBIS_HTTP_NB_EVENT_RESOLVER_ERR 0x00020000
+
+typedef struct OrbisHttpNBEvent {
+ uint32_t events;
+ uint32_t eventDetail;
+ int id;
+ void* userArg;
+} OrbisHttpNBEvent;
+
+
+#define ORBIS_HTTP_DEFAULT_RESOLVER_TIMEOUT (0) // libnet default
+#define ORBIS_HTTP_DEFAULT_RESOLVER_RETRY (0) // libnet default
+#define ORBIS_HTTP_DEFAULT_CONNECT_TIMEOUT (30* 1000 * 1000U)
+#define ORBIS_HTTP_DEFAULT_SEND_TIMEOUT (120* 1000 * 1000U)
+#define ORBIS_HTTP_DEFAULT_RECV_TIMEOUT (120* 1000 * 1000U)
+#define ORBIS_HTTP_DEFAULT_RECV_BLOCK_SIZE (1500U)
+#define ORBIS_HTTP_DEFAULT_RESPONSE_HEADER_MAX (5000U)
+#define ORBIS_HTTP_DEFAULT_REDIRECT_MAX (6U)
+#define ORBIS_HTTP_DEFAULT_TRY_AUTH_MAX (6U)
+
+#define ORBIS_HTTP_TRUE (int)(1)
+#define ORBIS_HTTP_FALSE (int)(0)
+
+#define ORBIS_HTTP_ENABLE ORBIS_HTTP_TRUE
+#define ORBIS_HTTP_DISABLE ORBIS_HTTP_FALSE
+
+#define ORBIS_HTTP_USERNAME_MAX_SIZE 256
+#define ORBIS_HTTP_PASSWORD_MAX_SIZE 256
+
+
+#define ORBIS_HTTP_TRUE (int)(1)
+#define ORBIS_HTTP_FALSE (int)(0)
+
+#define ORBIS_HTTP_ENABLE ORBIS_HTTP_TRUE
+#define ORBIS_HTTP_DISABLE ORBIS_HTTP_FALSE
+
+#define ORBIS_HTTP_USERNAME_MAX_SIZE 256
+#define ORBIS_HTTP_PASSWORD_MAX_SIZE 256
+
+
+typedef enum{
+ ORBIS_HTTP_VERSION_1_0=1,
+ ORBIS_HTTP_VERSION_1_1
+} OrbisHttpHttpVersion;
+
+typedef enum{
+ ORBIS_HTTP_PROXY_AUTO,
+ ORBIS_HTTP_PROXY_MANUAL
+} OrbisHttpProxyMode;
+
+typedef enum{
+ ORBIS_HTTP_HEADER_OVERWRITE,
+ ORBIS_HTTP_HEADER_ADD
+} OrbisHttpAddHeaderMode;
+
+typedef enum{
+ ORBIS_HTTP_AUTH_BASIC,
+ ORBIS_HTTP_AUTH_DIGEST,
+ ORBIS_HTTP_AUTH_RESERVED0,
+ ORBIS_HTTP_AUTH_RESERVED1,
+ ORBIS_HTTP_AUTH_RESERVED2
+} OrbisHttpAuthType;
+
+typedef enum {
+ ORBIS_HTTP_CONTENTLEN_EXIST,
+ ORBIS_HTTP_CONTENTLEN_NOT_FOUND,
+ ORBIS_HTTP_CONTENTLEN_CHUNK_ENC
+} OrbisHttpContentLengthType;
+
+typedef enum {
+ ORBIS_HTTP_REQUEST_STATUS_CONNECTION_RESERVED,
+ ORBIS_HTTP_REQUEST_STATUS_RESOLVING_NAME,
+ ORBIS_HTTP_REQUEST_STATUS_NAME_RESOLVED,
+ ORBIS_HTTP_REQUEST_STATUS_CONNECTING,
+ ORBIS_HTTP_REQUEST_STATUS_CONNECTED,
+ ORBIS_HTTP_REQUEST_STATUS_TLS_CONNECTING,
+ ORBIS_HTTP_REQUEST_STATUS_TLS_CONNECTED,
+ ORBIS_HTTP_REQUEST_STATUS_SENDING_REQUEST,
+ ORBIS_HTTP_REQUEST_STATUS_REQUEST_SENT,
+ ORBIS_HTTP_REQUEST_STATUS_RECEIVING_HEADER,
+ ORBIS_HTTP_REQUEST_STATUS_HEADER_RECEIVED,
+} OrbisHttpRequestStatus;
+
+
+#define ORBIS_HTTP_INVALID_ID 0
+
+
+typedef int (*OrbisHttpAuthInfoCallback)(
+ int request,
+ OrbisHttpAuthType authType,
+ const char *realm,
+ char *username,
+ char *password,
+ int isNeedEntity,
+ uint8_t **entityBody,
+ size_t *entitySize,
+ int *isSave,
+ void *userArg);
+
+typedef int (*OrbisHttpRedirectCallback)(
+ int request,
+ int32_t statusCode,
+ int32_t *method,
+ const char *location,
+ void *userArg);
+
+typedef void (*OrbisHttpRequestStatusCallback)(
+ int request,
+ OrbisHttpRequestStatus requestStatus,
+ void *userArg);
+
+typedef struct OrbisHttpMemoryPoolStats{
+ size_t poolSize;
+ size_t maxInuseSize;
+ size_t currentInuseSize;
+ int32_t reserved;
+} OrbisHttpMemoryPoolStats;
+
+
+
+// Empty Comment
+int sceHttpAbortRequest(int reqId);
+// Empty Comment
+void sceHttpAbortRequestForce();
+// Empty Comment
+int sceHttpAbortWaitRequest(OrbisHttpEpollHandle eh);
+// Empty Comment
+void sceHttpAddCookie();
+// Empty Comment
+int sceHttpAddRequestHeader(int id, const char *name, const char *value, uint32_t mode);
+// Empty Comment
+void sceHttpAddRequestHeaderRaw();
+// Empty Comment
+void sceHttpAuthCacheExport();
+// Empty Comment
+int sceHttpAuthCacheFlush(int libhttpCtxId);
+// Empty Comment
+void sceHttpAuthCacheImport();
+// Empty Comment
+void sceHttpCookieExport();
+// Empty Comment
+void sceHttpCookieFlush();
+// Empty Comment
+void sceHttpCookieImport();
+// Empty Comment
+int sceHttpCreateConnection(int tmplId, const char *serverName, const char *scheme, uint16_t port, int isEnableKeepalive);
+// Empty Comment
+int sceHttpCreateConnectionWithURL(int tmplId, const char *url, int isEnableKeepalive);
+// Empty Comment
+int sceHttpCreateEpoll(int libhttpCtxId, OrbisHttpEpollHandle* eh);
+// Empty Comment
+int sceHttpCreateRequest(int connId, int method, const char *path, uint64_t contentLength);
+// Empty Comment
+int sceHttpCreateRequest2(int connId, const char* method, const char *path, uint64_t contentLength);
+// Empty Comment
+int sceHttpCreateRequestWithURL(int connId, int method, const char *url, uint64_t contentLength);
+// Empty Comment
+int sceHttpCreateRequestWithURL2(int connId, const char* method, const char *url, uint64_t contentLength);
+// Empty Comment
+int sceHttpCreateTemplate(int libhttpCtxId, const char *userAgent, int httpVer, int isAutoProxyConf);
+// Empty Comment
+void sceHttpDbgGetConnectionStat();
+// Empty Comment
+void sceHttpDbgGetRequestStat();
+// Empty Comment
+void sceHttpDbgSetPrintf();
+// Empty Comment
+void sceHttpDbgShowConnectionStat();
+// Empty Comment
+void sceHttpDbgShowMemoryPoolStat();
+// Empty Comment
+void sceHttpDbgShowRequestStat();
+// Empty Comment
+void sceHttpDbgShowStat();
+// Empty Comment
+int sceHttpDeleteConnection(int connId);
+// Empty Comment
+int sceHttpDeleteRequest(int reqId);
+// Empty Comment
+int sceHttpDeleteTemplate(int tmplId);
+// Empty Comment
+int sceHttpDestroyEpoll(int libhttpCtxId, OrbisHttpEpollHandle eh);
+// Empty Comment
+void sceHttpGetAcceptEncodingGZIPEnabled();
+// Empty Comment
+int sceHttpGetAllResponseHeaders(int reqId, char **header, size_t *headerSize);
+// Empty Comment
+int sceHttpGetAuthEnabled(int id, int *isEnable);
+// Empty Comment
+int sceHttpGetAutoRedirect(int id, int *isEnable);
+// Empty Comment
+void sceHttpGetCookie();
+// Empty Comment
+void sceHttpGetCookieEnabled();
+// Empty Comment
+void sceHttpGetCookieStats();
+// Empty Comment
+int sceHttpGetEpoll(int id, OrbisHttpEpollHandle* eh, void **userArg);
+// Empty Comment
+void sceHttpGetEpollId();
+// Empty Comment
+int sceHttpGetLastErrno(int reqId, int* errNum);
+// Empty Comment
+int sceHttpGetMemoryPoolStats(int libhttpCtxId, OrbisHttpMemoryPoolStats* currentStat);
+// Empty Comment
+int sceHttpGetNonblock(int id, int *isEnable);
+// Empty Comment
+int sceHttpGetResponseContentLength(int reqId, int* result, uint64_t *contentLength);
+// Empty Comment
+int sceHttpGetStatusCode(int reqId, int *statusCode);
+// Empty Comment
+int sceHttpInit(int libnetMemId, int libsslCtxId, size_t poolSize);
+// Empty Comment
+int sceHttpParseResponseHeader(const char *header, size_t headerLen, const char *fieldStr, const char **fieldValue, size_t *valueLen);
+// Empty Comment
+int sceHttpParseStatusLine(const char *statusLine, size_t lineLen, int *httpMajorVer, int *httpMinorVer, int *responseCode, const char **reasonPhras);
+// Empty Comment
+int sceHttpReadData(int reqId, void *data, size_t size);
+// Empty Comment
+int sceHttpRedirectCacheFlush(int libhttpCtxId);
+// Empty Comment
+int sceHttpRemoveRequestHeader(int id, const char *name);
+// Empty Comment
+void sceHttpRequestGetAllHeaders();
+// Empty Comment
+void sceHttpsDisableOption();
+// Empty Comment
+void sceHttpsDisableOptionPrivate();
+// Empty Comment
+void sceHttpsEnableOption();
+// Empty Comment
+void sceHttpsEnableOptionPrivate();
+// Empty Comment
+int sceHttpSendRequest(int reqId, const void *postData, size_t size);
+// Empty Comment
+void sceHttpSetAcceptEncodingGZIPEnabled();
+// Empty Comment
+int sceHttpSetAuthEnabled(int id, int isEnable);
+// Empty Comment
+int sceHttpSetAuthInfoCallback(int id, OrbisHttpAuthInfoCallback cbfunc, void *userArg);
+// Empty Comment
+int sceHttpSetAutoRedirect(int id, int isEnable);
+// Empty Comment
+int sceHttpSetChunkedTransferEnabled(int id, int isEnable);
+// Empty Comment
+int sceHttpSetConnectTimeOut(int id, uint32_t usec);
+// Empty Comment
+void sceHttpSetCookieEnabled();
+// Empty Comment
+void sceHttpSetCookieMaxNum();
+// Empty Comment
+void sceHttpSetCookieMaxNumPerDomain();
+// Empty Comment
+void sceHttpSetCookieMaxSize();
+// Empty Comment
+void sceHttpSetCookieRecvCallback();
+// Empty Comment
+void sceHttpSetCookieSendCallback();
+// Empty Comment
+void sceHttpSetCookieTotalMaxSize();
+// Empty Comment
+void sceHttpSetDefaultAcceptEncodingGZIPEnabled();
+// Empty Comment
+void sceHttpSetEpoll();
+// Empty Comment
+void sceHttpSetEpollId();
+// Empty Comment
+int sceHttpSetInflateGZIPEnabled(int id, int isEnable);
+// Empty Comment
+int sceHttpSetNonblock(int id, int isEnable);
+// Empty Comment
+void sceHttpSetPolicyOption();
+// Empty Comment
+void sceHttpSetPriorityOption();
+// Empty Comment
+void sceHttpSetProxy();
+// Empty Comment
+void sceHttpSetRecvBlockSize();
+// Empty Comment
+int sceHttpSetRecvTimeOut(int id, uint32_t usec);
+// Empty Comment
+int sceHttpSetRedirectCallback(int id, OrbisHttpRedirectCallback cbfunc, void *userArg);
+// Empty Comment
+int sceHttpSetRequestContentLength(int id, uint64_t contentLength);
+// Empty Comment
+int sceHttpSetResolveRetry(int id, int retry);
+// Empty Comment
+int sceHttpSetResolveTimeOut(int id, uint32_t usec);
+// Empty Comment
+int sceHttpSetResponseHeaderMaxSize(int id, size_t headerSize);
+// Empty Comment
+int sceHttpSetSendTimeOut(int id, uint32_t usec);
+// Empty Comment
+void sceHttpsFreeCaList();
+// Empty Comment
+void sceHttpsGetCaList();
+// Empty Comment
+void sceHttpsGetSslError();
+// Empty Comment
+void sceHttpsLoadCert();
+typedef int (*HttpsCallback)(int libsslCtxId, unsigned int verifyErr, void* const sslCert[], int certNum, void* userArg);
+int sceHttpsSetSslCallback(int id, HttpsCallback cbfunc, void* userArg);
+// Empty Comment
+void sceHttpsSetSslVersion();
+// Empty Comment
+void sceHttpsUnloadCert();
+// Empty Comment
+int sceHttpTerm(int libhttpCtxId);
+// Empty Comment
+int sceHttpTryGetNonblock(int id, int *isEnable);
+// Empty Comment
+int sceHttpTrySetNonblock(int id, int isEnable);
+// Empty Comment
+int sceHttpUnsetEpoll(int id);
+// Empty Comment
+void sceHttpUriBuild();
+// Empty Comment
+void sceHttpUriCopy();
+// Empty Comment
+void sceHttpUriEscape();
+// Empty Comment
+void sceHttpUriMerge();
+// Empty Comment
+void sceHttpUriParse();
+// Empty Comment
+void sceHttpUriSweepPath();
+// Empty Comment
+void sceHttpUriUnescape();
+// Empty Comment
+int sceHttpWaitRequest(OrbisHttpEpollHandle eh, OrbisHttpNBEvent* nbev, int maxevents, int timeout);
+
+
+void sceNetCtlTerm();
+
+int sceNetPoolCreate(
+ const char *name,
+ int size,
+ int flags
+);
+int sceNetInit();
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+#ifdef __cplusplus
+#include
+#include
+struct clientArgs {
+ std::string ip;
+ int socket;
+ int cl_nmb;
+
+};
+#define DAEMON_BUFF_MAX 0x100
+
+struct IPCMessage{
+ int magic = 0xDEADBABE;
+ cmd cmd;
+ IPC_Errors error = NO_ERROR;
+ char msg[DAEMON_BUFF_MAX] = {0};
+};
+void* ipc_client(void* args);
+extern std::string fuse_session_ip;
+struct sockaddr_in networkAdress(uint16_t port);
+int networkListen(const char* soc_path);
+int networkAccept(int socket);
+int networkReceiveData(int socket, void* buffer, int32_t size);
+int networkSendData(int socket, void* buffer, int32_t size);
+int networkSendDebugData(uint8_t* buffer, int32_t size);
+int networkCloseConnection(int socket);
+int networkCloseDebugConnection();
+
+#endif
\ No newline at end of file
diff --git a/itemz-daemon/include/utils.hpp b/itemz-daemon/include/utils.hpp
new file mode 100644
index 0000000..a9dcf8a
--- /dev/null
+++ b/itemz-daemon/include/utils.hpp
@@ -0,0 +1,370 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include "defines.h"
+#include "log.h"
+
+#define SYS_dynlib_get_info 593
+#define SYS_dynlib_get_info_ex 608
+
+#define SYS_PROC_ALLOC 1
+#define SYS_PROC_FREE 2
+#define SYS_PROC_PROTECT 3
+#define SYS_PROC_VM_MAP 4
+#define SYS_PROC_INSTALL 5
+#define SYS_PROC_CALL 6
+#define SYS_PROC_ELF 7
+#define SYS_PROC_INFO 8
+#define SYS_PROC_THRINFO 9
+
+#define SYS_CONSOLE_CMD_REBOOT 1
+#define SYS_CONSOLE_CMD_PRINT 2
+#define SYS_CONSOLE_CMD_JAILBREAK 3
+#define SYS_CONSOLE_CMD_ISLOADED 6
+
+#define DIM(x) (sizeof(x)/sizeof(*(x)))
+#define KB(x) ((size_t) (x) << 10)
+#define MB(x) ((size_t) (x) << 20)
+
+#define STAT_DB "/data/itemzflow_daemon/stats.db"
+#define DAEMON_LOG_PS4 "/data/itemzflow_daemon/daemon.log"
+#define DAEMON_LOG_USB "/mnt/usb0/itemzflow/daemon.log"
+bool touch_file(const char* destfile);
+
+#define MAX_MESSAGE_SIZE 0x1000
+#define MAX_STACK_FRAMES 60
+
+typedef struct {
+ char* sp;
+ char* pc;
+} callframe_t;
+
+
+typedef struct {
+ char* unk01;
+ char* unk02;
+ off_t offset;
+ int unk04;
+ int unk05;
+ unsigned isFlexibleMemory : 1;
+ unsigned isDirectMemory : 1;
+ unsigned isStack : 1;
+ unsigned isPooledMemory : 1;
+ unsigned isCommitted : 1;
+ char name[32];
+} OrbisKernelVirtualQueryInfo;
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+
+enum Flag
+{
+ Flag_None = 0,
+ SkipLaunchCheck = 1,
+ SkipResumeCheck = 1,
+ SkipSystemUpdateCheck = 2,
+ RebootPatchInstall = 4,
+ VRMode = 8,
+ NonVRMode = 16
+};
+
+typedef struct _LncAppParam
+{
+ uint32_t sz;
+ uint32_t user_id;
+ uint32_t app_opt;
+ uint64_t crash_report;
+ enum Flag check_flag;
+}
+LncAppParam;
+
+typedef struct OrbisUserServiceLoginUserIdList {
+ int32_t userId[4];
+} OrbisUserServiceLoginUserIdList;
+
+typedef struct OrbisUserServiceInitializeParams {
+ int32_t priority;
+} OrbisUserServiceInitializeParams;
+
+bool rejail();
+bool jailbreak(const char* prx_path);
+bool full_init();
+bool isRestMode();
+bool IsOn();
+void notify(const char* message);
+void save_fuse_ip(const char* ip);
+extern int DaemonSocket;
+
+#define networkSendMessage(socket, format, ...)\
+do {\
+ char msgBuffer[512];\
+ int msgSize = sprintf(msgBuffer, format, ##__VA_ARGS__);\
+ sceNetSend(socket, msgBuffer, msgSize, 0);\
+} while(0)
+
+void *IPC_loop(void* args);
+bool check_update_from_url(const char* url);
+bool create_stat_db(const char* path);
+bool open_stat_db(const char* path);
+bool update_game_time_played(const char* tid);
+int get_game_time_played(const char* tid);
+time_t get_game_start_date(const char* tid);
+void DumpHex(const void* data, size_t size);
+uint32_t pkginstall(const char *path, bool is_if_update);
+struct fuse_actions {
+ int argc;
+ char argv[0x30];
+ char path[0x60];
+ char ip[0x100];
+ bool is_debug_mode;
+};
+extern bool reboot_daemon;
+int initialize_userland_fuse(const char* mp, const char* ip);
+
+#define SWAP32(x) \
+ ((uint32_t)( \
+ (((uint32_t)(x) & UINT32_C(0x000000FF)) << 24) | \
+ (((uint32_t)(x) & UINT32_C(0x0000FF00)) << 8) | \
+ (((uint32_t)(x) & UINT32_C(0x00FF0000)) >> 8) | \
+ (((uint32_t)(x) & UINT32_C(0xFF000000)) >> 24) \
+ ))
+
+#define BE32(x) SWAP32(x)
+
+struct _SceBgftDownloadRegisterErrorInfo {
+ /* TODO */
+ uint8_t buf[0x100];
+};
+
+enum bgft_task_option_t {
+ BGFT_TASK_OPTION_NONE = 0x0,
+ BGFT_TASK_OPTION_DELETE_AFTER_UPLOAD = 0x1,
+ BGFT_TASK_OPTION_INVISIBLE = 0x2,
+ BGFT_TASK_OPTION_ENABLE_PLAYGO = 0x4,
+ BGFT_TASK_OPTION_FORCE_UPDATE = 0x8,
+ BGFT_TASK_OPTION_REMOTE = 0x10,
+ BGFT_TASK_OPTION_COPY_CRASH_REPORT_FILES = 0x20,
+ BGFT_TASK_OPTION_DISABLE_INSERT_POPUP = 0x40,
+ BGFT_TASK_OPTION_DISABLE_CDN_QUERY_PARAM = 0x10000,
+};
+
+struct bgft_download_param {
+ int user_id;
+ int entitlement_type;
+ const char* id;
+ const char* content_url;
+ const char* content_ex_url;
+ const char* content_name;
+ const char* icon_path;
+ const char* sku_id;
+ enum bgft_task_option_t option;
+ const char* playgo_scenario_id;
+ const char* release_date;
+ const char* package_type;
+ const char* package_sub_type;
+ unsigned long package_size;
+};
+
+
+
+struct bgft_download_param_ex {
+ struct bgft_download_param param;
+ unsigned int slot;
+};
+
+
+
+struct bgft_task_progress_internal {
+ unsigned int bits;
+ int error_result;
+ unsigned long length;
+ unsigned long transferred;
+ unsigned long length_total;
+ unsigned long transferred_total;
+ unsigned int num_index;
+ unsigned int num_total;
+ unsigned int rest_sec;
+ unsigned int rest_sec_total;
+ int preparing_percent;
+ int local_copy_percent;
+};
+
+
+struct bgft_init_params {
+ void *heap;
+ size_t heapSize;
+};
+
+struct bgft_download_task_progress_info {
+ unsigned int bits;
+ int error_result;
+ unsigned long length;
+ unsigned long transferred;
+ unsigned long length_total;
+ unsigned long transferred_total;
+ unsigned int num_index;
+ unsigned int num_total;
+ unsigned int rest_sec;
+ unsigned int rest_sec_total;
+ int preparing_percent;
+ int local_copy_percent;
+};
+
+struct _SceBgftTaskProgress {
+ unsigned int bits;
+ int error_result;
+ unsigned long length;
+ unsigned long transferred;
+ unsigned long lengthTotal;
+ unsigned long transferredTotal;
+ unsigned int numIndex;
+ unsigned int numTotal;
+ unsigned int restSec;
+ unsigned int restSecTotal;
+ int preparingPercent;
+ int localCopyPercent;
+};
+
+typedef struct _SceBgftTaskProgress SceBgftTaskProgress;
+typedef int SceBgftTaskId;
+#define SCE_BGFT_INVALID_TASK_ID (-1)
+
+bool app_inst_util_is_exists(const char* title_id, bool* exists);
+typedef struct LibcMallocManagedSize {
+ uint16_t size;
+ uint16_t version;
+ uint32_t reserved1;
+ size_t maxSystemSize;
+ size_t currentSystemSize;
+ size_t maxInuseSize;
+ size_t currentInuseSize;
+} LibcMallocManagedSize;
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+int sceBgftServiceDownloadStartTask(SceBgftTaskId taskId);
+int sceBgftServiceDownloadStartTaskAll(void);
+int sceBgftServiceDownloadPauseTask(SceBgftTaskId taskId);
+int sceBgftServiceDownloadPauseTaskAll(void);
+int sceBgftServiceDownloadResumeTask(SceBgftTaskId taskId);
+int sceBgftServiceDownloadResumeTaskAll(void);
+int sceBgftServiceDownloadStopTask(SceBgftTaskId taskId);
+int sceBgftServiceDownloadStopTaskAll(void);
+int sceAppInstUtilTerminate();
+int sceBgftServiceDownloadGetProgress(SceBgftTaskId taskId, SceBgftTaskProgress* progress);
+
+
+int sceBgftFinalize(void);
+
+bool app_inst_util_init(void);
+int sceAppInstUtilInitialize(void);
+int sceAppInstUtilAppInstallPkg(const char* file_path, void* reserved);
+int sceAppInstUtilGetTitleIdFromPkg(const char* pkg_path, char* title_id, int* is_app);
+int sceAppInstUtilCheckAppSystemVer(const char* title_id, uint64_t buf, uint64_t bufs);
+int sceAppInstUtilAppPrepareOverwritePkg(const char* pkg_path);
+int sceAppInstUtilGetPrimaryAppSlot(const char* title_id, int* slot);
+int sceAppInstUtilAppUnInstall(const char* title_id);
+int sceAppInstUtilAppGetSize(const char* title_id, uint64_t* buf);
+int sceBgftServiceInit(struct bgft_init_params* params);
+int sceBgftServiceIntDownloadRegisterTaskByStorageEx(struct bgft_download_param_ex* params, int* task_id);
+int sceBgftServiceDownloadStartTask(int task_id);
+int sceSystemStateMgrGetCurrentState(void);
+int sceSystemStateMgrEnterStandby(int a1);
+int sceKernelOpenEventFlag(void* event, const char* name);
+int sceKernelCloseEventFlag(void* event);
+int sceKernelPollEventFlag(void* ef, uint64_t bitPattern, uint32_t waitMode, uint64_t *pResultPat);
+bool sceSystemStateMgrIsStandbyModeEnabled(void);
+int sceBgftServiceTerm(void);
+int32_t sceSystemServiceHideSplashScreen();
+int32_t sceSystemServiceParamGetInt(int32_t paramId, int32_t *value);
+bool patch_sfo_string(const char* input_file_name, const char* key, const char* new_value );
+uint32_t sceLncUtilLaunchApp(const char* tid, const char* argv[], LncAppParam* param);
+void print_memory();
+int sceSystemServiceLoadExec(const char* eboot, const char* argv[]);
+void drop_some_coverbox(void);
+int sceUserServiceGetForegroundUser(int *userid);
+int32_t sceSystemServiceHideSplashScreen();
+const char* Language_GetName(int m_Code);
+int sceSystemServiceGetAppIdOfMiniApp();
+int sceSystemServiceGetAppIdOfBigApp();
+int sceLncUtilGetAppId(const char* TID);
+int sceLncUtilGetAppTitleId(int appid, char* tid_out);
+int scePadSetProcessPrivilege(int privilege); // 0 = no privilege, 1 = privilege
+int sceLncUtilInitialize();
+bool if_exists(const char* path);
+int sceSystemServiceRegisterDaemon();
+void* prx_func_loader(const char* prx_path, const char* symbol);
+void loadModulesVanilla();
+int backtrace(void** buffer, int size);
+int sceAppInstUtilAppGetInsertedDiscTitleId(const char* tid);
+int scePadGetHandle(int uid, int idc, int idc_1);
+int sceAppInstUtilTerminate();
+int sceKernelBacktraceSelf(callframe_t frames[], size_t numBytesBuffer, uint32_t *pNumReturn, int mode);
+int32_t sceKernelVirtualQuery(const void *, int, OrbisKernelVirtualQueryInfo *, size_t);
+int sceKernelGetdents(int fd, char *buf, int nbytes);
+int sceSysUtilSendSystemNotificationWithText(int messageType, const char* message);
+int64_t sceKernelSendNotificationRequest(int64_t unk1, const char* Buffer, size_t size, int64_t unk2);
+int fuse_fw_supported();
+int sceKernelSetBesteffort(int besteffort, int priority);
+int pthread_getthreadid_np();
+struct tm *gmtime_s( const time_t *timer, struct tm *buf );
+void* syscall1(
+ uint64_t number,
+ void* arg1
+);
+
+void* syscall2(
+ uint64_t number,
+ void* arg1,
+ void* arg2
+);
+
+void* syscall3(
+ uint64_t number,
+ void* arg1,
+ void* arg2,
+ void* arg3
+);
+
+void* syscall4(
+ uint64_t number,
+ void* arg1,
+ void* arg2,
+ void* arg3,
+ void* arg4
+);
+
+void* syscall5(
+ uint64_t number,
+ void* arg1,
+ void* arg2,
+ void* arg3,
+ void* arg4,
+ void* arg5
+);
+
+int64_t sys_dynlib_load_prx(char* prxPath, int* moduleID);
+int64_t sys_dynlib_unload_prx(int64_t prxID);
+int64_t sys_dynlib_dlsym(int64_t moduleHandle, const char* functionName, void* destFuncOffset);
+int malloc_stats_fast(LibcMallocManagedSize *ManagedSize);
+int sceKernelDebugOutText(int DBG_CHANNEL, const char *text);
+uint32_t ps4_fw_version(void);
+#if defined(__cplusplus)
+}
+#endif
+bool copy_dir(const char* sourcedir,const char* destdir);
+bool rmtree(const char path[]);
+bool copyRegFile(const char* source, const char* dest);
+int fuse_nfs_main(struct fuse_actions args);
+long CalcAppsize(const char *path);
+void load_fuse_ip();
+extern bool fuse_debug_flag;
\ No newline at end of file
diff --git a/itemz-daemon/source/ftp.cpp b/itemz-daemon/source/ftp.cpp
new file mode 100644
index 0000000..2456145
--- /dev/null
+++ b/itemz-daemon/source/ftp.cpp
@@ -0,0 +1,1293 @@
+#include "ftp.h"
+#include "defines.h"
+#include
+#include "net.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include "log.h"
+#include
+#include "utils.hpp"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat"
+
+#define UNUSED(x) (void)(x)
+#define NET_INIT_SIZE (64 * 1024)
+#define DEFAULT_FILE_BUF_SIZE (4 * 1024 * 1024)
+
+#define FTP_DEFAULT_PATH "/"
+
+#define MAX_COMMANDS 32
+char ip_address[20];
+
+
+typedef struct SceNetEtherAddr {
+ uint8_t data[6];
+} SceNetEtherAddr;
+
+typedef union SceNetCtlInfo {
+ uint32_t device;
+ SceNetEtherAddr ether_addr;
+ uint32_t mtu;
+ uint32_t link;
+ SceNetEtherAddr bssid;
+ char ssid[33];
+ uint32_t wifi_security;
+ int32_t rssi_dbm;
+ uint8_t rssi_percentage;
+ uint8_t channel;
+ uint32_t ip_config;
+ char dhcp_hostname[256];
+ char pppoe_auth_name[128];
+ char ip_address[16];
+ char netmask[16];
+ char default_route[16];
+ char primary_dns[16];
+ char secondary_dns[16];
+ uint32_t http_proxy_config;
+ char http_proxy_server[256];
+ uint16_t http_proxy_port;
+} SceNetCtlInfo;
+
+static struct {
+ const char* cmd;
+ cmd_dispatch_func func;
+ int valid;
+} command_dispatchers[MAX_COMMANDS];
+
+static int ftp_initialized = 0;
+static unsigned int file_buf_size;
+static struct in_addr ps4_addr;
+static unsigned short int ps4_port;
+static ScePthread server_thid;
+static int server_sockfd;
+static int number_clients = 0;
+static ftps4_client_info_t* client_list = NULL;
+static ScePthreadMutex client_list_mtx;
+
+#define client_send_ctrl_msg(cl, str) \
+ sceNetSend(cl->ctrl_sockfd, str, strlen(str), 0)
+
+static void cmd_installpkg_func(ftps4_client_info_t *client)
+{
+ int ret = -1;
+ client_send_ctrl_msg(client, "Installing PKG ..." FTPS4_EOL);
+ log_info("[FTP] Installing PKG ...");
+ if((ret = pkginstall("/user/app/temp.pkg", false)) < 0){
+ notify("[FTP] Failed to install PKG see log for error message");
+ log_info("[FTP] Failed to install PKG with code: %i", ret);
+ client_send_ctrl_msg(client, "500 installpkg command failed!" FTPS4_EOL);
+
+ }
+ else{
+ log_info("[FTP] PKG installing");
+ notify("[FTP] PKG is now installing ...");
+ client_send_ctrl_msg(client, "200 installpkg command successful!" FTPS4_EOL);
+
+ }
+}
+static inline void client_send_data_msg(ftps4_client_info_t* client, const char* str)
+{
+ if (client->data_con_type == FTP_DATA_CONNECTION_ACTIVE) {
+ sceNetSend(client->data_sockfd, str, strlen(str), 0);
+ }
+ else {
+ sceNetSend(client->pasv_sockfd, str, strlen(str), 0);
+ }
+}
+
+static inline int client_recv_data_raw(ftps4_client_info_t* client, void* buf, unsigned int len)
+{
+ if (client->data_con_type == FTP_DATA_CONNECTION_ACTIVE) {
+ return sceNetRecv(client->data_sockfd, buf, len, 0);
+ }
+ else {
+ return sceNetRecv(client->pasv_sockfd, buf, len, 0);
+ }
+}
+
+static inline void client_send_data_raw(ftps4_client_info_t* client, const void* buf, unsigned int len)
+{
+ if (client->data_con_type == FTP_DATA_CONNECTION_ACTIVE) {
+ sceNetSend(client->data_sockfd, buf, len, 0);
+ }
+ else {
+ sceNetSend(client->pasv_sockfd, buf, len, 0);
+ }
+}
+
+static int file_exists(const char* path)
+{
+ struct stat s;
+ return (stat(path, &s) >= 0);
+}
+
+static void cmd_NOOP_func(ftps4_client_info_t* client)
+{
+ client_send_ctrl_msg(client, "200 No operation ;)" FTPS4_EOL);
+}
+
+static void cmd_USER_func(ftps4_client_info_t* client)
+{
+ client_send_ctrl_msg(client, "331 Username OK, need password b0ss." FTPS4_EOL);
+}
+
+static void cmd_PASS_func(ftps4_client_info_t* client)
+{
+ client_send_ctrl_msg(client, "230 User logged in!" FTPS4_EOL);
+}
+
+static void cmd_QUIT_func(ftps4_client_info_t* client)
+{
+ client_send_ctrl_msg(client, "221 Goodbye senpai :'(" FTPS4_EOL);
+}
+
+static void cmd_SYST_func(ftps4_client_info_t* client)
+{
+ client_send_ctrl_msg(client, "215 UNIX Type: L8" FTPS4_EOL);
+}
+
+static void cmd_PASV_func(ftps4_client_info_t* client)
+{
+ int ret;
+ UNUSED(ret);
+
+ char cmd[512];
+ unsigned int namelen;
+ struct sockaddr_in picked;
+
+ /* Create data mode socket name */
+ char data_socket_name[64];
+ sprintf(data_socket_name, "FTPS4_client_%i_data_socket",
+ client->num);
+
+ /* Create the data socket */
+ client->data_sockfd = sceNetSocket(data_socket_name,
+ AF_INET,
+ SOCK_STREAM,
+ 0);
+
+ log_debug("[FTP Module] PASV data socket fd: %d", client->data_sockfd);
+
+ /* Fill the data socket address */
+ client->data_sockaddr.sin_len = sizeof(client->data_sockaddr);
+ client->data_sockaddr.sin_family = AF_INET;
+ client->data_sockaddr.sin_addr.s_addr = sceNetHtonl(0);
+ /* Let the PS4 choose a port */
+ client->data_sockaddr.sin_port = sceNetHtons(0);
+
+ /* Bind the data socket address to the data socket */
+ ret = sceNetBind(client->data_sockfd,
+ (struct sockaddr*)&client->data_sockaddr,
+ sizeof(client->data_sockaddr));
+ log_debug("[FTP Module] sceNetBind(): 0x%08X", ret);
+
+ /* Start listening */
+ ret = sceNetListen(client->data_sockfd, 128);
+ log_debug("[FTP Module] sceNetListen(): 0x%08X", ret);
+
+ /* Get the port that the PS4 has chosen */
+ namelen = sizeof(picked);
+ sceNetGetsockname(client->data_sockfd, (struct sockaddr*)&picked,
+ &namelen);
+
+ log_debug("[FTP Module] PASV mode port: 0x%04X", picked.sin_port);
+
+ /* Build the command */
+ sprintf(cmd, "227 Entering Passive Mode (%hhu,%hhu,%hhu,%hhu,%hhu,%hhu)" FTPS4_EOL,
+ (ps4_addr.s_addr >> 0) & 0xFF,
+ (ps4_addr.s_addr >> 8) & 0xFF,
+ (ps4_addr.s_addr >> 16) & 0xFF,
+ (ps4_addr.s_addr >> 24) & 0xFF,
+ (picked.sin_port >> 0) & 0xFF,
+ (picked.sin_port >> 8) & 0xFF);
+
+ client_send_ctrl_msg(client, cmd);
+
+ /* Set the data connection type to passive! */
+ client->data_con_type = FTP_DATA_CONNECTION_PASSIVE;
+}
+
+static void cmd_PORT_func(ftps4_client_info_t* client)
+{
+ unsigned char data_ip[4];
+ unsigned char porthi, portlo;
+ unsigned short data_port;
+ char ip_str[16];
+ struct in_addr data_addr;
+ int n;
+
+ if (!client->recv_cmd_args) {
+ client_send_ctrl_msg(client, "500 Syntax error, command unrecognized." FTPS4_EOL);
+ return;
+ }
+
+ n = sscanf(client->recv_cmd_args, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",
+ &data_ip[0], &data_ip[1], &data_ip[2], &data_ip[3],
+ &porthi, &portlo);
+ if (n != 6) {
+ client_send_ctrl_msg(client, "500 Syntax error, command unrecognized." FTPS4_EOL);
+ return;
+ }
+
+ data_port = portlo + porthi * 256;
+
+ /* Convert to an X.X.X.X IP string */
+ sprintf(ip_str, "%d.%d.%d.%d",
+ data_ip[0], data_ip[1], data_ip[2], data_ip[3]);
+
+ /* Convert the IP to a struct in_addr */
+ sceNetInetPton(AF_INET, ip_str, &data_addr);
+
+ log_debug("[FTP Module] PORT connection to client's IP: %s Port: %d", ip_str, data_port);
+
+ /* Create data mode socket name */
+ char data_socket_name[64];
+ sprintf(data_socket_name, "FTPS4_client_%i_data_socket",
+ client->num);
+
+ /* Create data mode socket */
+ client->data_sockfd = sceNetSocket(data_socket_name,
+ AF_INET,
+ SOCK_STREAM,
+ 0);
+
+ log_debug("[FTP Module] Client %i data socket fd: %d", client->num,
+ client->data_sockfd);
+
+ /* Prepare socket address for the data connection */
+ client->data_sockaddr.sin_len = sizeof(client->data_sockaddr);
+ client->data_sockaddr.sin_family = AF_INET;
+ client->data_sockaddr.sin_addr = data_addr;
+ client->data_sockaddr.sin_port = sceNetHtons(data_port);
+
+ /* Set the data connection type to active! */
+ client->data_con_type = FTP_DATA_CONNECTION_ACTIVE;
+
+ client_send_ctrl_msg(client, "200 PORT command successful!" FTPS4_EOL);
+}
+
+static void client_open_data_connection(ftps4_client_info_t* client)
+{
+ int ret;
+ UNUSED(ret);
+
+ unsigned int addrlen;
+
+ if (client->data_con_type == FTP_DATA_CONNECTION_ACTIVE) {
+ /* Connect to the client using the data socket */
+ ret = sceNetConnect(client->data_sockfd,
+ (struct sockaddr*)&client->data_sockaddr,
+ sizeof(client->data_sockaddr));
+
+ log_debug("[FTP Module] sceNetConnect(): 0x%08X", ret);
+ }
+ else {
+ /* Listen to the client using the data socket */
+ addrlen = sizeof(client->pasv_sockaddr);
+ client->pasv_sockfd = sceNetAccept(client->data_sockfd,
+ (struct sockaddr*)&client->pasv_sockaddr,
+ &addrlen);
+ log_debug("[FTP Module] PASV client fd: 0x%08X", client->pasv_sockfd);
+ }
+}
+
+static void client_close_data_connection(ftps4_client_info_t* client)
+{
+ sceNetSocketClose(client->data_sockfd);
+ /* In passive mode we have to close the client pasv socket too */
+ if (client->data_con_type == FTP_DATA_CONNECTION_PASSIVE) {
+ sceNetSocketClose(client->pasv_sockfd);
+ }
+ client->data_con_type = FTP_DATA_CONNECTION_NONE;
+}
+
+static char file_type_char(mode_t mode)
+{
+ return S_ISBLK(mode) ? 'b' :
+ S_ISCHR(mode) ? 'c' :
+ S_ISREG(mode) ? '-' :
+ S_ISDIR(mode) ? 'd' :
+ S_ISFIFO(mode) ? 'p' :
+ S_ISSOCK(mode) ? 's' :
+ S_ISLNK(mode) ? 'l' : ' ';
+}
+
+static int gen_list_format(char* out, int n, mode_t file_mode, unsigned long long file_size,
+ const struct tm file_tm, const char* file_name, const char* link_name, const struct tm cur_tm)
+{
+ static const char num_to_month[][4] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ char yt[6];
+
+ if (cur_tm.tm_year == file_tm.tm_year) {
+ snprintf(yt, sizeof(yt), "%02d:%02d", file_tm.tm_hour, file_tm.tm_min);
+ }
+ else {
+ snprintf(yt, sizeof(yt), "%04d", 1900 + file_tm.tm_year);
+ }
+
+#define LIST_FMT "%c%c%c%c%c%c%c%c%c%c 1 ps4 ps4 %llu %s %2d %s %s"
+#define LIST_ARGS \
+ file_type_char(file_mode), \
+ file_mode & 0400 ? 'r' : '-', \
+ file_mode & 0200 ? 'w' : '-', \
+ file_mode & 0100 ? (S_ISDIR(file_mode) ? 's' : 'x') : (S_ISDIR(file_mode) ? 'S' : '-'), \
+ file_mode & 040 ? 'r' : '-', \
+ file_mode & 020 ? 'w' : '-', \
+ file_mode & 010 ? (S_ISDIR(file_mode) ? 's' : 'x') : (S_ISDIR(file_mode) ? 'S' : '-'), \
+ file_mode & 04 ? 'r' : '-', \
+ file_mode & 02 ? 'w' : '-', \
+ file_mode & 01 ? (S_ISDIR(file_mode) ? 's' : 'x') : (S_ISDIR(file_mode) ? 'S' : '-'), \
+ file_size, \
+ num_to_month[file_tm.tm_mon%12], \
+ file_tm.tm_mday, \
+ yt, \
+ file_name
+
+ if (!S_ISLNK(file_mode) || link_name[0] == '\0') {
+ return snprintf(out, n, LIST_FMT FTPS4_EOL, LIST_ARGS);
+ }
+ else {
+ return snprintf(out, n, LIST_FMT " -> %s" FTPS4_EOL, LIST_ARGS, link_name);
+ }
+
+#undef LIST_ARGS
+#undef LIST_FMT
+}
+
+static void send_LIST(ftps4_client_info_t* client, const char* path)
+{
+ char buffer[512];
+ uint8_t* dentbuf;
+ size_t dentbufsize;
+ int dfd, dentsize, err, readlinkerr;
+ struct dirent* dent, * dend;
+ struct stat st;
+ time_t cur_time;
+ struct tm tm, cur_tm;
+
+ if (stat(path, &st) < 0) {
+ client_send_ctrl_msg(client, "550 Invalid directory." FTPS4_EOL);
+ return;
+ }
+
+ dentbufsize = st.st_blksize;
+ log_debug("[FTP Module] dent buffer size = %lx", dentbufsize);
+
+ dfd = open(path, O_RDONLY, 0);
+ if (dfd < 0) {
+ client_send_ctrl_msg(client, "550 Invalid directory." FTPS4_EOL);
+ return;
+ }
+
+ dentbuf = (uint8_t*)malloc(dentbufsize);
+ memset(dentbuf, 0, dentbufsize);
+
+ client_send_ctrl_msg(client, "150 Opening ASCII mode data transfer for LIST." FTPS4_EOL);
+
+ client_open_data_connection(client);
+
+ time(&cur_time);
+ gmtime_s(&cur_time, &cur_tm);
+
+ while ((dentsize = getdents(dfd, (char*)dentbuf, dentbufsize)) > 0) {
+ dent = (struct dirent*)dentbuf;
+ dend = (struct dirent*)(&dentbuf[dentsize]);
+
+ while (dent != dend) {
+ if (dent->d_name[0] != '\0') {
+ char full_path[PATH_MAX];
+ snprintf(full_path, sizeof(full_path), "%s/%s", path, dent->d_name);
+
+ err = stat(full_path, &st);
+ if (err == 0) {
+ char link_path[PATH_MAX];
+ if (S_ISLNK(st.st_mode)) {
+ if ((readlinkerr = readlink(full_path, link_path, sizeof(link_path))) > 0) {
+ link_path[readlinkerr] = 0;
+ }
+ else {
+ link_path[0] = 0;
+ }
+ }
+
+ gmtime_s(&st.st_ctim.tv_sec, &tm);
+ gen_list_format(buffer, sizeof(buffer),
+ st.st_mode,
+ st.st_size,
+ tm,
+ dent->d_name,
+ S_ISLNK(st.st_mode) && link_path[0] != '\0' ? link_path : NULL,
+ cur_tm);
+
+ client_send_data_msg(client, buffer);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ else {
+ log_debug("[FTP Module] %s stat returned %s", full_path, strerror(errno));
+ }
+ }
+ else {
+ log_debug("[FTP Module] got empty dent");
+ }
+ dent = (struct dirent*)(((char*)dent) + dent->d_reclen);
+ }
+ memset(dentbuf, 0, dentbufsize);
+ }
+
+ close(dfd);
+ free(dentbuf);
+
+ log_debug("[FTP Module] Done sending LIST");
+
+ client_close_data_connection(client);
+ client_send_ctrl_msg(client, "226 Transfer complete." FTPS4_EOL);
+}
+
+static void cmd_LIST_func(ftps4_client_info_t* client)
+{
+ char list_path[PATH_MAX];
+ int list_cur_path = 1;
+ int n = !client->recv_cmd_args
+ ? 0
+ : sscanf(client->recv_cmd_args, "%[^\r\n\t]", list_path);
+
+ if (n > 0 && file_exists(list_path))
+ list_cur_path = 0;
+
+ if (list_cur_path)
+ send_LIST(client, client->cur_path);
+ else
+ send_LIST(client, list_path);
+}
+
+static void cmd_NLST_func(ftps4_client_info_t* client)
+{
+ cmd_LIST_func(client);
+}
+
+static void cmd_PWD_func(ftps4_client_info_t* client)
+{
+ char msg[PATH_MAX];
+ snprintf(msg, sizeof(msg), "257 \"%s\" is the current directory." FTPS4_EOL, client->cur_path);
+ client_send_ctrl_msg(client, msg);
+}
+
+static void dir_up(char* path)
+{
+ char* pch;
+ size_t len_in = strlen(path);
+ if (len_in == 1) {
+ root:
+ strcpy(path, "/");
+ return;
+ }
+ pch = strrchr(path, '/');
+ if (pch == path)
+ goto root;
+ *pch = '\0';
+}
+
+static void cmd_CWD_func(ftps4_client_info_t* client)
+{
+ char cmd_path[PATH_MAX];
+ char tmp_path[PATH_MAX];
+ int pd;
+ int n = !client->recv_cmd_args
+ ? 0
+ : sscanf(client->recv_cmd_args, "%[^\r\n\t]", cmd_path);
+
+ if (n < 1) {
+ client_send_ctrl_msg(client, "500 Syntax error, command unrecognized." FTPS4_EOL);
+ }
+ else {
+ if (strcmp(cmd_path, "/") == 0) {
+ strcpy(client->cur_path, cmd_path);
+ }
+ else if (strcmp(cmd_path, "..") == 0) {
+ dir_up(client->cur_path);
+ }
+ else {
+ if (cmd_path[0] == '/') { /* Full path */
+ strcpy(tmp_path, cmd_path);
+ }
+ else { /* Change dir relative to current dir */
+ if (strcmp(client->cur_path, "/") == 0)
+ snprintf(tmp_path, sizeof(tmp_path), "%s%s", client->cur_path, cmd_path);
+ else
+ snprintf(tmp_path, sizeof(tmp_path), "%s/%s", client->cur_path, cmd_path);
+ }
+
+ /* If the path is not "/", check if it exists */
+ if (strcmp(tmp_path, "/") != 0) {
+ /* Check if the path exists */
+ pd = open(tmp_path, O_RDONLY, 0);
+ if (pd < 0) {
+ client_send_ctrl_msg(client, "550 Invalid directory." FTPS4_EOL);
+ return;
+ }
+ close(pd);
+ }
+ strcpy(client->cur_path, tmp_path);
+ }
+ client_send_ctrl_msg(client, "250 Requested file action okay, completed." FTPS4_EOL);
+ }
+}
+
+static void cmd_TYPE_func(ftps4_client_info_t* client)
+{
+ char data_type;
+ char format_control[8];
+ int n_args = !client->recv_cmd_args
+ ? 0
+ : sscanf(client->recv_cmd_args, "%c %s", &data_type, format_control);
+
+ if (n_args > 0) {
+ switch (data_type) {
+ case 'A':
+ case 'I':
+ client_send_ctrl_msg(client, "200 Okay" FTPS4_EOL);
+ break;
+ case 'E':
+ case 'L':
+ default:
+ client_send_ctrl_msg(client, "504 Error: bad parameters?" FTPS4_EOL);
+ break;
+ }
+ }
+ else {
+ client_send_ctrl_msg(client, "504 Error: bad parameters?" FTPS4_EOL);
+ }
+}
+
+static void cmd_CDUP_func(ftps4_client_info_t* client)
+{
+ dir_up(client->cur_path);
+ client_send_ctrl_msg(client, "200 Command okay." FTPS4_EOL);
+}
+
+static void send_file(ftps4_client_info_t* client, const char* path)
+{
+ int fd;
+ unsigned int bytes_read;
+
+ log_debug("[FTP Module] Opening: %s", path);
+
+ if ((fd = open(path, O_RDONLY, 0)) >= 0) {
+
+ lseek(fd, client->restore_point, SEEK_SET);
+
+ std::vector buffer(file_buf_size); // Allocate memory using std::vector
+ if (buffer.empty()) {
+ client_send_ctrl_msg(client, "550 Could not allocate memory." FTPS4_EOL);
+ return;
+ }
+
+ client_open_data_connection(client);
+ client_send_ctrl_msg(client, "150 Opening Image mode data transfer." FTPS4_EOL);
+
+ while ((bytes_read = read(fd, buffer.data(), file_buf_size)) > 0) {
+ client_send_data_raw(client, buffer.data(), bytes_read);
+ }
+
+ close(fd);
+ // No need to free the buffer, it will be automatically deallocated
+ client->restore_point = 0;
+ client_send_ctrl_msg(client, "226 Transfer completed." FTPS4_EOL);
+ client_close_data_connection(client);
+
+ }
+ else {
+ client_send_ctrl_msg(client, "550 File not found." FTPS4_EOL);
+ }
+}
+
+/* This function generates a FTP full-path valid path with the input path (relative or absolute)
+ * from RETR, STOR, DELE, RMD, MKD, RNFR and RNTO commands */
+static void gen_ftp_fullpath(ftps4_client_info_t* client, char* path, size_t path_size)
+{
+ char cmd_path[PATH_MAX];
+ int n = !client->recv_cmd_args
+ ? 0
+ : sscanf(client->recv_cmd_args, "%[^\r\n\t]", cmd_path);
+
+ if (n < 1) {
+ client_send_ctrl_msg(client, "500 Syntax error, command unrecognized." FTPS4_EOL);
+ return;
+ }
+
+ if (cmd_path[0] == '/') {
+ /* Full path */
+ strncpy(path, cmd_path, path_size);
+ }
+ else {
+ /* The file is relative to current dir, so
+ * append the file to the current path */
+ snprintf(path, path_size, "%s/%s", client->cur_path, cmd_path);
+ }
+}
+
+static void cmd_RETR_func(ftps4_client_info_t* client)
+{
+ char dest_path[PATH_MAX];
+ gen_ftp_fullpath(client, dest_path, sizeof(dest_path));
+ send_file(client, dest_path);
+
+}
+
+static void receive_file(ftps4_client_info_t* client, const char* path)
+{
+ int fd;
+ unsigned int bytes_recv;
+
+ log_debug("[FTP Module] Opening: %s", path);
+
+ int mode = O_CREAT | O_RDWR;
+ /* if we resume broken - append missing part
+ * else - overwrite file */
+ if (client->restore_point) {
+ mode = mode | O_APPEND;
+ }
+ else {
+ mode = mode | O_TRUNC;
+ }
+
+ if ((fd = open(path, mode, 0777)) >= 0) {
+
+ std::vector buffer(file_buf_size); // Allocate memory using std::vector
+ if (buffer.empty()) {
+ client_send_ctrl_msg(client, "550 Could not allocate memory." FTPS4_EOL);
+ return;
+ }
+
+ client_open_data_connection(client);
+ client_send_ctrl_msg(client, "150 Opening Image mode data transfer." FTPS4_EOL);
+
+ while ((bytes_recv = client_recv_data_raw(client, buffer.data(), file_buf_size)) > 0) {
+ write(fd, buffer.data(), bytes_recv);
+ }
+
+ close(fd);
+ // No need to free the buffer, it will be automatically deallocated
+ client->restore_point = 0;
+ if (bytes_recv == 0) {
+ client_send_ctrl_msg(client, "226 Transfer completed." FTPS4_EOL);
+ }
+ else {
+ unlink(path);
+ client_send_ctrl_msg(client, "426 Connection closed; transfer aborted." FTPS4_EOL);
+ }
+ client_close_data_connection(client);
+
+ }
+ else {
+ client_send_ctrl_msg(client, "550 File not found." FTPS4_EOL);
+ }
+}
+
+static void cmd_STOR_func(ftps4_client_info_t* client)
+{
+ char dest_path[PATH_MAX];
+ gen_ftp_fullpath(client, dest_path, sizeof(dest_path));
+ receive_file(client, dest_path);
+}
+
+static void delete_file(ftps4_client_info_t* client, const char* path)
+{
+ log_debug("[FTP Module] Deleting: %s", path);
+
+ if (unlink(path) >= 0) {
+ client_send_ctrl_msg(client, "226 File deleted." FTPS4_EOL);
+ }
+ else {
+ client_send_ctrl_msg(client, "550 Could not delete the file." FTPS4_EOL);
+ }
+}
+
+static void cmd_DELE_func(ftps4_client_info_t* client)
+{
+ char dest_path[PATH_MAX];
+ gen_ftp_fullpath(client, dest_path, sizeof(dest_path));
+ delete_file(client, dest_path);
+}
+
+static void delete_dir(ftps4_client_info_t* client, const char* path)
+{
+ int ret;
+ log_debug("[FTP Module] Deleting: %s", path);
+ ret = rmdir(path);
+ if (ret >= 0) {
+ client_send_ctrl_msg(client, "226 Directory deleted." FTPS4_EOL);
+ }
+ else if (errno == 66) { /* ENOTEMPTY */
+ client_send_ctrl_msg(client, "550 Directory is not empty." FTPS4_EOL);
+ }
+ else {
+ client_send_ctrl_msg(client, "550 Could not delete the directory." FTPS4_EOL);
+ }
+}
+
+static void cmd_RMD_func(ftps4_client_info_t* client)
+{
+ char dest_path[PATH_MAX];
+ gen_ftp_fullpath(client, dest_path, sizeof(dest_path));
+ delete_dir(client, dest_path);
+}
+
+static void create_dir(ftps4_client_info_t* client, const char* path)
+{
+ log_debug("[FTP Module] Creating: %s", path);
+
+ if (mkdir(path, 0777) >= 0) {
+ client_send_ctrl_msg(client, "226 Directory created." FTPS4_EOL);
+ }
+ else {
+ client_send_ctrl_msg(client, "550 Could not create the directory." FTPS4_EOL);
+ }
+}
+
+static void cmd_MKD_func(ftps4_client_info_t* client)
+{
+ char dest_path[PATH_MAX];
+ gen_ftp_fullpath(client, dest_path, sizeof(dest_path));
+ create_dir(client, dest_path);
+}
+
+static void cmd_RNFR_func(ftps4_client_info_t* client)
+{
+ char from_path[PATH_MAX];
+ /* Get the origin filename */
+ gen_ftp_fullpath(client, from_path, sizeof(from_path));
+
+ /* Check if the file exists */
+ if (!file_exists(from_path)) {
+ client_send_ctrl_msg(client, "550 The file doesn't exist." FTPS4_EOL);
+ return;
+ }
+ /* The file to be renamed is the received path */
+ strcpy(client->rename_path, from_path);
+ client_send_ctrl_msg(client, "350 I need the destination name b0ss." FTPS4_EOL);
+}
+
+static void cmd_RNTO_func(ftps4_client_info_t* client)
+{
+ char path_to[PATH_MAX];
+ /* Get the destination filename */
+ gen_ftp_fullpath(client, path_to, sizeof(path_to));
+
+ log_debug("[FTP Module] Renaming: %s to %s", client->rename_path, path_to);
+
+ if (rename(client->rename_path, path_to) < 0) {
+ client_send_ctrl_msg(client, "550 Error renaming the file." FTPS4_EOL);
+ }
+
+ client_send_ctrl_msg(client, "226 Rename completed." FTPS4_EOL);
+}
+
+static void cmd_SIZ3_func(ftps4_client_info_t* client)
+{
+ struct stat s;
+ char path[PATH_MAX];
+ char cmd[64];
+ /* Get the filename to retrieve its size */
+ gen_ftp_fullpath(client, path, sizeof(path));
+
+ /* Check if the file exists */
+ if (stat(path, &s) < 0) {
+ client_send_ctrl_msg(client, "550 The file doesn't exist." FTPS4_EOL);
+ return;
+ }
+ /* Send the size of the file */
+ sprintf(cmd, "213 %lld" FTPS4_EOL, s.st_size);
+ client_send_ctrl_msg(client, cmd);
+}
+
+static void cmd_REST_func(ftps4_client_info_t* client)
+{
+ char cmd[64];
+ sscanf(client->recv_buffer, "%*[^ ] %d", &client->restore_point);
+ sprintf(cmd, "350 Resuming at %d" FTPS4_EOL, client->restore_point);
+ client_send_ctrl_msg(client, cmd);
+}
+
+static void cmd_FEAT_func(ftps4_client_info_t* client)
+{
+ /*So client would know that we support resume */
+ client_send_ctrl_msg(client, "211-extensions" FTPS4_EOL);
+ client_send_ctrl_msg(client, "REST STREAM" FTPS4_EOL);
+ client_send_ctrl_msg(client, "211 end" FTPS4_EOL);
+}
+
+static void cmd_APPE_func(ftps4_client_info_t* client)
+{
+ /* set restore point to not 0
+ restore point numeric value only matters if we RETR file from ps4.
+ If we STOR or APPE, it is only used to indicate that we want to resume
+ a broken transfer */
+ client->restore_point = -1;
+ char dest_path[PATH_MAX];
+ gen_ftp_fullpath(client, dest_path, sizeof(dest_path));
+ receive_file(client, dest_path);
+}
+
+static cmd_dispatch_func get_dispatch_func(const char* cmd)
+{
+ int i;
+ // Check for commands
+ for (i = 0; i < MAX_COMMANDS; i++) {
+ if (command_dispatchers[i].valid) {
+ if (strcmp(cmd, command_dispatchers[i].cmd) == 0) {
+ return command_dispatchers[i].func;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void client_list_add(ftps4_client_info_t* client)
+{
+ /* Add the client at the front of the client list */
+ scePthreadMutexLock(&client_list_mtx);
+
+ if (client_list == NULL) { /* List is empty */
+ client_list = client;
+ client->prev = NULL;
+ client->next = NULL;
+ }
+ else {
+ client->next = client_list;
+ client->next->prev = client;
+ client->prev = NULL;
+ client_list = client;
+ }
+ client->restore_point = 0;
+ number_clients++;
+
+ scePthreadMutexUnlock(&client_list_mtx);
+}
+
+static void client_list_delete(ftps4_client_info_t* client)
+{
+ /* Remove the client from the client list */
+ scePthreadMutexLock(&client_list_mtx);
+
+ if (client->prev) {
+ client->prev->next = client->next;
+ }
+ if (client->next) {
+ client->next->prev = client->prev;
+ }
+ if (client == client_list) {
+ client_list = client->next;
+ }
+
+ number_clients--;
+
+ scePthreadMutexUnlock(&client_list_mtx);
+}
+
+static void client_list_thread_end()
+{
+ ftps4_client_info_t* it, * next;
+ ScePthread client_thid;
+ const int data_abort_flags = 0x01 |
+ 0x02;
+
+ /* Iterate over the client list and close their sockets */
+ scePthreadMutexLock(&client_list_mtx);
+
+ it = client_list;
+
+ while (it) {
+ next = it->next;
+ client_thid = it->thid;
+
+ /* Abort the client's control socket, only abort
+ * receiving data so we can still send control messages */
+ sceNetSocketAbort(it->ctrl_sockfd,
+ 0x01);
+
+ /* If there's an open data connection, abort it */
+ if (it->data_con_type != FTP_DATA_CONNECTION_NONE) {
+ sceNetSocketAbort(it->data_sockfd, data_abort_flags);
+ if (it->data_con_type == FTP_DATA_CONNECTION_PASSIVE) {
+ sceNetSocketAbort(it->pasv_sockfd, data_abort_flags);
+ }
+ }
+
+ /* Wait until the client threads ends */
+ scePthreadJoin(client_thid, NULL);
+
+ it = next;
+ }
+
+ scePthreadMutexUnlock(&client_list_mtx);
+}
+
+static void* client_thread(void* arg)
+{
+ char cmd[16];
+ cmd_dispatch_func dispatch_func;
+ ftps4_client_info_t* client = (ftps4_client_info_t*)arg;
+
+ log_debug("[FTP Module] Client thread %i started!", client->num);
+
+ client_send_ctrl_msg(client, "220 FTPS4 Server ready." FTPS4_EOL);
+
+ while (1) {
+ memset(client->recv_buffer, 0, sizeof(client->recv_buffer));
+
+ client->n_recv = sceNetRecv(client->ctrl_sockfd, client->recv_buffer, sizeof(client->recv_buffer), 0);
+ if (client->n_recv > 0) {
+
+ /* The command is the first chars until the first space */
+ sscanf(client->recv_buffer, "%s", cmd);
+
+ client->recv_cmd_args = strchr(client->recv_buffer, ' ');
+ if (client->recv_cmd_args)
+ client->recv_cmd_args++; /* Skip the space */
+
+ /* Wait 1 ms before sending any data */
+ sceKernelUsleep(1 * 1000);
+
+ if ((dispatch_func = get_dispatch_func(cmd))) {
+ dispatch_func(client);
+ }
+ else {
+ client_send_ctrl_msg(client, "502 Sorry, command not implemented. :(" FTPS4_EOL);
+ }
+
+ }
+ else if (client->n_recv == 0) {
+ /* Value 0 means connection closed by the remote peer */
+ log_info("Connection closed by the client %i.", client->num);
+ /* Delete itself from the client list */
+ client_list_delete(client);
+ break;
+ }
+ else if (client->n_recv == 0x80410104) {
+ /* Socket aborted (ftps4_fini() called) */
+ log_info("Client %i socket aborted.", client->num);
+ break;
+ }
+ else {
+ /* Other errors */
+ log_info("Client %i socket error: 0x%08X", client->num, client->n_recv);
+ client_list_delete(client);
+ break;
+ }
+ }
+
+ /* Close the client's socket */
+ sceNetSocketClose(client->ctrl_sockfd);
+
+ /* If there's an open data connection, close it */
+ if (client->data_con_type != FTP_DATA_CONNECTION_NONE) {
+ sceNetSocketClose(client->data_sockfd);
+ if (client->data_con_type == FTP_DATA_CONNECTION_PASSIVE) {
+ sceNetSocketClose(client->pasv_sockfd);
+ }
+ }
+
+ log_debug("[FTP Module] Client thread %i exiting!", client->num);
+
+ delete client;
+
+ scePthreadExit(NULL);
+ return NULL;
+}
+
+static void* server_thread(void* arg)
+{
+ int ret, enable;
+ UNUSED(ret);
+
+ struct sockaddr_in serveraddr;
+
+ log_debug("[FTP Module] Server thread started!");
+
+ /* Create server socket */
+ server_sockfd = sceNetSocket("FTPS4_server_sock",
+ AF_INET,
+ SOCK_STREAM,
+ 0);
+
+ log_debug("[FTP Module] Server socket fd: %d", server_sockfd);
+
+ enable = 1;
+ sceNetSetsockopt(server_sockfd, 0xffff, 0x04, &enable, sizeof(enable));
+
+ /* Fill the server's address */
+ serveraddr.sin_len = sizeof(serveraddr);
+ serveraddr.sin_family = AF_INET;
+ serveraddr.sin_addr.s_addr = sceNetHtonl(0);
+ serveraddr.sin_port = sceNetHtons(ps4_port);
+
+ /* Bind the server's address to the socket */
+ ret = sceNetBind(server_sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
+ log_debug("[FTP Module] sceNetBind(): 0x%08X", ret);
+
+ /* Start listening */
+ ret = sceNetListen(server_sockfd, 128);
+ log_debug("[FTP Module] sceNetListen(): 0x%08X", ret);
+
+ while (1) {
+ /* Accept clients */
+ struct sockaddr_in clientaddr;
+ int client_sockfd;
+ unsigned int addrlen = sizeof(clientaddr);
+
+ log_debug("[FTP Module] Waiting for incoming connections...");
+
+ client_sockfd = sceNetAccept(server_sockfd, (struct sockaddr*)&clientaddr, &addrlen);
+ if (client_sockfd >= 0) {
+ log_debug("[FTP Module] New connection, client fd: 0x%08X", client_sockfd);
+
+ /* Get the client's IP address */
+ char remote_ip[16];
+ sceNetInetNtop(AF_INET,
+ &clientaddr.sin_addr.s_addr,
+ remote_ip,
+ sizeof(remote_ip));
+
+ log_info("Client %i connected, IP: %s port: %i",
+ number_clients, remote_ip, clientaddr.sin_port);
+
+ /* Allocate the ftps4_client_info_t struct for the new client */
+ ftps4_client_info_t* client = new ftps4_client_info_t;
+ client->num = number_clients;
+ client->ctrl_sockfd = client_sockfd;
+ client->data_con_type = FTP_DATA_CONNECTION_NONE;
+ strcpy(client->cur_path, FTP_DEFAULT_PATH);
+ memcpy(&client->addr, &clientaddr, sizeof(client->addr));
+
+ /* Add the new client to the client list */
+ client_list_add(client);
+
+ /* Create a new thread for the client */
+ char client_thread_name[64];
+ sprintf(client_thread_name, "FTPS4_client_%i_thread",
+ number_clients);
+
+ /* Create a new thread for the client */
+ scePthreadCreate(&client->thid, NULL, client_thread, client, client_thread_name);
+
+ log_debug("[FTP Module] Client %i thread UID: 0x%08X", number_clients, client->thid);
+
+ number_clients++;
+ }
+ else if (client_sockfd == 0x80410104) {
+ log_info("Server socket aborted.");
+ break;
+ }
+ else {
+ /* if sceNetAccept returns < 0, it means that the listening
+ * socket has been closed, this means that we want to
+ * finish the server thread */
+ log_debug("[FTP Module] Server socket closed, 0x%08X", client_sockfd);
+ break;
+ }
+ }
+
+ log_debug("[FTP Module] Server thread exiting!");
+
+ /* Causing a crash? */
+ /*scePthreadExit(NULL);*/
+ return NULL;
+}
+
+#define add_command(name) ftps4_ext_add_command(#name, cmd_##name##_func)
+bool ftps4_init(const char* ip, unsigned short int port)
+{
+ int i;
+
+ if (ftp_initialized) {
+ return false;
+ }
+
+ file_buf_size = DEFAULT_FILE_BUF_SIZE;
+
+ /* Save the listening port of the PS4 to a global variable */
+ ps4_port = port;
+
+ /* Save the IP of the PS4 to a global variable */
+ sceNetInetPton(AF_INET, ip, &ps4_addr);
+
+ /* Create the client list mutex */
+ scePthreadMutexInit(&client_list_mtx, NULL, "FTPS4_client_list_mutex");
+ log_debug("[FTP Module] Client list mutex UID: 0x%08X", client_list_mtx);
+
+ for (i = 0; i < MAX_COMMANDS; i++) {
+ command_dispatchers[i].valid = 0;
+ }
+
+ /* Add commands */
+ add_command(NOOP);
+ add_command(USER);
+ add_command(installpkg);
+ add_command(PASS);
+ add_command(QUIT);
+ add_command(SYST);
+ add_command(PASV);
+ add_command(PORT);
+ add_command(LIST);
+ add_command(NLST);
+ add_command(PWD);
+ add_command(CWD);
+ add_command(TYPE);
+ add_command(CDUP);
+ add_command(RETR);
+ add_command(STOR);
+ add_command(DELE);
+ add_command(RMD);
+ add_command(MKD);
+ add_command(RNFR);
+ add_command(RNTO);
+ add_command(SIZ3); //size
+ add_command(REST);
+ add_command(FEAT);
+ add_command(APPE);
+
+ /* Create server thread */
+ scePthreadCreate(&server_thid, NULL, server_thread, NULL, "FTPS4_server_thread");
+ log_debug("[FTP Module] Server thread UID: 0x%08X", server_thid);
+
+ ftp_initialized = 1;
+
+ return true;
+}
+
+void ftps4_fini()
+{
+ if (ftp_initialized) {
+ /* Necessary to get sceNetAccept to notice the close on PS4? */
+ sceNetSocketAbort(server_sockfd, 0);
+ /* In order to "stop" the blocking sceNetAccept,
+ * we have to close the server socket; this way
+ * the accept call will return an error */
+ sceNetSocketClose(server_sockfd);
+
+ /* Wait until the server threads ends */
+ scePthreadJoin(server_thid, NULL);
+
+ /* To close the clients we have to do the same:
+ * we have to iterate over all the clients
+ * and shutdown their sockets */
+ client_list_thread_end();
+
+ /* Delete the client list mutex */
+ scePthreadMutexDestroy(&client_list_mtx);
+
+ client_list = NULL;
+ number_clients = 0;
+
+ ftp_initialized = 0;
+ }
+}
+
+int ftps4_is_initialized()
+{
+ return ftp_initialized;
+}
+
+void ftps4_set_file_buf_size(unsigned int size)
+{
+ file_buf_size = size;
+}
+
+int ftps4_ext_add_command(const char* cmd, cmd_dispatch_func func)
+{
+ int i;
+ for (i = 0; i < MAX_COMMANDS; i++) {
+ if (!command_dispatchers[i].valid) {
+ command_dispatchers[i].cmd = cmd;
+ command_dispatchers[i].func = func;
+ command_dispatchers[i].valid = 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int ftps4_ext_del_command(const char* cmd)
+{
+ int i;
+ for (i = 0; i < MAX_COMMANDS; i++) {
+ if (strcmp(cmd, command_dispatchers[i].cmd) == 0) {
+ command_dispatchers[i].valid = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ftps4_ext_client_send_ctrl_msg(ftps4_client_info_t* client, const char* msg)
+{
+ client_send_ctrl_msg(client, msg);
+}
+
+void ftps4_ext_client_send_data_msg(ftps4_client_info_t* client, const char* str)
+{
+ client_send_data_msg(client, str);
+}
+
+void ftps4_gen_ftp_fullpath(ftps4_client_info_t* client, char* path, size_t path_size)
+{
+ gen_ftp_fullpath(client, path, path_size);
+}
+
+int get_ip_address(char* ip_address)
+{
+ int ret;
+ SceNetCtlInfo info;
+
+ ret = sceNetCtlInit();
+ if (ret < 0)
+ goto error;
+
+ ret = sceNetCtlGetInfo(14, &info);
+ if (ret < 0)
+ goto error;
+
+ memcpy(ip_address, info.ip_address, sizeof(info.ip_address));
+
+ sceNetCtlTerm();
+
+ return ret;
+
+error:
+ ip_address = NULL;
+ return -1;
+}
+bool ftp_started = false;
+
+bool StartFTP() {
+ ftp_started = false;
+ if (get_ip_address(&ip_address[0]) < 0)
+ return false;
+
+ return (ftp_started = ftps4_init(&ip_address[0], 21));
+}
+void ShutdownFTP() {
+ ftps4_fini();
+}
+void check_ftp_addr_change(){
+
+ char func_ip_address[16];
+ if (get_ip_address(&func_ip_address[0]) < 0 || !ftp_started)
+ return;
+
+ if (strcmp(&ip_address[0], &func_ip_address[0]) != 0) {
+ log_debug("[FTP Module] IP Address changed, restarting FTP server");
+ ShutdownFTP();
+ StartFTP();
+ }
+}
diff --git a/itemz-daemon/source/fuse-nfs.cpp b/itemz-daemon/source/fuse-nfs.cpp
new file mode 100644
index 0000000..8de8bb8
--- /dev/null
+++ b/itemz-daemon/source/fuse-nfs.cpp
@@ -0,0 +1,1045 @@
+/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */
+/*
+ Copyright (C) by Ronnie Sahlberg 2013
+ ------------------------------------------------------------------
+ Edited by LM for PS4 (2023)
+ ------------------------------------------------------------------
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see .
+*/
+/* A FUSE filesystem based on libnfs. */
+
+#define FUSE_USE_VERSION 26
+#define _FILE_OFFSET_BITS 64
+
+
+#define FUSE_USE_VERSION 26
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "utils.hpp"
+#include
+#include
+
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if you have the header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_NETINET_TCP_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_POLL_H 1
+
+/* Whether sockaddr struct has sa_len */
+#define HAVE_SOCKADDR_LEN 1
+
+/* Whether we have sockaddr_Storage */
+#define HAVE_SOCKADDR_STORAGE 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_SYS_STATVFS_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_UTIME_H 1
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+
+
+#ifndef FUSE_STAT
+#define FUSE_STAT stat
+#endif
+/* ============================= END OF CONFIG.H ========================================= */
+
+/* Only one thread at a time can enter libnfs */
+static pthread_mutex_t nfs_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+
+struct nfs_context *nfs = NULL;
+int custom_uid = -1;
+int custom_gid = -1;
+
+uid_t mount_user_uid;
+gid_t mount_user_gid;
+
+int fusenfs_allow_other_own_ids=0;
+int fuse_default_permissions=1;
+int fuse_multithreads=1;
+
+static int map_uid(int possible_uid) {
+ if (custom_uid != -1 && possible_uid == custom_uid){
+ return fuse_get_context()->uid;
+ }
+ return possible_uid;
+}
+
+static int map_gid(int possible_gid) {
+ if (custom_gid != -1 && possible_gid == custom_gid){
+ return fuse_get_context()->gid;
+ }
+ return possible_gid;
+}
+
+static int map_reverse_uid(int possible_uid) {
+ if (custom_uid != -1 && possible_uid == getuid()) {
+ return custom_uid;
+ }
+ return possible_uid;
+}
+
+static int map_reverse_gid(int possible_gid) {
+ if (custom_gid != -1 && possible_gid == getgid()){
+ return custom_gid;
+ }
+ return possible_gid;
+}
+
+struct sync_cb_data {
+ int is_finished;
+ int status;
+
+ void *return_data;
+ size_t max_size;
+};
+
+static void
+wait_for_nfs_reply(struct nfs_context *nfs, struct sync_cb_data *cb_data)
+{
+ struct pollfd pfd;
+ int revents;
+ int ret;
+ static pthread_mutex_t reply_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+ pthread_mutex_lock(&reply_mutex);
+ while (!cb_data->is_finished) {
+
+ pfd.fd = nfs_get_fd(nfs);
+ pfd.events = nfs_which_events(nfs);
+ pfd.revents = 0;
+
+ ret = poll(&pfd, 1, 100);
+ if (ret < 0) {
+ revents = -1;
+ } else {
+ revents = pfd.revents;
+ }
+
+ pthread_mutex_lock(&nfs_mutex);
+ ret = nfs_service(nfs, revents);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ cb_data->status = -EIO;
+ log_error("nfs_service failed with %d", ret);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&reply_mutex);
+}
+
+static void
+generic_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct sync_cb_data *cb_data = static_cast(private_data);
+
+ cb_data->is_finished = 1;
+ cb_data->status = status;
+}
+
+/* Update the rpc credentials to the current user unless
+ * have are overriding the credentials via url arguments.
+ */
+static void update_rpc_credentials(void) {
+ if (custom_uid == -1 && !fusenfs_allow_other_own_ids) {
+ nfs_set_uid(nfs, fuse_get_context()->uid);
+ } else if ((custom_uid == -1 ||
+ fuse_get_context()->uid != mount_user_uid)
+ && fusenfs_allow_other_own_ids) {
+ nfs_set_uid(nfs, fuse_get_context()->uid);
+ } else {
+ nfs_set_uid(nfs, custom_uid);
+ }
+ if (custom_gid == -1 && !fusenfs_allow_other_own_ids) {
+ nfs_set_gid(nfs, fuse_get_context()->gid);
+ } else if ((custom_gid == -1 ||
+ fuse_get_context()->gid != mount_user_gid)
+ && fusenfs_allow_other_own_ids) {
+ nfs_set_gid(nfs, fuse_get_context()->gid);
+ } else {
+ nfs_set_gid(nfs, custom_gid);
+ }
+}
+
+static void
+stat64_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct sync_cb_data *cb_data = static_cast(private_data);
+
+ cb_data->is_finished = 1;
+ cb_data->status = status;
+
+ if(fuse_debug_flag)
+ log_debug("stat64_cb status:%d", status);
+
+ if (status < 0) {
+ log_error("stat64_cb failed with %d", status);
+ return;
+ }
+ memcpy(cb_data->return_data, data, sizeof(struct nfs_stat_64));
+}
+
+static int
+fuse_nfs_getattr(const char *path, struct FUSE_STAT *stbuf)
+{
+ struct nfs_stat_64 st;
+ struct sync_cb_data cb_data;
+ int ret;
+
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_getattr entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ cb_data.return_data = &st;
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_lstat64_async(nfs, path, stat64_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_lstat64_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ stbuf->st_dev = st.nfs_dev;
+ stbuf->st_ino = st.nfs_ino;
+ stbuf->st_mode = st.nfs_mode;
+ stbuf->st_nlink = st.nfs_nlink;
+ stbuf->st_uid = map_uid(st.nfs_uid);
+ stbuf->st_gid = map_gid(st.nfs_gid);
+ stbuf->st_rdev = st.nfs_rdev;
+ stbuf->st_size = st.nfs_size;
+ stbuf->st_blksize = st.nfs_blksize;
+ stbuf->st_blocks = st.nfs_blocks;
+
+ stbuf->st_atim.tv_sec = st.nfs_atime;
+ stbuf->st_atim.tv_nsec = st.nfs_atime_nsec;
+ stbuf->st_mtim.tv_sec = st.nfs_mtime;
+ stbuf->st_mtim.tv_nsec = st.nfs_mtime_nsec;
+ stbuf->st_ctim.tv_sec = st.nfs_ctime;
+ stbuf->st_ctim.tv_nsec = st.nfs_ctime_nsec;
+
+ return cb_data.status;
+}
+
+static void
+readdir_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct sync_cb_data *cb_data = static_cast(private_data);
+
+ cb_data->is_finished = 1;
+ cb_data->status = status;
+
+ if (status < 0) {
+ log_error("readdir_cb status:%d", status);
+ return;
+ }
+ cb_data->return_data = data;
+}
+
+static int
+fuse_nfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct nfsdir *nfsdir;
+ struct nfsdirent *nfsdirent;
+ struct stat st;
+ struct sync_cb_data cb_data;
+ int ret;
+
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_readdir entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_opendir_async(nfs, path, readdir_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_opendir_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+ if (cb_data.status){
+ log_error("nfs_opendir_async failed with %d", cb_data.status);
+ return cb_data.status;
+ }
+
+ nfsdir = static_cast(cb_data.return_data);
+ while ((nfsdirent = nfs_readdir(nfs, nfsdir)) != NULL) {
+ //fuse_nfs_getattr(nfsdirent->name, &st);
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_readdir: %s, d_type %i", nfsdirent->name, st.st_mode);
+
+ filler(buf, nfsdirent->name, NULL, 0);
+ }
+
+ nfs_closedir(nfs, nfsdir);
+
+ return cb_data.status;
+}
+
+static void
+readlink_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct sync_cb_data *cb_data = static_cast(private_data);
+
+ cb_data->is_finished = 1;
+ cb_data->status = status;
+
+ if (status < 0) {
+ log_error("readlink_cb failed with %d", status);
+ return;
+ }
+ strncat((char*)cb_data->return_data, (const char*)data, cb_data->max_size);
+}
+
+static int
+fuse_nfs_readlink(const char *path, char *buf, size_t size)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_readlink entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ *buf = 0;
+ cb_data.return_data = buf;
+ cb_data.max_size = size;
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_readlink_async(nfs, path, readlink_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static void
+open_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct sync_cb_data *cb_data = static_cast(private_data);
+
+ cb_data->is_finished = 1;
+ cb_data->status = status;
+
+ if(fuse_debug_flag)
+ log_debug("open_cb status:%d", status);
+
+ if (status < 0) {
+ log_error("open_cb status:%d", status);
+ return;
+ }
+ cb_data->return_data = data;
+}
+
+static int
+fuse_nfs_open(const char *path, struct fuse_file_info *fi)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_open entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+
+ fi->fh = 0;
+ ret = nfs_open_async(nfs, path, fi->flags, open_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_info("nfs_open_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ fi->fh = (uint64_t)cb_data.return_data;
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_release(const char *path, struct fuse_file_info *fi)
+{
+ struct sync_cb_data cb_data;
+ struct nfsfh *nfsfh = (struct nfsfh *)fi->fh;
+
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_release entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ nfs_close_async(nfs, nfsfh, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return 0;
+}
+
+static void
+read_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct sync_cb_data *cb_data = static_cast(private_data);
+
+ cb_data->is_finished = 1;
+ cb_data->status = status;
+
+ if (status < 0) {
+ log_error("read_cb status:%d", status);
+ return;
+ }
+ memcpy(cb_data->return_data, data, status);
+}
+
+static int
+fuse_nfs_read(const char *path, char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct nfsfh *nfsfh = (struct nfsfh *)fi->fh;
+ struct sync_cb_data cb_data;
+ int ret;
+
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_read entered [%s] offset %lx", path, offset);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ cb_data.return_data = buf;
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_pread_async(nfs, nfsfh, offset, size, read_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_info("nfs_pread_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct nfsfh *nfsfh = (struct nfsfh *)fi->fh;
+ struct sync_cb_data cb_data;
+ int ret;
+
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_write entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_pwrite_async(nfs, nfsfh, offset, size, discard_const(buf),
+ generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_info("nfs_pwrite_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
+{
+ struct sync_cb_data cb_data;
+ int ret = 0;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_create entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_creat_async(nfs, path, mode, open_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_info("nfs_creat_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ fi->fh = (uint64_t)cb_data.return_data;
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_utime(const char *path, struct utimbuf *times)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_utime entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_utime_async(nfs, path, times, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("fuse_nfs_utime returned %d. %s", ret,
+ nfs_get_error(nfs));
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_unlink(const char *path)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_unlink entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_unlink_async(nfs, path, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_unlink_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_rmdir(const char *path)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_mknod entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_rmdir_async(nfs, path, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_rmdir_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int
+fuse_nfs_mkdir(const char *path, mode_t mode)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_mkdir entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_mkdir_async(nfs, path, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_mkdir_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ cb_data.is_finished = 0;
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_chmod_async(nfs, path, mode, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_mkdir_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_mknod entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_mknod_async(nfs, path, mode, rdev, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_mknod_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_symlink(const char *from, const char *to)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_symlink entered [%s -> %s]", from, to);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_symlink_async(nfs, from, to, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_symlink_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_rename(const char *from, const char *to)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_rename entered [%s -> %s]", from, to);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_rename_async(nfs, from, to, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_rename_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int
+fuse_nfs_link(const char *from, const char *to)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_link entered [%s -> %s]", from, to);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_link_async(nfs, from, to, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_link_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int
+fuse_nfs_chmod(const char *path, mode_t mode)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_chmod entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_chmod_async(nfs, path, mode, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_chmod_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_chown(const char *path, uid_t uid, gid_t gid)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_chown entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_chown_async(nfs, path,
+ map_reverse_uid(uid), map_reverse_gid(gid),
+ generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_chown_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int fuse_nfs_truncate(const char *path, off_t size)
+{
+ struct sync_cb_data cb_data;
+ int ret;
+
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_truncate entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_truncate_async(nfs, path, size, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_truncate_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static int
+fuse_nfs_fsync(const char *path, int isdatasync,
+ struct fuse_file_info *fi)
+{
+ struct nfsfh *nfsfh = (struct nfsfh *)fi->fh;
+ struct sync_cb_data cb_data;
+ int ret;
+
+ //log_info("fuse_nfs_fsync entered [%s]", path);
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+
+ pthread_mutex_lock(&nfs_mutex);
+ update_rpc_credentials();
+ ret = nfs_fsync_async(nfs, nfsfh, generic_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_fsync_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ return cb_data.status;
+}
+
+static void
+statvfs_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+ struct sync_cb_data *cb_data = static_cast(private_data);
+
+ cb_data->is_finished = 1;
+ cb_data->status = status;
+
+ if (status < 0) {
+ log_error("statvfs_cb failed with %d", status);
+ return;
+ }
+ memcpy(cb_data->return_data, data, sizeof(struct statvfs));
+}
+
+static int
+fuse_nfs_statfs(const char *path, struct statvfs* stbuf)
+{
+ int ret;
+ struct statvfs svfs;
+
+ struct sync_cb_data cb_data;
+
+ if(fuse_debug_flag)
+ log_debug("fuse_nfs_statfs entered [%s]", path);
+
+ memset(&cb_data, 0, sizeof(struct sync_cb_data));
+ cb_data.return_data = &svfs;
+
+ pthread_mutex_lock(&nfs_mutex);
+ ret = nfs_statvfs_async(nfs, path, statvfs_cb, &cb_data);
+ pthread_mutex_unlock(&nfs_mutex);
+ if (ret < 0) {
+ log_error("nfs_statvfs_async failed with %d", ret);
+ return ret;
+ }
+ wait_for_nfs_reply(nfs, &cb_data);
+
+ stbuf->f_bsize = svfs.f_bsize;
+ stbuf->f_frsize = svfs.f_frsize;
+ stbuf->f_fsid = svfs.f_fsid;
+ stbuf->f_flag = svfs.f_flag;
+ stbuf->f_namemax = svfs.f_namemax;
+ stbuf->f_blocks = svfs.f_blocks;
+ stbuf->f_bfree = svfs.f_bfree;
+ stbuf->f_bavail = svfs.f_bavail;
+ stbuf->f_files = svfs.f_files;
+ stbuf->f_ffree = svfs.f_ffree;
+ stbuf->f_favail = svfs.f_favail;
+
+
+
+ return cb_data.status;
+}
+
+static struct fuse_operations nfs_oper = {
+ .chmod = fuse_nfs_chmod,
+ .chown = fuse_nfs_chown,
+ .create = fuse_nfs_create,
+ .fsync = fuse_nfs_fsync,
+ .getattr = fuse_nfs_getattr,
+ .link = fuse_nfs_link,
+ .mkdir = fuse_nfs_mkdir,
+ .mknod = fuse_nfs_mknod,
+ .open = fuse_nfs_open,
+ .read = fuse_nfs_read,
+ .readdir = fuse_nfs_readdir,
+ .readlink = fuse_nfs_readlink,
+ .release = fuse_nfs_release,
+ .rmdir = fuse_nfs_rmdir,
+ .unlink = fuse_nfs_unlink,
+ .utime = fuse_nfs_utime,
+ .rename = fuse_nfs_rename,
+ .symlink = fuse_nfs_symlink,
+ .truncate = fuse_nfs_truncate,
+ .write = fuse_nfs_write,
+ .statfs = fuse_nfs_statfs,
+ //.access = fuse_nfs_access,
+};
+
+bool fuse_debug_flag = false;
+
+int fuse_ret = -1;
+
+int initialize_userland_fuse(const char* mp, const char* ip)
+{
+ log_info("Starting FUSE ...");
+ sceKernelSetBesteffort(ULPMGR_THREAD_PRIO_BASE, 255); // set best cpu effort to highest for IO
+
+ if(!if_exists("/dev/mira")){
+ log_info("fuse: Mira not found");
+ if(!fuse_fw_supported())
+ return FUSE_FW_NOT_SUPPORTED;
+ }
+ else
+ log_debug("fuse: Mira found, no patches will be applied");
+
+ struct fuse_actions args = { args.argc = 16 };
+ snprintf(args.path, sizeof(args.path)-1, "%s", mp);
+ snprintf(args.ip, sizeof(args.ip)-1, "%s", ip);
+ args.is_debug_mode = fuse_debug_flag;
+
+ unlink("/data/itemzflow_daemon/libfuse.log");
+ fuse_ret = fuse_nfs_main(args);
+ log_info("fuse: fuse_main returned %d", fuse_ret);
+ return fuse_ret;
+}
+
+void *fuse_main_init(void *args)
+{
+ struct fuse_actions *fuse_args = (struct fuse_actions *)args;
+ int ret;
+
+ if(fuse_args->is_debug_mode)
+ log_debug("fuse_main_init DEBUG entered %d", fuse_args->argc);
+ else
+ fuse_args->argc--;
+
+ const char* argv[] = {
+ "dummy",
+ fuse_args->path,
+ "-o",
+ "allow_other",
+ "-o", "rw",
+ "-o", "use_ino",
+ "-o", "direct_io",
+ "-o", "attr_timeout=0.0",
+ "-o", "max_read=262144",
+ "-f", "-d", NULL
+ };
+
+ ret = fuse_main(fuse_args->argc, const_cast(argv), &nfs_oper, NULL);
+ if (ret < 0) {
+ log_error("fuse_main failed");
+ }
+
+ delete fuse_args;
+ log_info("fuse_main_init exited with status %d, err %s", ret, strerror(errno));
+
+ return NULL;
+}
+
+int fatal_fuse_error(int ret, struct nfs_url *urls )
+{
+ nfs_destroy_url(urls);
+ if (nfs)
+ nfs_destroy_context(nfs);
+ log_error("Failed to mount nfs share : %i", ret);
+ return ret;
+}
+
+int fuse_nfs_main(struct fuse_actions args)
+{
+
+ int ret = 0;
+ struct nfs_url *urls = NULL;
+ //fuse_thread
+ ScePthread fuse_thread;
+
+ mount_user_uid=getuid();
+ mount_user_gid=getgid();
+
+ char nfs_ip[0x100];
+ if (strncmp(args.ip, "nfs://", 6) == 0) {
+ log_debug("String is in NFS format.");
+ snprintf(nfs_ip, sizeof(nfs_ip)-1, "%s", args.ip);
+ } else {
+ log_debug("String is not in NFS format.");
+ snprintf(nfs_ip, sizeof(nfs_ip)-1, "nfs://%s/", args.ip);
+ }
+
+ log_info("NFS [%s]", nfs_ip);
+
+ nfs = nfs_init_context();
+ if (nfs == NULL) {
+ log_error("Failed to init NFS context");
+ ret = 10;
+ return fatal_fuse_error(ret, urls);
+ }
+
+ urls = nfs_parse_url_dir(nfs, nfs_ip);
+ if (urls == NULL) {
+ log_error("Failed to parse url : %s", nfs_get_error(nfs));
+ ret = 10;
+ return fatal_fuse_error(ret, urls);
+ }
+
+ ret = nfs_mount(nfs, urls->server, urls->path);
+ if (ret != 0) {
+ log_error("Failed to mount nfs share : %s", nfs_get_error(nfs));
+ return fatal_fuse_error(ret, urls);
+ }
+
+ struct fuse_actions *fa = new fuse_actions;
+ fa->argc = args.argc;
+ fa->is_debug_mode = args.is_debug_mode;
+ snprintf(fa->path, sizeof(fa->path)-1, "%s", args.path);
+
+ if(fa->is_debug_mode)
+ log_debug("Starting fuse_main in debug mode");
+
+ scePthreadCreate(&fuse_thread, NULL, fuse_main_init, fa, "FUSE_Main_Thread");
+
+ return 0;
+}
\ No newline at end of file
diff --git a/itemz-daemon/source/fuse.c b/itemz-daemon/source/fuse.c
index a05599f..1ede3ac 100644
--- a/itemz-daemon/source/fuse.c
+++ b/itemz-daemon/source/fuse.c
@@ -10,21 +10,39 @@
#include
#include
#include