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 -#define FUSE_USE_VERSION 26 -#include "fuse/fuse.h" #include "defines.h" #include #include #include -int fuse_ret = -1; -int sceKernelSetBesteffort(int besteffort, int priority); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvisibility" + // NOTE that max_read will be ignored when mounting so if you want to change it then you // also have to change the fuse mounting func //DEBUG //char* argv[] = { "dummy", "/host", "-o", "allow_other", "-o", "rw", "-o", "use_ino", "-o", "direct_io", "-o", "attr_timeout=0.0", "-o", "max_read=262144", "-f", "-d", NULL }; // NON-DEBUG //char* argv[] = { "dummy", "/hostapp", "-o", "allow_other", "-o", "rw", "-o", "use_ino", "-o", "direct_io", "-o", "attr_timeout=0.0", "-o", "max_read=262144", "-f", NULL }; + +#include + +uint32_t sdkVersion = -1; + +uint32_t ps4_fw_version(void) +{ + //cache the FW Version + if (0 < sdkVersion) { + size_t len = 4; + // sysKernelGetLowerLimitUpdVersion == machdep.lower_limit_upd_version + // rewrite of sysKernelGetLowerLimitUpdVersion + sysctlbyname("machdep.lower_limit_upd_version", &sdkVersion, &len, NULL, 0); + } + + // FW Returned is in HEX + return (sdkVersion >> 16); +} + int fuse_kernel_patches_505(struct thread *td) { @@ -32,7 +50,7 @@ int fuse_kernel_patches_505(struct thread *td) uint8_t* kernel_ptr = (uint8_t*)kernel_base; int *ksuser_enabled=(int *)(kernel_base+0x2300B88); if(*ksuser_enabled == 1) // kernel already patched - return NULL; + return 0; cpu_disable_wp(); struct vfsconf *p=(struct vfsconf *)(kernel_base + 0x19FC380 ); @@ -44,6 +62,7 @@ int fuse_kernel_patches_505(struct thread *td) kernel_ptr[0x3B219E]=0; kernel_ptr[0x49DFE4] = 0x0F; kernel_ptr[0x49DFE5] = 0x84; + //skip devkit/testkit/dipsw check in fuse_loader kernel_ptr[0x49DDDE] = 0xEB; kernel_ptr[0x49DDDF] = 0x1B; @@ -63,7 +82,7 @@ int fuse_kernel_patches_505(struct thread *td) // skip sceSblACMgrIsSystemUcred check in fuse_vfsop_statfs kernel_ptr[0x4A3BED] = 0xEB; kernel_ptr[0x4A3BEE] = 0x04; - kernel_ptr[0x4A27FC]=0xB6; + kernel_ptr[0x4A27FC]= 0xB6; // patch kernel cpu_enable_wp(); @@ -71,7 +90,7 @@ int fuse_kernel_patches_505(struct thread *td) fuse_loader(NULL, 0, NULL); - return NULL; + return 0; } int fuse_kernel_patches_900(struct thread *td) @@ -82,7 +101,7 @@ int fuse_kernel_patches_900(struct thread *td) int *ksuser_enabled=(int *)(kernel_base+0x01B94588); //0x01B94588 if(*ksuser_enabled == 1) // kernel already patched - return NULL; + return 0; cpu_disable_wp(); struct vfsconf *p=(struct vfsconf *)(kernel_base + 0x01A7F188 ); //0x01A7F188 @@ -118,16 +137,70 @@ int fuse_kernel_patches_900(struct thread *td) int (*fuse_loader)(void* m, int op, void* arg)=(void *)(kernel_base + 0x0490720); //0x0490720 fuse_loader(NULL, 0, NULL); - return NULL; + return 0; } +int fuse_kernel_patches_672(struct thread *td) +{ + + void* kernel_base = &((uint8_t*)kernelRdmsr(0xC0000082))[-0x1C0]; + uint8_t* kernel_ptr = (uint8_t*)kernel_base; + int *ksuser_enabled=(int *)(kernel_base+0x1be9498); //0x1BE9498 + + if(*ksuser_enabled == 1) // kernel already patched + return 0; + + cpu_disable_wp(); + struct vfsconf *p=(struct vfsconf *)(kernel_base + 0x01aa0a68 ); //0x01A7F188 + //suser_enabled in priv_check_cred + *ksuser_enabled=1; + //add jail friendly for fuse file system + p->vfc_flags = 0x00400000 | 0x00080000; + //avoid enforce_dev_perms checks default prison_priv_check to 0 + kernel_ptr[0x270e8e] = 0; + kernel_ptr[0x4b0c74] = 0x0F; + kernel_ptr[0x4b0c75] = 0x84; + //skip devkit/testkit/dipsw check in fuse_loader + kernel_ptr[0x4af2fe] = 0xEB; + kernel_ptr[0x4af2ff] = 0x1B; + //skip sceSblACMgrIsSyscoreProcess check in fuse_open_device + kernel_ptr[0x4aeb14] = 0xEB; + kernel_ptr[0x4aeb15] = 0x0; + //skip sceSblACMgrIsDebuggerProcess/sceSblACMgrIsSyscoreProcess check in fuse_close_device + kernel_ptr[0x4aec12] = 0xEB; + //skip sceSblACMgrIsDebuggerProcess/sceSblACMgrIsSyscoreProcess check in fuse_poll_device + kernel_ptr[0x4AF174] = 0xEB; + // skip sceSblACMgrIsSyscoreProcess check in fuse_vfsop_mount + kernel_ptr[0x4ac086] = 0xEB; + kernel_ptr[0x4ac087] = 0x04; + // skip sceSblACMgrIsMinisyscore/unknown check in fuse_vfsop_unmount + kernel_ptr[0x4ac7dc] = 0xEB; + kernel_ptr[0x4ac7dd] = 0x00; + // skip sceSblACMgrIsSystemUcred check in fuse_vfsop_statfs + kernel_ptr[0x4acb8d] = 0xEB; + kernel_ptr[0x4acb8e] = 0x04; + kernel_ptr[0x4aea2c] = 0xB6; + // patch kernel + cpu_enable_wp(); + + int (*fuse_loader)(void* m, int op, void* arg)=(void *)(kernel_base + 0x4af2d0); //0x0490720 + fuse_loader(NULL, 0, NULL); + + return 0; +} + + bool fuse_fw_supported() { switch (ps4_fw_version()) { + case 0x507: case 0x505: syscall(11, fuse_kernel_patches_505); return true; + case 0x672: + syscall(11, fuse_kernel_patches_672); + return true; case 0x900: syscall(11, fuse_kernel_patches_900); return true; @@ -137,29 +210,3 @@ bool fuse_fw_supported() } } } - -int initialize_userland_fuse(const char* mp) -{ - 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", fuse_session_ip); - args.is_debug_mode = fuse_debug_flag; - - free(fuse_session_ip), fuse_session_ip = NULL; - - unlink("/data/itemzflow_daemon/libfuse.log"); - fuse_ret = fuse_nfs_main(args); - log_info("fuse: fuse_main returned %d", fuse_ret); - return fuse_ret; -} \ No newline at end of file diff --git a/itemz-daemon/source/log.cpp b/itemz-daemon/source/log.cpp new file mode 100644 index 0000000..d2affbe --- /dev/null +++ b/itemz-daemon/source/log.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2020 rxi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "log.h" +#include + +#define MAX_CALLBACKS 32 + +typedef struct { + log_LogFn fn; + void* udata; + int level; +} Callback; + +static struct { + void* udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; +} L; + + +static const char* level_strings[] = { + "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" +}; + +#ifdef LOG_USE_COLOR +static const char* level_colors[] = { + "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" +}; +#endif + +//#define __ORBIS__ 1 + +#ifdef __ORBIS__ +#define DGB_CHANNEL_TTYL 0 +#endif + + + static void stdout_callback(log_Event* ev) { + char buf[16]; + char format_buf[400]; + char print_buf[400]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; + + vsnprintf(&format_buf[0], 399, ev->fmt, ev->ap); + + snprintf(&print_buf[0], 399, "%s %-5s %s:%d: %s\n", buf, level_strings[ev->level], ev->file, ev->line, &format_buf[0]); + +#ifdef __ORBIS__ + sceKernelDebugOutText(DGB_CHANNEL_TTYL, &print_buf[0]); +#else + log_info("%s\n" &print_buf[0]); +#endif + } + + + +static void file_callback(log_Event* ev) { + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf( + (FILE*)ev->udata, "%s %-5s %s:%d: ", + buf, level_strings[ev->level], ev->file, ev->line); + vfprintf((FILE *)ev->udata, ev->fmt, ev->ap); + fprintf((FILE *)ev->udata, "\n"); + fflush((FILE *)ev->udata); +} + + +static void lock(void) { + if (L.lock) { L.lock(true, L.udata); } +} + + +static void unlock(void) { + if (L.lock) { L.lock(false, L.udata); } +} + + +const char* log_level_string(int level) { + return level_strings[level]; +} + + +void log_set_lock(log_LockFn fn, void* udata) { + L.lock = fn; + L.udata = udata; +} + + +void log_set_level(int level) { + L.level = level; +} + + +void log_set_quiet(bool enable) { + L.quiet = enable; +} + + +int log_add_callback(log_LogFn fn, void* udata, int level) { + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback){ fn, udata, level }; + return 0; + } + } + return -1; +} + + +int log_add_fp(FILE* fp, int level) { + return log_add_callback(file_callback, fp, level); +} + + +static void init_event(log_Event* ev, void* udata) { + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime(&t); + } + ev->udata = udata; +} + + +void log_log(int level, const char* file, int line, const char* fmt, ...) { + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback* cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } + } + + unlock(); +} \ No newline at end of file diff --git a/itemz-daemon/source/main.cpp b/itemz-daemon/source/main.cpp new file mode 100644 index 0000000..fb8d4d9 --- /dev/null +++ b/itemz-daemon/source/main.cpp @@ -0,0 +1,236 @@ +#include "defines.h" +#include +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net.hpp" +#include "utils.hpp" +#include + +bool IS_ERROR(uint32_t a1) +{ + return a1 & 0x80000000; +} + +bool is_home_redirect_enabled = false; +bool critical_suspend = false; + +// bool critical_suspend2 = false; +// int pid = 0; +void PS_Button_loop() +{ + OrbisUserServiceInitializeParams params; + memset(¶ms, 0, sizeof(params)); + params.priority = 256; + int userId = -1; + log_info(" sceUserServiceInitialize %x", sceUserServiceInitialize((int*)¶ms)); + log_info(" sceUserServiceGetForegroundUser %x", sceUserServiceGetForegroundUser(&userId)); + + int ret = scePadInit(); + if (ret < 0) + log_info(" %s scePadInit return error 0x%8x", __FUNCTION__, ret); + + + log_info(" scePadSetProcessPrivilege %x", scePadSetProcessPrivilege(1)); + + + int pad = scePadOpen(userId, 0, 0, NULL); + if (pad < 0) + log_info(" %s scePadOpen return error 0x%8x", __FUNCTION__, pad); + else + log_info(" Opened Pad"); + + + ScePadData data; + + log_info(" l1 %x", sceLncUtilInitialize()); + + LncAppParam param; + param.sz = sizeof(LncAppParam); + param.user_id = userId; + param.app_opt = 0; + param.crash_report = 0; + param.check_flag = SkipSystemUpdateCheck; + log_info(" Home menu Boot Option SkipSystemUpdateCheck Applied"); + log_info(" Main Loop Started"); + + while(pad > 0) + { + if(isRestMode()){ + log_info(" Rest Mode Detected shutting down ****"); + notify("Rest Mode Detected, Shutting Down Daemon..."); + raise(SIGQUIT); + } + else if (is_home_redirect_enabled || critical_suspend) + { + //get sample size + scePadReadState(pad, &data); + + if (data.buttons & PS_BUTTON && is_home_redirect_enabled) {//PS_BUTTON from RE + + log_info(" PS BUTTON Was Pressed & intercepted"); + log_info(" Redirecting Home Menu to ItemzCore"); + + uint32_t sys_res = sceLncUtilLaunchApp(HOME_TITLE_ID, 0, ¶m); + if (IS_ERROR(sys_res)) { + if (sys_res == SCE_LNC_UTIL_ERROR_ALREADY_RUNNING || sys_res == SCE_LNC_UTIL_ERROR_ALREADY_RUNNING_SUSPEND_NEEDED || sys_res == SCE_LNC_UTIL_ERROR_ALREADY_RUNNING_KILL_NEEDED) { + log_info(" ITEMzFlow Already running, resuming App...."); + log_info(" Redirect Successful"); + } + else + log_info(" launch ret 0x%x", sys_res); + } + else + log_info(" Redirect Successful"); + + } + + if(critical_suspend){ + int BigAppid = sceSystemServiceGetAppIdOfBigApp(); + if ((BigAppid & ~0xFFFFFF) == 0x60000000) { + log_info(" FOUND Open Game: %x", BigAppid); + log_info(" Suspending Open Game"); + sceLncUtilLaunchApp(HOME_TITLE_ID, 0, ¶m); + critical_suspend = false; + } + } + } + + usleep(15000); + } +} + +void *Game_Stats_thread(void* args){ + + char tid[16]; + int retries = 0; + int seconds_past= 0; + + log_info(" Game_Stats Thread Started"); + log_info(" sceLncUtilInitialize %x", sceLncUtilInitialize()); + +failed_timeout: + if(!if_exists(STAT_DB)){ + log_info(" Creating Stat DB"); + if(create_stat_db(STAT_DB)) + log_info(" Stat DB Created"); + else{ + log_info("[FATAL] Stat DB Creation Failed"); + goto err; + } + } + else + log_info(" Stat DB Exists"); + + if(open_stat_db(STAT_DB)) + log_info(" Stat DB Opened"); + else{ + log_info("[FATAL] Stat DB Open Failed"); + goto err; + } + + while (1){ + + if(retries > 4){ + retries = 0; + goto failed_timeout; + } + + //make sure app still exists + //if(!if_exists("/user/app/ITEM00001/")){ + // notify("ItemzFlow Not Installed!, Daemon Exiting..."); + //raise(SIGQUIT); + //} + + check_ftp_addr_change(); + + int BigAppid = sceSystemServiceGetAppIdOfBigApp(); + // IS Big App Running? + if ((BigAppid & ~0xFFFFFF) == 0x60000000){ + memset(tid, 0, 16); + // Get TID of Big App + if(sceLncUtilGetAppTitleId(BigAppid, &tid[0]) != 0) + log_info(" failed to get tid"); + else { + if(!update_game_time_played(&tid[0])) + log_info(" failed to update game time playe retry: %i", retries++); + else { + log_info(" Game Time Played Updated for 0x%08x", BigAppid); + sleep(59); //sleep for 59 Secs if everything successful + seconds_past += 59; //sleep for 60 Secs if everything successful + } + } + } + + sleep(1); + seconds_past += 1; + if(seconds_past >= 60 * 5) { + seconds_past = 0; + print_memory(); + } + + } + +err: + notify("Failed, Game Stats will be Unavailable"); + pthread_exit(NULL); + return NULL; + +} +int main(int argc, char* argv[]) +{ + ScePthread thread, stats_thr;//, fuse_thread; + // internals: net, user_service, system_service + loadModulesVanilla(); + + if (!jailbreak("/app0/Media/jb.prx")) goto exit; + + if (!full_init()) goto exit; + + log_info(" Registering Daemon..."); + + sceSystemServiceRegisterDaemon(); + + log_info("Starting SQL Services..."); + log_info("SQL library Version: %s", sqlite3_libversion()); + log_info("Started SQL Services "); + + log_info(" Starting IPC loop Thread..."); + scePthreadCreate(&thread, NULL, IPC_loop, NULL, "IPC_loop_thread"); + + log_info(" Starting Game Stats Thread..."); + scePthreadCreate(&stats_thr, NULL, Game_Stats_thread, NULL, "Game_Stats_Thread"); + + //log_info(" Starting Resource Monitor Thread..."); + //scePthreadCreate(&up_thread, NULL, resource_monitor_thread, NULL, "Resource_Monitor_Thread"); + + log_info(" Starting FTP & Infinix..."); + if(!StartFTP()) + log_error(" Failed to Start FTP & Infinix"); + else + log_info(" FTP Started Successfully"); +// + log_info(" Starting main PS Button loop..."); + PS_Button_loop(); + + log_info(" WTF the Loop eneded???"); + + +exit: + log_info(" Exiting"); + if (!rejail()) + log_info(" rejail failed"); + + sceKernelIccSetBuzzer(2); + return sceSystemServiceLoadExec("exit", 0); +} \ No newline at end of file diff --git a/itemz-daemon/source/md5.cpp b/itemz-daemon/source/md5.cpp new file mode 100644 index 0000000..a374ca3 --- /dev/null +++ b/itemz-daemon/source/md5.cpp @@ -0,0 +1,294 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#ifndef HAVE_OPENSSL + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them in a + * properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned memory + * accesses is just an optimization. Nothing will break if it fails to detect + * a suitable architecture. + * + * Unfortunately, this optimization may be a C strict aliasing rules violation + * if the caller's data buffer has effective type that cannot be aliased by + * MD5_u32plus. In practice, this problem may occur if these MD5 routines are + * inlined into a calling function, or with future and dangerously advanced + * link-time optimizations. For the time being, keeping these MD5 routines in + * their own translation unit avoids the problem. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + + + void* unused; + +/* + * This processes one or more 64-byte data blocks, but does NOT update the bit + * counters. There are no alignment requirements. + */ +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void MD5_Init(MD5_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + unused = memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +#define OUT(dst, src) \ + (dst)[0] = (unsigned char)(src); \ + (dst)[1] = (unsigned char)((src) >> 8); \ + (dst)[2] = (unsigned char)((src) >> 16); \ + (dst)[3] = (unsigned char)((src) >> 24); + +void MD5_Final(unsigned char *result, MD5_CTX *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + unused = memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + unused = memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + OUT(&ctx->buffer[56], ctx->lo) + OUT(&ctx->buffer[60], ctx->hi) + + body(ctx, ctx->buffer, 64); + + OUT(&result[0], ctx->a) + OUT(&result[4], ctx->b) + OUT(&result[8], ctx->c) + OUT(&result[12], ctx->d) + + unused =memset(ctx, 0, sizeof(*ctx)); +} + +#endif \ No newline at end of file diff --git a/itemz-daemon/source/net.cpp b/itemz-daemon/source/net.cpp new file mode 100644 index 0000000..9f51e0b --- /dev/null +++ b/itemz-daemon/source/net.cpp @@ -0,0 +1,150 @@ +#include "defines.h" +#include +#include +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net.hpp" +#include +#include + +#define INVAIL -1 +#define IPC_SOC "/system_tmp/IPC_Socket" +int DaemonSocket = 0; + +struct sockaddr_in networkAdress(uint16_t port) +{ + struct sockaddr_in address; + address.sin_len = sizeof(address); + address.sin_family = AF_INET; + address.sin_port = sceNetHtons(port); + memset(address.sin_zero, 0, sizeof(address.sin_zero)); + return address; +} + +int networkListen(const char* soc_path) +{ + struct sockaddr_un server; + unlink(soc_path); + log_info("[Daemon] Deleted Socket..."); + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + { + log_info("[Daemon] Socket failed! %s", strerror(errno)); + return INVAIL; + } + + memset(&server, 0, sizeof(server)); + server.sun_family = AF_UNIX; + strcpy(server.sun_path, soc_path); + + int r = bind(s, (struct sockaddr*)&server, SUN_LEN(&server)); + if (r < 0) + { + log_info("[Daemon] Bind failed! %s", strerror(errno)); + return INVAIL; + } + + log_info("Socket has name %s", server.sun_path); + + r = listen(s, 100); + if (r < 0) + { + log_info("[Daemon] listen failed! %s", strerror(errno)); + return INVAIL; + } + + return s; +} + +int networkAccept(int socket) +{ + touch_file("/system_tmp/IPC_init"); + return accept(socket, 0, 0); +} + + +int networkReceiveData(int socket, void* buffer, int32_t size) +{ + int nu = recv(socket, buffer, size, 0); + log_info("got %i bytes", nu); + return nu; +} + +int networkSendData(int socket, void* buffer, int32_t size) +{ + return send(socket, buffer, size, MSG_NOSIGNAL); +} + +int networkSendDebugData(void* buffer, int32_t size) +{ + return networkSendData(DaemonSocket, buffer, size); +} + +int networkCloseConnection(int socket) +{ + return close(socket); +} + +int networkCloseDebugConnection() +{ + return networkCloseConnection(DaemonSocket); +} + +bool touch_file(const char* destfile) +{ + int fd = open(destfile, O_WRONLY | O_CREAT | O_TRUNC, 0777); + if (fd > 0) { + close(fd); + return true; + } + else + return false; +} + +void *IPC_loop(void* args) { + // Listen on port + int serverSocket = networkListen(IPC_SOC); + if (serverSocket < 0) { + log_debug("[Daemon IPC] networkListen error %s", strerror(errno)); + return nullptr; + } + + // Keep accepting client connections + int cli_new = 0; + while (true) { + // Accept a client connection + int clientSocket = networkAccept(serverSocket); + if (clientSocket < 0) { + log_debug("[Daemon IPC] networkAccept error %s", strerror(errno)); + break; // Breaking out of the loop on error to cleanup + } + + log_debug("[Daemon IPC] Connection Accepted"); + log_info("[Daemon IPC] cl_nmb %i", cli_new); + + // Build data to send to thread + auto clientParams = new clientArgs(); + clientParams->ip = "localhost"; + clientParams->socket = clientSocket; + clientParams->cl_nmb = cli_new; + + log_info("[Daemon IPC] clientParams->cl_nmb %i", clientParams->cl_nmb); + + // Handle client on a thread + ScePthread thread; + scePthreadCreate(&thread, NULL, ipc_client, (void*)clientParams, "IPC_SERVER_THREAD"); + cli_new++; + } + + // Cleanup + networkCloseConnection(serverSocket); + return nullptr; +} \ No newline at end of file diff --git a/itemz-daemon/source/rpc.cpp b/itemz-daemon/source/rpc.cpp new file mode 100644 index 0000000..a2a88d9 --- /dev/null +++ b/itemz-daemon/source/rpc.cpp @@ -0,0 +1,318 @@ +#include "defines.h" +#include +#include +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +std::mutex ipc_mutex; + +void load_fuse_ip() { + std::ifstream file("/data/itemzflow_daemon/fuse_ip.txt"); + if (!file.is_open()) { + log_error("[FUSE SESSION] Failed to open fuse_ip.txt"); + return; + } + + std::getline(file, fuse_session_ip); + + if(fuse_session_ip.size() > 3){ + log_info(" Starting FUSE Thread IP %s", fuse_session_ip.c_str()); + if(initialize_userland_fuse("/hostapp", fuse_session_ip.c_str()) == NO_ERROR){ + log_info(" FUSE Thread Started!"); + notify("Connected to Network Share!"); + } + else{ + log_error("FUSE Thread Failed to Start!"); + notify("Failed to connect to Network Share!"); + } + + fuse_session_ip.clear(); + } +} + +IPC_Errors CONNECT_TEST(std::string& mode) { + mode = "Connected"; + return NO_ERROR; +} + +std::string fuse_session_ip; +extern bool is_home_redirect_enabled; +extern bool critical_suspend; +// extern bool critical_suspend2; + +int sys_kill(pid_t pid, int sig) +{ + return syscall(37, pid, sig); +} + +IPC_Errors get_game_mins(std::string& input) { + // Extract the title ID, skipping the command bit. + std::string titleId = input; + + int mins = get_game_time_played(titleId.c_str()); // Assuming this function still requires a C-style string + if(mins == -1) { + log_error("[ItemzDaemon] Failed to get game time played, tid: %s", titleId.c_str()); + input.clear(); + return OPERATION_FAILED; + } else { + log_info("[ItemzDaemon] Game time played for %s: %d", titleId.c_str(), mins); + } + + // Build IPC Response without the ERROR Bit + input.clear(); + input = std::to_string(mins); + + return NO_ERROR; +} + +IPC_Errors set_fuse_session_ip(const std::string& input) { + // Extract the IP, skipping the command bit. + std::string ip = input; + + if(fuse_session_ip.size() > 6) { + log_error("[FUSE SESSION] session ip already set"); + return FUSE_IP_ALREADY_SET; + } else { + fuse_session_ip = ip; + log_info("[FUSE SESSION] session ip set to %s", fuse_session_ip.c_str()); + } + + return NO_ERROR; +} + +IPC_Errors get_game_start(std::string& input) { + // Extract the game title ID, skipping the command bit. + std::string titleId = input; + + time_t date = get_game_start_date(titleId.c_str()); + if (date == 0) { + log_error("[ItemzDaemon] Failed to get game start date, tid: %s", titleId.c_str()); + return OPERATION_FAILED; + } else { + log_info("[ItemzDaemon] Game Start date for %s: %ld", titleId.c_str(), date); + } + + input.clear(); + input = std::to_string(static_cast(date)); + return NO_ERROR; +} + +bool isNumber(const std::string& str) { + return !str.empty() && std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isdigit(c); }); +} + +void handleIPC(struct clientArgs* client, std::string &inputStr, cmd command) { + std::lock_guard lock(ipc_mutex); + IPCMessage outputMessage; + outputMessage.cmd = command; + + int error = NO_ERROR; + switch (command) + { + case FUSE_SET_DEBUG_FLAG: + log_info("[Daemon IPC][client %i] FUSE_SET_DEBUG_FLAG command called", client->cl_nmb); + fuse_debug_flag = true; + log_info("[Daemon IPC][client %i] Debug FUSE Flag set", client->cl_nmb); + break; + case FUSE_SET_SESSION_IP: + error = set_fuse_session_ip(inputStr); + break; + /*case MACGUFFIN_CMD: + log_info("[Daemon IPC][client %i] MACGUFFIN command called", client->cl_nmb); + log_info(" Starting FUSE Thread..."); + scePthreadCreate(&fuse_thread, NULL, initialize_userland_fuse, NULL, "FUSE_Thread"); + break;*/ + case RESTART_FUSE_FS: + log_info("[Daemon IPC][client %i] RESTART_FUSE_FS command called", client->cl_nmb); + log_info(" Restarting FUSE & Daemon..."); + networkSendData(client->socket, reinterpret_cast(&outputMessage), sizeof(outputMessage)); + unlink("/system_tmp/IPC_init"); + unlink("/system_tmp/IPC_Socket"); + reboot_daemon = true; + raise(SIGQUIT); + break; + case FUSE_START_W_PATH:{ + + log_info("[Daemon IPC][client %i] FUSE_START_W_PATH command called", client->cl_nmb); + if(inputStr.size() < 3) { + log_error("[Daemon IPC][client %i] FUSE_START_W_PATH command called with invalid mountpoint", client->cl_nmb); + error = OPERATION_FAILED; + break; + } + + if(fuse_session_ip.size() < 6) { + log_error("[Daemon IPC][client %i] FUSE_START_W_PATH command called with no session ip set sz: %zu", client->cl_nmb, fuse_session_ip.size()); + error = FUSE_IP_NOT_SET; + break; + } + + log_info(" Starting FUSE Thread IP %s & mp %s", fuse_session_ip.c_str(), inputStr.c_str()); + if((error = initialize_userland_fuse(inputStr.c_str(), fuse_session_ip.c_str())) == NO_ERROR) { + log_info(" FUSE Thread Started!"); + save_fuse_ip(fuse_session_ip.c_str()); + } else { + log_error("FUSE Thread Failed to Start with code %i!", static_cast(error)); + } + + fuse_session_ip.clear(); // Clear the session IP string + break; + } + case GAME_GET_START_TIME: + error = get_game_start(inputStr); + break; + case RESTART_FTP_SERVICE: + ShutdownFTP(); + notify("FTP Restarting..."); + StartFTP(); + break; + case SHUTDOWN_DAEMON: + log_info("[Daemon IPC][client %i] command SHUTDOWN_DAEMON() called", client->cl_nmb); + //DumpHex(buffer.data(), 10); + notify("Daemon Shutting Down..."); + networkSendData(client->socket, reinterpret_cast(&outputMessage), sizeof(outputMessage)); + unlink("/system_tmp/IPC_init"); + unlink("/system_tmp/IPC_Socket"); + raise(SIGQUIT); + break; + case GAME_GET_MINS: + error = get_game_mins(inputStr); + break; + case CONNECTION_TEST: { + log_info("[Daemon IPC][client %i] command CONNECTION_TEST() called", client->cl_nmb); + error = CONNECT_TEST(inputStr); + break; + } + case DISABLE_HOME_REDIRECT: { + log_info("[Daemon IPC][client %i] command DISABLE_HOME_REDIRECT() called", client->cl_nmb); + is_home_redirect_enabled = false; + error = NO_ERROR; + break; + } + case ENABLE_HOME_REDIRECT: { + log_info("[Daemon IPC][client %i] command ENABLE_HOME_REDIRECT() called", client->cl_nmb); + is_home_redirect_enabled = true; + error = NO_ERROR; + break; + } + case DEAMON_UPDATE: { + log_info("[Daemon IPC][client %i] command DEAMON_UPDATE() called", client->cl_nmb); + log_info("[Daemon IPC][client %i] Reloading Daemon ...", client->cl_nmb); + log_info("Daemon Rejailed, Rebooting Daemon ..."); + networkSendData(client->socket, reinterpret_cast(&outputMessage), sizeof(outputMessage)); + reboot_daemon = true; + unlink("/system_tmp/IPC_init"); + unlink("/system_tmp/IPC_Socket"); + raise(SIGQUIT); + break; + } + case CRITICAL_SUSPEND: { + log_info("[Daemon IPC][client %i] command CRITICAL_SUSPEND() called", client->cl_nmb); + critical_suspend = true; + log_info("[Daemon] Critical Suspend activated"); + error = NO_ERROR; + break; + } + case INSTALL_IF_UPDATE:{ + // Terminate IF with SIGQUIT to re-jail it + //DumpHex(buffer.data(), 10); + + if (isNumber(inputStr)) { + // int pid = std::stoi(inputStr); + //sys_kill(pid, SIGQUIT); + } else { + log_error("[Daemon IPC][client %i] command INSTALL_IF_UPDATE() failed, invalid PID", client->cl_nmb); + error = OPERATION_FAILED; + break; + } + + log_info("[Daemon IPC][client %i] command INSTALL_IF_UPDATE() called", client->cl_nmb); + mkdir("/user/update_tmp", 0777); + mkdir("/user/update_tmp/covers", 0777); + mkdir("/user/update_tmp/custom_app_names", 0777); + copyRegFile("/user/app/ITEM00001/settings.ini", "/user/update_tmp/settings.ini"); + copy_dir("/user/app/ITEM00001/covers", "/user/update_tmp/covers"); + copy_dir("/user/app/ITEM00001/custom_app_names", "/user/update_tmp/custom_app_names"); + + //copy_dir("/user/update", "/user/update_tmp"); + long pkg_size = 0; + if(if_exists("/user/app/if_update.pkg") && (pkg_size = CalcAppsize("/user/app/if_update.pkg")) > 0){ + log_info("[Daemon] Update Return 0x%X | sz %i", pkginstall("/user/app/if_update.pkg", true), pkg_size); + log_debug("[Daemon] Copying files back ..."); + + copy_dir("/user/update_tmp/covers", "/user/app/ITEM00001/covers"); + copy_dir("/user/update_tmp/custom_app_names", "/user/app/ITEM00001/custom_app_names"); + copyRegFile("/user/update_tmp/settings.ini", "/user/app/ITEM00001/settings.ini"); + unlink("/user/app/if_update.pkg"); + unlink("/mnt/usb0/if_update.pkg"); + unlink("/mnt/usb1/if_update.pkg"); + unlink("/mnt/usb2/if_update.pkg"); + unlink("/user/app/if_update.pkg.sig"); + rmdir("/user/update_tmp"); + error = NO_ERROR; + } + else{ + log_info("[Daemon] No Update Found or 0 bytes Update"); + error = INVALID; + } + + break; + } + default:{// + log_info("[Daemon IPC][client %i] command %i called", client->cl_nmb, command); + error = INVALID; + break; + } + } + + outputMessage.error = (IPC_Errors)error; + + if (!inputStr.empty()) { + strncpy(outputMessage.msg, inputStr.c_str(), sizeof(outputMessage.msg) - 1); + // Null-terminate the destination array + outputMessage.msg[sizeof(outputMessage.msg) - 1] = '\0'; + } + + networkSendData(client->socket, reinterpret_cast(&outputMessage), sizeof(outputMessage)); +} + +void* ipc_client(void* args) +{ + struct clientArgs* client = (struct clientArgs*)args; + log_debug("[Daemon IPC] Thread created for Socket %i", client->socket); + + uint32_t readSize = 0; + IPCMessage ipcMessage; // Create an IPCMessage struct to store received data + + while ((readSize = networkReceiveData(client->socket, reinterpret_cast(&ipcMessage), sizeof(ipcMessage))) > 0) { + if (ipcMessage.magic == 0xDEADBABE) { + // Handle IPCMessage + std::string message = ipcMessage.msg; // Retrieve the string message + handleIPC(client, message, ipcMessage.cmd); + } else { + log_error("[Daemon IPC][client %i] Invalid magic number", client->cl_nmb); + } + } + + log_debug("[Daemon IPC][client %i] IPC Connection disconnected, Shutting down ...", client->cl_nmb); + networkCloseConnection(client->socket); + delete client; + scePthreadExit(NULL); + + return NULL; +} \ No newline at end of file diff --git a/itemz-daemon/source/utils.cpp b/itemz-daemon/source/utils.cpp new file mode 100644 index 0000000..baa3be5 --- /dev/null +++ b/itemz-daemon/source/utils.cpp @@ -0,0 +1,990 @@ +#include +#include +#include "log.h" +#include +#include +#include +#include +#include +#include +#include "jsmn.h" +#include +#include "md5.h" +#include +#include +#include + +sqlite3 *db; +#define SSL_HEAP_SIZE (304 * 1024) +#define HTTP_HEAP_SIZE (1024 * 1024) +#define NET_HEAP_SIZE (1 * 1024 * 1024) +#define TEST_USER_AGENT "Daemon/PS4" + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 02 + +const unsigned char completeVersion[] = { + VERSION_MAJOR_INIT, + '.', + VERSION_MINOR_INIT, + '-', + 'V', + '-', + BUILD_YEAR_CH0, + BUILD_YEAR_CH1, + BUILD_YEAR_CH2, + BUILD_YEAR_CH3, + '-', + BUILD_MONTH_CH0, + BUILD_MONTH_CH1, + '-', + BUILD_DAY_CH0, + BUILD_DAY_CH1, + 'T', + BUILD_HOUR_CH0, + BUILD_HOUR_CH1, + ':', + BUILD_MIN_CH0, + BUILD_MIN_CH1, + ':', + BUILD_SEC_CH0, + BUILD_SEC_CH1, + '\0' +}; + +#define DIFFERENT_HASH true +#define SAME_HASH false +#define UPDATE_NEEDED true +#define UPDATE_NOT_NEEDED false + + +static int (*jailbreak_me)(void) = NULL; +static int (*rejail_multi)(void) = NULL; + +int64_t sys_dynlib_load_prx(char* prxPath, int* moduleID) +{ + return (int64_t)syscall4(594, prxPath, 0, moduleID, 0); +} + +int64_t sys_dynlib_unload_prx(int64_t prxID) +{ + return (int64_t)syscall1(595, (void*)prxID); +} + + +int64_t sys_dynlib_dlsym(int64_t moduleHandle, const char* functionName, void* destFuncOffset) +{ + return (int64_t)syscall3(591, (void*)moduleHandle, (void*)functionName, destFuncOffset); +} + + +int libjbc_module = 0; + +int PKG_ERROR(const char* name, int ret) +{ + log_error( "%s error: %i", name, ret); + return ret; +} + +void* prx_func_loader(const char* prx_path, const char* symbol) { + + void* addrp = NULL; + log_info("Loading %s", prx_path); + + int libcmi = sceKernelLoadStartModule(prx_path, 0, NULL, 0, 0, 0); + if(libcmi < 0){ + log_error("Error loading prx: 0x%X", libcmi); + return addrp; + } + else + log_debug("%s loaded successfully", prx_path); + + if(sceKernelDlsym(libcmi, symbol, &addrp) < 0){ + log_error("Symbol %s NOT Found", symbol); + } + else + log_debug("Function %s | addr %p loaded successfully", symbol, addrp); + + return addrp; +} + +/* we use bgft heap menagement as init/fini as flatz already shown at + * https://github.com/flatz/ps4_remote_pkg_installer/blob/master/installer.c + */ + +#define BGFT_HEAP_SIZE (1 * 1024 * 1024) + +static bool s_app_inst_util_initialized = false; +static bool s_bgft_initialized = false; +static struct bgft_init_params s_bgft_init_params; + +bool app_inst_util_init(void) { + int ret; + + if (s_app_inst_util_initialized) { + goto done; + } + + ret = sceAppInstUtilInitialize(); + if (ret) { + log_debug( "sceAppInstUtilInitialize failed: 0x%08X", ret); + goto err; + } + + s_app_inst_util_initialized = true; + +done: + return true; + +err: + s_app_inst_util_initialized = false; + + return false; +} + +void app_inst_util_fini(void) { + int ret; + + if (!s_app_inst_util_initialized) { + return; + } + + ret = sceAppInstUtilTerminate(); + if (ret) { + log_debug( "sceAppInstUtilTerminate failed: 0x%08X", ret); + } + + s_app_inst_util_initialized = false; +} + +bool bgft_init(void) { + int ret; + + if (s_bgft_initialized) { + goto done; + } + + memset(&s_bgft_init_params, 0, sizeof(s_bgft_init_params)); + { + s_bgft_init_params.heapSize = BGFT_HEAP_SIZE; + s_bgft_init_params.heap = (uint8_t*)malloc(s_bgft_init_params.heapSize); + if (!s_bgft_init_params.heap) { + log_debug( "No memory for BGFT heap."); + goto err; + } + memset(s_bgft_init_params.heap, 0, s_bgft_init_params.heapSize); + } + + ret = sceBgftServiceInit(&s_bgft_init_params); + if (ret) { + log_debug( "sceBgftInitialize failed: 0x%08X", ret); + goto err_bgft_heap_free; + } + + s_bgft_initialized = true; + +done: + return true; + +err_bgft_heap_free: + if (s_bgft_init_params.heap) { + free(s_bgft_init_params.heap); + s_bgft_init_params.heap = NULL; + } + + memset(&s_bgft_init_params, 0, sizeof(s_bgft_init_params)); + +err: + s_bgft_initialized = false; + + return false; +} + +void bgft_fini(void) { + int ret; + + if (!s_bgft_initialized) { + return; + } + + ret = sceBgftServiceTerm(); + if (ret) { + log_debug( "sceBgftServiceTerm failed: 0x%08X", ret); + } + + if (s_bgft_init_params.heap) { + free(s_bgft_init_params.heap); + s_bgft_init_params.heap = NULL; + } + + memset(&s_bgft_init_params, 0, sizeof(s_bgft_init_params)); + + s_bgft_initialized = false; +} +/* sample ends */ + + +/* install package wrapper: + init, (install), then clean AppInstUtil and BGFT + for next install */ + +uint32_t pkginstall(const char *path, bool is_if_update) +{ + int ret = -1; + int task_id = -1; + int is_app = false; + char title_id[30]; + SceBgftTaskProgress progress_info; + + if( if_exists(path) ) + { + log_info("Initializing AppInstUtil..."); + if (!app_inst_util_init()) { + log_debug( "AppInstUtil initialization failed."); + return -1; + } + log_info("Initializing BGFT..."); + if (!bgft_init()) { + log_debug( "BGFT initialization failed."); + return -1; + } + + struct bgft_download_param_ex download_params; + memset(&download_params, 0, sizeof(download_params)); + download_params.param.entitlement_type = 5; + download_params.param.id = ""; + download_params.param.content_url = path; + if(is_if_update) + download_params.param.content_name = "ItemzFlow Update"; + else + download_params.param.content_name = "PKG Installed remotely"; + + download_params.param.icon_path = "/update/fakepic.png"; + download_params.param.playgo_scenario_id = "0"; + download_params.param.option = BGFT_TASK_OPTION_DELETE_AFTER_UPLOAD; + download_params.slot = 0; + + ret = sceAppInstUtilGetTitleIdFromPkg(path, &title_id[0], &is_app); + if (ret) + return PKG_ERROR("sceAppInstUtilGetTitleIdFromPkg", ret); + +retry: + ret = sceBgftServiceIntDownloadRegisterTaskByStorageEx(&download_params, &task_id); + if(ret == 0x80990088 || ret == 0x80990015) + { + ret = sceAppInstUtilAppUnInstall(&title_id[0]); + if(ret != 0) + return PKG_ERROR("sceAppInstUtilAppUnInstall", ret); + + log_info("Uninstalled %s, retrying install...", &title_id[0]); + goto retry; + } + else if(ret) + return PKG_ERROR("sceBgftDownloadRegisterTaskByStorageEx", ret); + + log_info("Task ID(s): 0x%08X", task_id); + + ret = sceBgftServiceDownloadStartTask(task_id); + if(ret) + return PKG_ERROR("sceBgftDownloadStartTask", ret); + + int prog = 0; + while (prog < 99) { + memset(&progress_info, 0, sizeof(progress_info)); + + int ret = sceBgftServiceDownloadGetProgress(task_id, &progress_info); + if (ret) + return PKG_ERROR("sceBgftDownloadGetProgress", ret); + + prog = (uint32_t)(((float)progress_info.transferred / progress_info.length) * 100.f); + } + + } + else + return PKG_ERROR("no file at", ret); + + return 0; +} + + +bool rejail() +{ + int ret = sys_dynlib_dlsym(libjbc_module, "rejail_multi", &rejail_multi); + if (!ret) + { + log_info("[Daemon] rejail_multi resolved from PRX"); + + if ((ret = rejail_multi() != 0)) return false; + else + return true; + } + else + { + // fail con + log_debug("[Daemon] rejail_multi failed to resolve"); + return false; + } + + return false; +} + +bool jailbreak(const char* prx_path) +{ + sys_dynlib_load_prx((char*)prx_path, &libjbc_module); + int ret = sys_dynlib_dlsym(libjbc_module, "jailbreak_me", &jailbreak_me); + if (!ret) + { + log_info("[Daemon] jailbreak_me resolved from PRX"); + + if ((ret = jailbreak_me() != 0)) + { + log_debug("[Daemon] jailbreak_me returned %i", ret); + return false; + } + else + return true; + } + else + log_debug("[Daemon] jailbreak_me failed to resolve"); + + return false; +} + + +bool if_exists(const char* path) +{ + int dfd = open(path, O_RDONLY, 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 reboot_daemon = false; + +int sceKernelBacktraceSelf(callframe_t frames[], size_t numBytesBuffer, uint32_t *pNumReturn, int mode); + +void OrbisKernelBacktrace(const char* reason) { + char addr2line[MAX_STACK_FRAMES * 20]; + callframe_t frames[MAX_STACK_FRAMES]; + OrbisKernelVirtualQueryInfo info; + char buf[MAX_MESSAGE_SIZE + 3]; + unsigned int nb_frames = 0; + char temp[80]; + + memset(addr2line, 0, sizeof addr2line); + memset(frames, 0, sizeof frames); + memset(buf, 0, sizeof buf); + + snprintf(buf, sizeof buf, "<118>[Crashlog]: %s\n", reason); + + strncat(buf, "<118>[Crashlog]: Backtrace:\n", MAX_MESSAGE_SIZE); + sceKernelBacktraceSelf(frames, sizeof frames, &nb_frames, 0); + for (unsigned int i = 0; i < nb_frames; i++) { + memset(&info, 0, sizeof info); + sceKernelVirtualQuery(frames[i].pc, 0, &info, sizeof info); + + snprintf(temp, sizeof temp, + "<118>[Crashlog]: #%02d %32s: 0x%lx\n", + i + 1, info.name, frames[i].pc - info.unk01 - 1); + strncat(buf, temp, MAX_MESSAGE_SIZE); + + snprintf(temp, sizeof temp, + "0x%lx ", frames[i].pc - info.unk01 - 1); + strncat(addr2line, temp, sizeof addr2line - 1); + } + + strncat(buf, "<118>[Crashlog]: addr2line: ", MAX_MESSAGE_SIZE); + strncat(buf, addr2line, MAX_MESSAGE_SIZE); + strncat(buf, "\n", MAX_MESSAGE_SIZE); + + buf[MAX_MESSAGE_SIZE + 1] = '\n'; + buf[MAX_MESSAGE_SIZE + 2] = '\0'; + + log_error("%s", buf); +} + + +bool mount_procFS(){ + char profpath[150]; + + if(getuid() != 0){ + log_debug("Not root, skipping procfs mount"); + return false; + } + + snprintf(profpath, sizeof(profpath)-1, "/mnt/proc/%i/", getpid()); + + if (getuid() == 0 && !if_exists(profpath)) + { + int result = mkdir("/mnt/proc", 0777); + if (result < 0 && errno != EEXIST) + { + log_debug("Failed to create /mnt/proc"); + return false; + } + + result = mount("procfs", "/mnt/proc", 0, NULL); + if (result < 0 && errno != EBUSY) + { + log_debug("Failed to mount procfs: %s", strerror(errno)); + return false; + } + } + log_info("# Procfs mounted to %s", profpath); + return true; +} + + +void SIG_Handler(int sig_numb) +{ + // void* array[100]; + if(sig_numb != SIGQUIT){ + sceKernelIccSetBuzzer(2); + + log_debug("############# DAEMON HAS CRASHED with SIG %i ##########", sig_numb); + log_debug("# Thread ID: %i", pthread_getthreadid_np()); + log_debug("# PID: %i", getpid()); + log_debug("# UID: %i", getuid()); + + char buff[255]; + if (mount_procFS()) + { + FILE* fp = fopen("/mnt/proc/curproc/status", "r"); + if (fp == NULL) { + log_error("Failed to open file"); + goto bt; + } + if (fscanf(fp, "%254s", buff) != 1) { + log_error("Failed to read string from file"); + fclose(fp); + goto bt; + } + + log_debug("# Reading curproc..."); + log_debug("# Proc Name: %s", buff); + fclose(fp); + } +bt: + log_debug("################### Backtrace ########################"); + //backtrace(array, 100); + OrbisKernelBacktrace("SIG_Handler"); + notify("ItemzDaemon has crashed, exiting..."); + } + + rejail(); + if(reboot_daemon) + sceSystemServiceLoadExec("/app0/eboot.bin", 0); + else + sceSystemServiceLoadExec("exit", 0); + +} + + + +bool MD5_hash_compare(const char* file1, const char* hash) +{ + unsigned char c[MD5_HASH_LENGTH]; + FILE* f1 = fopen(file1, "rb"); + if (f1 == NULL) + return true; + MD5_CTX mdContext; + + int bytes = 0; + unsigned char data[1024]; + + MD5_Init(&mdContext); + while ((bytes = fread(data, 1, 1024, f1)) != 0) + MD5_Update(&mdContext, data, bytes); + MD5_Final(c, &mdContext); + + char md5_string[33] = { 0 }; + + for (int i = 0; i < 16; ++i) { + snprintf(&md5_string[i * 2], 32, "%02x", (unsigned int)c[i]); + } + log_info("FILE HASH: %s", md5_string); + md5_string[32] = 0; + + if (strcmp(md5_string, hash) != 0) { + return DIFFERENT_HASH; + } + + log_info("Input HASH: %s", hash); + fclose(f1); + + return SAME_HASH; +} + +int netInit(void) +{ + // CURL SOMEDAY + return 0; +} + + +#include + + +void Network_Init() +{ + int ret = netInit(); + if (ret < 0) { log_info("netInit() error: 0x%08X", ret); } +} + +int jsoneq(const char* json, jsmntok_t* tok, const char* s) { + if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return 0; + } + return -1; +} + +static char *checkedsize = NULL; + +long CalcAppsize(const char *path) +{ + FILE *fp = NULL; + long off; + + fp = fopen(path, "r"); + if (fp == NULL) return 0; + + if (fseek(fp, 0, SEEK_END) == -1) goto cleanup; + + off = ftell(fp); + if (off == (long)-1) goto cleanup; + + if (fclose(fp) != 0) goto cleanup; + + if(off) { return off; } + else { checkedsize = NULL; return 0; } + +cleanup: + if (fp != NULL) + fclose(fp); + + return 0; + +} + +void save_fuse_ip(const char* ip) { + + FILE* fp = fopen("/data/itemzflow_daemon/fuse_ip.txt", "w"); + if(fp == NULL) + return; + fprintf(fp, "%s", ip); + fclose(fp); +} + +enum{ + MAIN_ON_STANDBY = 500, + WORKING = 1000, +}; + +bool isRestMode() +{ + return sceSystemStateMgrGetCurrentState() == MAIN_ON_STANDBY; +} + +bool IsOn() +{ + //return (unsigned int)sceSystemStateMgrGetCurrentState() == WORKING; + return true; +} + +int sceSysUtilSendSystemNotificationWithText(int messageType, char* message); + +void notify(const char* message) +{ + log_info(message); + sceKernelLoadStartModule("/system/common/lib/libSceSysUtil.sprx", 0, NULL, 0, 0, 0); + sceSysUtilSendSystemNotificationWithText(222, message); +} + +bool create_stat_db(const char* path){ + char *err_msg = 0; + + if(!if_exists("/user/app/ITEM00001/")) + mkdir("/user/app/ITEM00001/", 0777); + + mkdir("/user/app/ITEM00001/game_stats/", 0777); + int rc = sqlite3_open(path, &db); + if (rc != SQLITE_OK) { + log_error("[STATS] Cannot open database: %s", sqlite3_errmsg(db)); + sqlite3_close(db); + return false; + } + + const char *smt = "CREATE TABLE Game_Stats(tid TEXT, mins_played INT, first_started INT, PRIMARY KEY(tid));"; + rc = sqlite3_exec(db, smt, 0, 0, &err_msg); + if (rc != SQLITE_OK ) { + log_error("[STATS] SQL error: %s", err_msg); + sqlite3_free(err_msg); + sqlite3_close(db); + return false; + } + + sqlite3_close(db); + return true; +} + + +void DumpHex(const void* data, size_t size) { + char ascii[17]; + size_t i, j; + ascii[16] = '\0'; + for (i = 0; i < size; ++i) { + printf("%02X ", ((unsigned char*)data)[i]); + if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { + ascii[i % 16] = ((unsigned char*)data)[i]; + } else { + ascii[i % 16] = '.'; + } + if ((i+1) % 8 == 0 || i+1 == size) { + printf(" "); + if ((i+1) % 16 == 0) { + printf("| %s \n", ascii); + } else if (i+1 == size) { + ascii[(i+1) % 16] = '\0'; + if ((i+1) % 16 <= 8) { + printf(" "); + } + for (j = (i+1) % 16; j < 16; ++j) { + printf(" "); + } + printf("| %s \n", ascii); + } + } + } +} + +#define ORBIS_LIBC_MALLOC_MANAGED_SIZE_VERSION (0x0001U) + +#ifndef SCE_LIBC_INIT_MALLOC_MANAGED_SIZE +#define SCE_LIBC_INIT_MALLOC_MANAGED_SIZE(mmsize) do { \ + mmsize.size = sizeof(mmsize); \ + mmsize.version = ORBIS_LIBC_MALLOC_MANAGED_SIZE_VERSION; \ + mmsize.reserved1 = 0; \ + mmsize.maxSystemSize = 0; \ + mmsize.currentSystemSize = 0; \ + mmsize.maxInuseSize = 0; \ + mmsize.currentInuseSize = 0; \ +} while (0) +#endif + +void* statemgr = NULL; + +int sceSystemOpenStartMgr(){ + if ( (unsigned int)sceKernelOpenEventFlag(&statemgr, "SceSystemStateMgrInfo") ) + return -1; + + return 0; +} +#define SCE_KERNEL_EVF_WAITMODE_OR 0x02 + +int sceSystemStateMgrGetCurrentState() +{ + uint64_t ret = 0; + + if(!statemgr){ + if(sceSystemOpenStartMgr() == -1) + return -1; + } + sceKernelPollEventFlag(statemgr, 0xFFFF, SCE_KERNEL_EVF_WAITMODE_OR, &ret); + if((int)ret == WORKING && sceKernelPollEventFlag(statemgr, 0x200000, SCE_KERNEL_EVF_WAITMODE_OR, 0) == 0) + ret = MAIN_ON_STANDBY; + // my custom check + // int MAppid = sceSystemServiceGetAppIdOfMiniApp(); + // if (0 < MAppid) + // ret = MAIN_ON_STANDBY; + if((int)ret == MAIN_ON_STANDBY) + log_debug("SceSystemStateMgrGetCurrentState MAIN_ON_STANDBY"); + + return (int)ret; +} + +void print_memory() +{ + LibcMallocManagedSize ManagedSize; + SCE_LIBC_INIT_MALLOC_MANAGED_SIZE(ManagedSize); + malloc_stats_fast(&ManagedSize); + log_info("[MEM_MANAGER] ================================================]"); + log_info("[MEM] CurrentSystemSize: %i, CurrentInUseSize: %i", ManagedSize.currentSystemSize, ManagedSize.currentInuseSize); + log_info("[MEM] MaxSystemSize: %i, MaxInUseSize: %i", ManagedSize.maxSystemSize, ManagedSize.maxInuseSize); + log_info("[MEM_MANAGER] ================================================]"); + +} + +bool open_stat_db(const char* path){ + int ret; + struct stat sb; + + // check if our sqlite db is already present in our path + ret = sceKernelStat(path, &sb); + if (ret != 0) + { + log_error("[ERROR][SQLLDR][%s][%d] sceKernelStat(%s) failed: 0x%08X", __FUNCTION__, __LINE__, path, ret); + return false; + } + ret = sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE, NULL); + if (ret != SQLITE_OK) + { + log_error("[ERROR][SQLLDR][%s][%d] ERROR 0x%08X: %s", __FUNCTION__, __LINE__, ret, sqlite3_errmsg(db)); + return false; + } + + return true; +} + +bool update_game_time_played(const char* tid){ + + char smt[200]; + char *err_msg = NULL; + + if(!tid && strlen(tid) != 9){ + log_error("[STATS] Invalid tid"); + return false; + } + + snprintf(smt, sizeof(smt)-1, "INSERT OR IGNORE INTO Game_Stats(tid, mins_played, first_started) VALUES('%s', 0, '%ld')", tid, (long)time(NULL)); + + int rc = sqlite3_exec(db, &smt[0], 0, 0, &err_msg); + if (rc != SQLITE_OK ) { + log_error("[STATS] SQL error: %s", err_msg); + sqlite3_free(err_msg); + return false; + } + + snprintf(smt, sizeof(smt)-1, "UPDATE Game_Stats SET mins_played = mins_played+1 WHERE tid = '%s'", tid); + + rc = sqlite3_exec(db, &smt[0], 0, 0, &err_msg); + if (rc != SQLITE_OK ) { + log_error("[STATS] SQL error: %s", err_msg); + sqlite3_free(err_msg); + return false; + } + + return true; +} + +int int_cb(void *in, int idc, char **argv, char **idc_2) +{ + int *out = (int *)in; + *out = atoi(argv[0]); + return 0; +} +/* +static int token_cb(void *in, int count, char **data, char **columns) +{ + char **result_str = (char **)in; + *result_str = sqlite3_mprintf("%w", data[0]); + + return 0; +} +*/ +int get_game_time_played(const char* tid){ + char smt[200]; + char *err_msg = NULL; + int mins_played = -1; + + snprintf(smt, sizeof(smt)-1, "SELECT mins_played FROM Game_Stats WHERE tid = '%s'", tid); + + int rc = sqlite3_exec(db, &smt[0], int_cb, &mins_played, &err_msg); + if (rc != SQLITE_OK ) { + log_error("[STATS] SQL error: %s", err_msg); + sqlite3_free(err_msg); + return -1; + } + + return mins_played; +} + +time_t get_game_start_date(const char* tid){ + char smt[200]; + char* err_msg = NULL; + time_t epoch = 0; + + snprintf(smt, sizeof(smt)-1, "SELECT first_started FROM Game_Stats WHERE tid = '%s'", tid); + + int rc = sqlite3_exec(db, &smt[0], int_cb, &epoch, &err_msg); + if (rc != SQLITE_OK ) { + log_error("[STATS] SQL error: %s", err_msg); + sqlite3_free(err_msg); + return 0; + } + + return epoch; +} +bool copyRegFile(const char* source, const char* dest) +{ + + int src = sceKernelOpen(source, 0x0000, 0); + if (src > 0) + { + + int out = sceKernelOpen(dest, 0x0001 | 0x0200 | 0x0400, 0777); + if (out > 0) + { + size_t bytes = 0; + char buf[4096]; + while (0 < (bytes = sceKernelRead(src, &buf[0], 4096))){ + sceKernelWrite(out, &buf[0], bytes); + memset(&buf[0], 0, 4096); + } + } + else + { + sceKernelClose(src); + return false; + } + + sceKernelClose(src); + sceKernelClose(out); + return true; + } + else + return false; + +} + +bool rmtree(const char path[]) { + + struct dirent* de; + char fname[300]; + DIR* dr = opendir(path); + if (dr == NULL) + { + log_debug("No file or directory found: %s", path); + return false; + } + while ((de = readdir(dr)) != NULL) + { + int ret = -1; + struct stat statbuf; + snprintf(fname, sizeof(fname)-1, "%s/%s", path, de->d_name); + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + if (!stat(fname, &statbuf)) + { + if (S_ISDIR(statbuf.st_mode)) + { + log_debug("removing dir: %s Err: %d", fname, ret = unlinkat(dirfd(dr), fname, AT_REMOVEDIR)); + if (ret != 0) + { + rmtree(fname); + log_debug("Err: %d", ret = unlinkat(dirfd(dr), fname, AT_REMOVEDIR)); + } + } + else + { + log_debug("Removing file: %s, Err: %d", fname, unlink(fname)); + } + } + } + closedir(dr); + + return true; +} + + +bool copy_dir(const char* sourcedir,const char* destdir) { + + log_info("s: %s d: %s", sourcedir, destdir); + DIR* dir = opendir(sourcedir); + + struct dirent* dp; + struct stat info; + char src_path[1024]; + char dst_path[1024]; + + if (!dir) { + return false; + } + mkdir(destdir, 0777); + while ((dp = readdir(dir)) != NULL) { + if(strstr(dp->d_name, "save.ini") != NULL) + log_info("Save.ini file skipped..."); + else if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") + || strstr(dp->d_name, "nobackup") != NULL) { + continue; + } + else { + sprintf(src_path, "%s/%s", sourcedir, dp->d_name); + sprintf(dst_path, "%s/%s", destdir, dp->d_name); + + if (!stat(src_path, &info)) { + if (S_ISDIR(info.st_mode)) { + copy_dir(src_path, dst_path); + } + else if (S_ISREG(info.st_mode)) { + if (!copyRegFile(src_path, dst_path)) + return false; + } + } + } + } + closedir(dir); + + return true; +} + + +bool full_init() +{ + + // pad + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_PAD)) return false; + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_SYSUTIL)) return false; + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_NETCTL)) return false; + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_NET)) return false; + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_HTTP)) return false; + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_SSL)) return false; + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_SYS_CORE)) return false; + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_BGFT)) return false; + if (sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_APPINSTUTIL)) return false; + + mkdir("/data/itemzflow_daemon", 0777); + mkdir("/mnt/usb0/itemzflow", 0777); + unlink(DAEMON_LOG_PS4); + // get fuse ip + load_fuse_ip(); + + /*-- INIT LOGGING FUNCS --*/ + log_set_quiet(false); + log_set_level(LOG_DEBUG); + FILE* fp = fopen(DAEMON_LOG_PS4, "w"); + log_add_fp(fp, LOG_DEBUG); + + if (touch_file(DAEMON_LOG_USB)) + { + fp = fopen(DAEMON_LOG_USB, "w"); + log_add_fp(fp, LOG_DEBUG); + } + /* -- END OF LOGINIT --*/ + log_info("------------------------ ItemzFlow[Daemon] Compiled Time: %s @ %s -------------------------", __DATE__, __TIME__); + log_info(" --------------------------- Daemon Version: %s ------------------------------------", completeVersion); + + //Dump code and sig hanlder + struct sigaction new_SIG_action; + + new_SIG_action.sa_handler = SIG_Handler; + sigemptyset(&new_SIG_action.sa_mask); + new_SIG_action.sa_flags = 0; + //for now just SEGSEV + for (int i = 0; i < 12; i++) + sigaction(i, &new_SIG_action, NULL); + + sceSystemServiceHideSplashScreen(); + + if(sceSystemOpenStartMgr() == -1){ + log_error("Failed to open start state manager"); + notify("Failed to open start state manager"); + } + + return true; +} diff --git a/itemz-loader/Makefile b/itemz-loader/Makefile index 3288300..bf5fc15 100644 --- a/itemz-loader/Makefile +++ b/itemz-loader/Makefile @@ -7,7 +7,7 @@ TargetFile=homebrew.elf include $(ORBISDEV)/make/ps4sdk.mk -LinkerFlags += -lSceLncUtil_stub -lc++ -lc++abi -lc++experimental -luser_mem_sys -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 -lSceIme_stub -lSceSysUtil_stub -lSceHttp_stub -lSceSsl_stub -lSceNetCtl_stub -lkernel_sys_stub +LinkerFlags += -lSceLncUtil_stub -lc++ -lc++abi -lc++experimental -luser_mem_sys -lkernel_sys_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 -lSceIme_stub -lSceSysUtil_stub -lSceHttp_stub -lSceSsl_stub -lSceNetCtl_stub -lkernel_sys_stub CompilerFlags += -D__PS4__ -D__ORBIS__ diff --git a/itemz-loader/include/Header.h b/itemz-loader/include/Header.h index 5f8cb49..347fb26 100644 --- a/itemz-loader/include/Header.h +++ b/itemz-loader/include/Header.h @@ -31,7 +31,6 @@ #define TRUE 1 #define FALSE 0 -void logshit(char* format, ...); #define SCE_SYSMODULE_INTERNAL_SYS_CORE 0x80000004 #define SCE_SYSMODULE_INTERNAL_NETCTL 0x80000009 @@ -152,26 +151,6 @@ void logshit(char* format, ...); #define SCE_SYSMODULE_INTERNAL_COMMON_DIALOG 0x80000018 #define SCE_SYSMODULE_INTERNAL_SYSUTIL 0x80000018 -enum IPC_Errors -{ - INVALID = -1, - NO_ERROR = 0, - OPERATION_FAILED = 1, - DEAMON_UPDATING = 100 -}; - - -enum IPC_Commands -{ - 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, - DEAMON_UPDATE = 100 -}; enum { CWD_KEEP, CWD_ROOT, CWD_RESET }; extern void (*jbc_run_as_root)(void(*fn)(void* arg), void* arg, int cwd_mode); @@ -199,24 +178,30 @@ LncAppParam; extern uint8_t daemon_eboot[]; extern int32_t daemon_eboot_size; - +#ifdef __cplusplus +extern "C" { +#endif int32_t sceSystemServiceHideSplashScreen(); int32_t sceSystemServiceParamGetInt(int32_t paramId, int32_t *value); uint32_t sceLncUtilLaunchApp(const char* tid, const char* argv[], LncAppParam* param); -bool boot_daemon_services(); int sceLncUtilGetAppId(const char* tid); int sceSystemServiceLoadExec(const char* eboot, const char* argv[]); int copyFile(const char* sourcefile, const char* destfile); void loader_rooted(void *arg); void init_itemzGL_modules(); -int msgok(char* format, ...); +int msgok(const char* format, ...); int loadmsg(char* format, ...); int pingtest(int libnetMemId, int libhttpCtxId, const char* src); int download_file(int libnetMemId, int libhttpCtxId, const char* src, const char* dst); bool if_exists(const char* path); int32_t netInit(void); -void logshit(char* format, ...); +void logshit(const char* format, ...); void init_STOREGL_modules(); int sceKernelDebugOutText(int DBG_CHANNEL, const char* text); int Confirmation_Msg(const char* msg); bool rmtree(const char path[]); +void *malloc(size_t size); +#ifdef __cplusplus +} +#endif +bool boot_daemon_services(); \ No newline at end of file diff --git a/itemz-loader/include/ipc_client.hpp b/itemz-loader/include/ipc_client.hpp new file mode 100644 index 0000000..a91fb6a --- /dev/null +++ b/itemz-loader/include/ipc_client.hpp @@ -0,0 +1,216 @@ +#pragma once +#ifndef IPC_HEADER_H +#define IPC_HEADER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum class IPC_Ret { + INVALID = -1, + NO_ERROR = 0, + OPERATION_FAILED = 1, + FUSE_IP_ALREADY_SET = 3, + FUSE_IP_NOT_SET = 4, + FUSE_FW_NOT_SUPPORTED = 5, + DEAMON_UPDATING = 100 +}; + +enum class IPC_Commands { + MACGUFFIN_CMD = 69, + CMD_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, +}; + +constexpr size_t DAEMON_BUFF_MAX = 0x100; // Assuming a buffer max size, you can change this. +struct IPCMessage{ + int magic = 0xDEADBABE; + IPC_Commands cmd; + IPC_Ret error = IPC_Ret::INVALID; + char msg[DAEMON_BUFF_MAX] = {0}; +}; + +const char IPC_SOC[] = "/system_tmp/IPC_Socket"; +static int DaemonSocket = -1; + +class IPC_Client{ +private: + mutable std::mutex socketMutex; + +public: + +// Socket Management +int OpenConnection(const char* path) { + sockaddr_un server; + int soc = socket(AF_UNIX, SOCK_STREAM, 0); + if (soc == -1) { + logshit("Failed to create socket"); + return -1; + } + server.sun_family = AF_UNIX; + strncpy(server.sun_path, path, sizeof(server.sun_path)-1); + if (connect(soc, (struct sockaddr*)&server, SUN_LEN(&server)) == -1) { + close(soc); + logshit("Failed to connect to socket"); + return -1; + } + return soc; +} + +// IPC Functions +bool IPCOpenConnection() { + DaemonSocket = OpenConnection(IPC_SOC); + return DaemonSocket >= 0; +} + +bool IPCOpenIfNotConnected() { + if(DaemonSocket >= 0) { + return true; + } + return IPCOpenConnection(); +} +int IPCReceiveData(IPCMessage& msg) { + return recv(DaemonSocket, reinterpret_cast(&msg), sizeof(msg), MSG_NOSIGNAL); +} + +int IPCSendData(const IPCMessage& msg) { + int ret = send(DaemonSocket, reinterpret_cast(&msg), sizeof(msg), MSG_NOSIGNAL); + if (ret < 0) { + logshit("IPCSendData failed with: %s", strerror(errno)); + } + return ret; +} + +IPC_Ret IPCCloseConnection() { + if (DaemonSocket < 0) { + return IPC_Ret::INVALID; + } + + close(DaemonSocket); + DaemonSocket = -1; + return IPC_Ret::NO_ERROR; +} + + +IPC_Ret IPCSendCommand(IPC_Commands cmd, std::string& ipc_msg, bool silent = false) { + std::lock_guard lock(socketMutex); + + IPCMessage msg; + msg.cmd = cmd; + // Copy the string content into the destination array + strncpy(msg.msg, ipc_msg.c_str(), sizeof(msg.msg) - 1); + // Null-terminate the destination array + msg.msg[sizeof(msg.msg) - 1] = '\0'; + + IPC_Ret error = IPC_Ret::INVALID; + if (IPCOpenIfNotConnected()) { + if(!silent) + logshit(" Sending IPC Command"); + + IPCSendData(msg); + + if(!silent) + logshit(" Sent IPC Command %i", static_cast(cmd)); + + ipc_msg.clear(); + + if (cmd == IPC_Commands::DEAMON_UPDATE) { + logshit(" Daemon Updating..."); + IPCCloseConnection(); + return IPC_Ret::DEAMON_UPDATING; + } + + // Get message back from daemon + uint32_t readSize = IPCReceiveData(msg); + if (cmd == IPC_Commands::RESTART_FUSE_FS) { + logshit("Restarting FUSE"); + IPCCloseConnection(); + } + if (static_cast(readSize) >= 0) { + if(!silent) + logshit(" Got message with Size: %i from Daemon", readSize); + // Get IPC Error code + error = msg.error; + if (error != IPC_Ret::INVALID) { + // Modifies the Buffer to exclude the error code + if(error == IPC_Ret::NO_ERROR) { + ipc_msg = msg.msg; + } + else { + ipc_msg = "Error"; + } + if(!silent) + logshit("[ItemzDaemon] Daemon IPC Response: %s, code: %s, readSize: %i", msg.msg, error == IPC_Ret::NO_ERROR ? "NO_ERROR" : "Other", readSize); + } else { + logshit("[ItemzDaemon] Daemon returned INVAL"); + } + } else { + if(!silent) + logshit("IPCReceiveData failed with: %s", strerror(errno)); + IPCCloseConnection(); + } + } + if (cmd == IPC_Commands::CMD_SHUTDOWN_DAEMON) { + IPCCloseConnection(); + } + + return error; +} + +IPC_Ret IPCMountFUSE(std::string ip, std::string path, bool debug_mode) { + std::string in; + IPC_Ret error = IPC_Ret::INVALID; + IPCSendCommand(IPC_Commands::FUSE_SET_SESSION_IP, ip); + if (debug_mode) { + if (IPCSendCommand(IPC_Commands::FUSE_SET_DEBUG_FLAG, in) == IPC_Ret::NO_ERROR) { + logshit("FUSE debug mode enabled"); + } else { + logshit("FUSE failed to enable debug mode"); + } + } + + // Set the mount point + // Copy the string content into the destination array + if ((error = IPCSendCommand(IPC_Commands::FUSE_START_W_PATH, path)) == IPC_Ret::NO_ERROR) { + logshit("FUSE started"); + } else { + logshit("FUSE failed to start with error code: %i", static_cast(error)); + } + + return error; +} + // Deleted copy constructor and assignment operator to ensure only one instance + IPC_Client& operator=(const IPC_Client&) = delete; + + // Static method to access the instance + static IPC_Client& getInstance() { + static IPC_Client instance; // Lazy-loaded instance, guaranteed to be destroyed + return instance; + } +// ... (Placeholder for remaining functions) + +}; // namespace IPC + +#endif // IPC_HEADER_H diff --git a/itemz-loader/source/app.cpp b/itemz-loader/source/app.cpp new file mode 100644 index 0000000..d881581 --- /dev/null +++ b/itemz-loader/source/app.cpp @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include "Header.h" + +const unsigned char completeVersion[] = {VERSION_MAJOR_INIT, + '.', + VERSION_MINOR_INIT, + '-', + 'V', + '-', + BUILD_YEAR_CH0, + BUILD_YEAR_CH1, + BUILD_YEAR_CH2, + BUILD_YEAR_CH3, + '-', + BUILD_MONTH_CH0, + BUILD_MONTH_CH1, + '-', + BUILD_DAY_CH0, + BUILD_DAY_CH1, + 'T', + BUILD_HOUR_CH0, + BUILD_HOUR_CH1, + ':', + BUILD_MIN_CH0, + BUILD_MIN_CH1, + ':', + BUILD_SEC_CH0, + BUILD_SEC_CH1, + '\0'}; + + +void logshit(const char* format, ...) +{ + //Check the length of the format string. + // If it is too long, return immediately. + if (strlen(format) > 1000) { + return; + } + + // Initialize a buffer to hold the formatted log message. + char buff[1024]; + + // Initialize a va_list to hold the variable arguments passed to the function. + va_list args; + va_start(args, format); + + // Format the log message using the format string and the variable arguments. + vsprintf(buff, format, args); + + // Clean up the va_list. + va_end(args); + + // Append a newline character to the log message. + strcat(buff, "\n"); + + // Output the log message using sceKernelDebugOutText. + sceKernelDebugOutText(DGB_CHANNEL_TTYL, buff); + + int fd = sceKernelOpen("/user/app/ITEM00001/logs/loader.log", O_WRONLY | O_CREAT | O_APPEND, 0777); + if (fd >= 0) + { + sceKernelWrite(fd, &buff[0], strlen(&buff[0])); + sceKernelClose(fd); + } +} + + +bool rmtree(const char path[]) { + + struct dirent* de; + char fname[300]; + DIR* dr = opendir(path); + if (dr == NULL) + { + logshit("No file or directory found: %s", path); + return false; + } + while ((de = readdir(dr)) != NULL) + { + int ret = -1; + struct stat statbuf; + snprintf(fname, 299, "%s/%s", path, de->d_name); + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + if (!stat(fname, &statbuf)) + { + if (S_ISDIR(statbuf.st_mode)) + { + logshit("removing dir: %s Err: %d", fname, ret = unlinkat(dirfd(dr), fname, AT_REMOVEDIR)); + if (ret != 0) + { + rmtree(fname); + //logshit("Err: %d", ret = unlinkat(dirfd(dr), fname, AT_REMOVEDIR)); + } + } + else + { + logshit("Removing file: %s, Err: %d", fname, unlink(fname)); + } + } + } + closedir(dr); + + return true; +} + +int copyFile(const char* sourcefile, const char* destfile) +{ + int src = sceKernelOpen(sourcefile, 0x0000, 0); + if (src > 0) + { + int out = sceKernelOpen(destfile, 0x0001 | 0x0200 | 0x0400, 0777); + if (out > 0) + { + size_t bytes; + char* buffer = (char*)malloc(65536); + if (buffer != NULL) + { + while (0 < (bytes = sceKernelRead(src, buffer, 65536))) + sceKernelWrite(out, buffer, bytes); + free(buffer); + } + sceKernelClose(out); + } + else + return -1; + + + sceKernelClose(src); + return 0; + } + else + { + logshit("[ELFLOADER] fuxking error"); + logshit("[Itemz-loader:%s:%i] ----- src fd = %i---", __FUNCTION__, __LINE__, src); + return -1; + } +} + +bool if_exists(const char* path) +{ + int dfd = open(path, O_RDONLY, 0); // try to open dir + if (dfd < 0) { + logshit("path %s, errno %s", path, strerror(errno)); + return false; + } + else + close(dfd); + + + return true; +} + +void init_itemzGL_modules() +{ + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_SYSTEM_SERVICE); + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_USER_SERVICE); + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_NETCTL); + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_NET); + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_HTTP); + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_SSL); + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_SYS_CORE); + sceSysmoduleLoadModuleInternal(0x80000018); + sceSysmoduleLoadModuleInternal(0x80000026); // 0x80000026 + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_BGFT); // 0x80000026; + sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_APPINSTUTIL); // 0x80000026 0x80000037 + sceSysmoduleLoadModuleInternal(0x80000024); // 0x80000026 0x80000037 + sceSysmoduleLoadModule(ORBIS_SYSMODULE_MESSAGE_DIALOG); +} + +void (*jbc_run_as_root)(void(*fn)(void* arg), void* arg, int cwd_mode); + +void loader_rooted(void *arg){ + if(if_exists("/usb0/recovery.flag") || if_exists("/user/recovery.flag")){ + + if(Confirmation_Msg("ItemzLoader Recovery Menu\n\n\nDo you want to Factory reset ItemzFlow??\n\nWarning: ItemzFlow App Stats, Covers, Settings, Updates, Themes and More will be Deleted!") == YES){ + //rmtree app.xml app.pkg app.pbm.backup app.pbm app.json + logshit("Starting factory reset..."); + mkdir("/user/app/reset", 0777); + copyFile("/user/app/ITEM00001/app.xml", "/user/app/reset/app.xml"); + copyFile("/user/app/ITEM00001/app.pkg", "/user/app/reset/app.pkg"); + copyFile("/user/app/ITEM00001/app.pbm.backup", "/user/app/reset/app.pbm.backup"); + copyFile("/user/app/ITEM00001/app.pbm", "/user/app/reset/app.pbm"); + copyFile("/user/app/ITEM00001/app.json", "/user/app/reset/app.json"); + + rmtree("/user/app/ITEM00001"); + mkdir("/user/app/ITEM00001", 0777); + + copyFile("/user/app/reset/app.xml", "/user/app/ITEM00001/app.xml"); + copyFile("/user/app/reset/app.pkg", "/user/app/ITEM00001/app.pkg"); + copyFile("/user/app/reset/app.pbm.backup", "/user/app/ITEM00001/app.pbm.backup"); + copyFile("/user/app/reset/app.pbm", "/user/app/ITEM00001/app.pbm"); + copyFile("/user/app/reset/app.json", "/user/app/ITEM00001/app.json"); + rmtree("/user/app/reset"); + rmdir("/user/app/reset"); + + logshit("Factory reset done!"); + unlink("/usb0/recovery.flag"); + rmdir("/usb0/recovery.flag"); + unlink("/user/recovery.flag"); + rmdir("/user/recovery.flag"); + msgok("ItemzLoader Recovery Menu\n\n\nItemzFlow has been Factory Reset!\n\nPress OK to exit"); + logshit("Factory reset complete!"); + sceSystemServiceLoadExec("EXIT", NULL); + } + else + logshit("ItemzLoader Recovery Canned..."); + + unlink("/usb0/recovery.flag"); + rmdir("/usb0/recovery.flag"); + unlink("/user/recovery.flag"); + rmdir("/user/recovery.flag"); + } + + logshit("After jb"); + mkdir("/user/app/ITEM00001/storedata/", 0777); + mkdir("/user/app/ITEM00001/logs/", 0777); + + unlink("/user/app/ITEM00001/logs/loader.log"); + + logshit("[Itemz-loader:%s:%i] ----- All Internal Modules Loaded -----", __FUNCTION__, __LINE__); + logshit("------------------------ Itemz Loader[GL] Compiled Time: %s @ %s EST -------------------------", __DATE__, __TIME__); + logshit("[Itemz-loader:%s:%i] ----- Itemz-Loader Version: %s -----", __FUNCTION__, __LINE__, completeVersion); + logshit("----------------------------------------------- -------------------------"); + + if (if_exists("/usb0/settings.ini")) + logshit("[Itemz-loader:%s:%i] ----- FOUND USB RECOVERY INI ---", __FUNCTION__, __LINE__); + else + { + if (!if_exists("/user/app/ITEM00001/settings.ini")) + { + logshit("[Itemz-loader:%s:%i] ----- APP INI Not Found, Making ini ---", __FUNCTION__, __LINE__); + + char buff[1024]; + memset(&buff[0], 0, 1024); + snprintf(&buff[0], 1024, "[Settings]\nSort_By=1\nSecure_Boot=1\nDumper_option=0\nFTP_Auto_Start=1\nShow_install_prog=1\nHomeMenu_Redirection=0\nDaemon_on_start=1\ncover_message=1\n"); + + int fd = open("/user/app/ITEM00001/settings.ini", O_WRONLY | O_CREAT | O_TRUNC, 0777); + if (fd >= 0) + { + write(fd, &buff[0], strlen(&buff[0])); + close(fd); + } + else + logshit("Could not make create INI File"); + + mkdir("/user/app/ITEM00001/downloads", 0777); + + } + else + logshit("[Itemz-loader:%s:%i] ----- INI FOUND ---", __FUNCTION__, __LINE__); + + } + + logshit("[Itemz-loader:%s:%i] ----- Starting daemon services -----", __FUNCTION__, __LINE__); + if(boot_daemon_services()) + logshit("[Itemz-loader:%s:%i] ----- Daemon services started -----", __FUNCTION__, __LINE__); + else + logshit("[Itemz-loader:%s:%i] ----- Daemon services failed to start -----", __FUNCTION__, __LINE__); + // wrapper(); + +} + diff --git a/itemz-loader/source/daemon.cpp b/itemz-loader/source/daemon.cpp new file mode 100644 index 0000000..ca82697 --- /dev/null +++ b/itemz-loader/source/daemon.cpp @@ -0,0 +1,222 @@ +#include +#include +#include "ini.h" +#include +#include +#include +#include +#include +#include +#include "md5.h" +#include +#include +#include + + +#define DAEMON_PATH "/system/vsh/app/ITEM00002" + +/* *********************** IPC SHIT **********************/ +#include "ipc_client.hpp" +/* *********************** IPC SHIT **********************/ +/* *********************** daemon shit **********************/ + +int MD5_hash_compare(const char* file1, const char* file2) +{ + unsigned char c[MD5_HASH_LENGTH]; + unsigned char c2[MD5_HASH_LENGTH]; + MD5_CTX mdContext, mdContext2; + int bytes2, bytes = 0; + unsigned char data2[1024]; + unsigned char data[1024]; + + FILE* f1 = fopen(file1, "rb"); + if(!f1) + logshit("cannot open: %s", file1); + return true; + + FILE* f2 = fopen(file2, "rb"); + if(!f2){ + logshit("cannot open: %s", file2); + fclose(f1); + return true; + } + + MD5_Init(&mdContext); + while ((bytes = fread(data, 1, 1024, f1)) != 0) + MD5_Update(&mdContext, data, bytes); + MD5_Final(c, &mdContext); + + MD5_Init(&mdContext2); + while ((bytes2 = fread(data2, 1, 1024, f2)) != 0) + MD5_Update(&mdContext2, data2, bytes2); + MD5_Final(c2, &mdContext2); + + for (int i = 0; i < 16; i++) + { + if (c[i] != c2[i]){ + fclose(f1); + fclose(f2); + return true; + } + } + + fclose(f1); + fclose(f2); + + return false; +} + +bool is_daemon_outdated(void) +{ + bool res = true; + if (if_exists(DAEMON_PATH"/eboot.bin")) + { + res = MD5_hash_compare(DAEMON_PATH"/eboot.bin", "/mnt/sandbox/pfsmnt/ITEM00001-app0/daemon/daemon.self"); + logshit("Daemon Is Outdated?: %s", res ? "Yes" : "No"); + } + + return res; +} + +void build_iovec(struct iovec** iov, int* iovlen, const char* name, const void* val, size_t len) { + int i; + + if (*iovlen < 0) + return; + + i = *iovlen; + *iov = (struct iovec*)realloc((void*)(*iov), sizeof(struct iovec) * (i + 2)); + if (*iov == NULL) { + *iovlen = -1; + return; + } + + (*iov)[i].iov_base = strdup(name); + (*iov)[i].iov_len = strlen(name) + 1; + ++i; + + (*iov)[i].iov_base = (void*)val; + if (len == (size_t)-1) { + if (val != NULL) + len = strlen((const char*)val) + 1; + else + len = 0; + } + (*iov)[i].iov_len = (int)len; + + *iovlen = ++i; +} + + +int mountfs(const char* device, const char* mountpoint, const char* fstype, const char* mode, uint64_t flags) +{ + struct iovec* iov = NULL; + int iovlen = 0; + int ret; + + build_iovec(&iov, &iovlen, "fstype", fstype, -1); + build_iovec(&iov, &iovlen, "fspath", mountpoint, -1); + build_iovec(&iov, &iovlen, "from", device, -1); + build_iovec(&iov, &iovlen, "large", "yes", -1); + build_iovec(&iov, &iovlen, "timezone", "static", -1); + build_iovec(&iov, &iovlen, "async", "", -1); + build_iovec(&iov, &iovlen, "ignoreacl", "", -1); + + if (mode) { + build_iovec(&iov, &iovlen, "dirmask", mode, -1); + build_iovec(&iov, &iovlen, "mask", mode, -1); + } + + logshit("##^ [I] Mounting %s \"%s\" to \"%s\"", fstype, device, mountpoint); + ret = nmount(iov, iovlen, flags); + if (ret < 0) { + logshit("##^ [E] Failed: %d (errno: %d).", ret, errno); + goto error; + } + else { + logshit("##^ [I] Success."); + } + +error: + return ret; +} + +uint32_t Launch_Daemon(const char* TITLE_ID) { + uint32_t userId = -1; + + int libcmi = sceKernelLoadStartModule("/system/common/lib/libSceSystemService.sprx", 0, NULL, 0, 0, 0); + if (libcmi > 0) + { + + LncAppParam param; + param.sz = sizeof(LncAppParam); + param.user_id = userId; + param.app_opt = 0; + param.crash_report = 0; + param.check_flag = SkipSystemUpdateCheck; + + return sceLncUtilLaunchApp(TITLE_ID, NULL, ¶m); + } + + return 0; +} + +#define MNT_UPDATE 0x0000000000010000ULL /* not real mount, just update */ +bool boot_daemon_services() +{ + std::string ipc_msg; + logshit("Booting Daemon Services"); + IPC_Client& ipc = IPC_Client::getInstance(); + + if (!if_exists(DAEMON_PATH) || is_daemon_outdated()) + { + if (!!mountfs("/dev/da0x4.crypt", "/system", "exfatfs", "511", MNT_UPDATE)) + { + logshit("mounting /system failed with %s.", strerror(errno)); + return false; + } + else + { + + logshit("Remount Successful"); + //Delete the folder and all its files + rmtree(DAEMON_PATH); + mkdir(DAEMON_PATH, 0777); + mkdir(DAEMON_PATH"/Media", 0777); + mkdir(DAEMON_PATH"/sce_sys", 0777); + + if (copyFile("/mnt/sandbox/pfsmnt/ITEM00001-app0/Media/jb.prx", DAEMON_PATH"/Media/jb.prx") != -1 && + copyFile("/system/vsh/app/NPXS21007/sce_sys/param.sfo", DAEMON_PATH"/sce_sys/param.sfo") != -1) + { + if (copyFile("/mnt/sandbox/pfsmnt/ITEM00001-app0/daemon/daemon.self", DAEMON_PATH"/eboot.bin") == 0) { + ipc.IPCSendCommand(IPC_Commands::DEAMON_UPDATE, ipc_msg); + } + else + { + logshit("Creating the Daemon eboot failed to create: %s", strerror(errno)); + return false; + } + } + else + { + logshit("Copying Daemon files failed"); + return false; + } + } + } + + uint32_t appid = sceLncUtilGetAppId("ITEM00002"); + //Launch Daemon with silent + if ((appid & ~0xFFFFFF) != 0x60000000) { + appid = Launch_Daemon("ITEM00002"); + logshit("Launched Daemon AppId: %x", appid); + } + else + logshit("Found Daemon AppId: %x", appid); + + + + return true; + +} +/* *********************** daemon shit **********************/ \ No newline at end of file diff --git a/itemz-loader/source/lang.c b/itemz-loader/source/lang.c index 2322152..7546953 100644 --- a/itemz-loader/source/lang.c +++ b/itemz-loader/source/lang.c @@ -7,12 +7,10 @@ LangStrings* stropts; bool lang_is_initialized = false; int32_t PS4GetLang() { int32_t lang; - if (sceSystemServiceParamGetInt(1, &lang) != 0) { logshit("Unable to qet language code"); return 0x1; } - return lang; } @@ -25,7 +23,7 @@ char* getLangSTR(enum Lang_STR str) } else { - if (stropts->strings[str] != NULL) + if (stropts->strings[str]) return stropts->strings[str]; else return stropts->strings[STR_NOT_FOUND]; @@ -127,8 +125,10 @@ bool LoadLangs(int LangCode) else lang_is_initialized = true; } - else + else{ + msgok("LoadLangs failed"); return false; + } return true; } \ No newline at end of file diff --git a/itemz-loader/source/main.c b/itemz-loader/source/main.c index b18e944..a83a833 100644 --- a/itemz-loader/source/main.c +++ b/itemz-loader/source/main.c @@ -14,13 +14,81 @@ #include #include "lang.h" #include "ini.h" - +#include +#include int ret; int libnetMemId = 0; int libsslCtxId = 0; int libhttpCtxId = 0; bool restart_nessary = false; +#define MAX_MESSAGE_SIZE 0x1000 +#define MAX_STACK_FRAMES 60 + +/** + * A callframe captures the stack and program pointer of a + * frame in the call path. + **/ +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; + +void OrbisKernelBacktrace(const char* reason) { + char addr2line[MAX_STACK_FRAMES * 20]; + callframe_t frames[MAX_STACK_FRAMES]; + OrbisKernelVirtualQueryInfo info; + char buf[MAX_MESSAGE_SIZE + 3]; + unsigned int nb_frames = 0; + char temp[80]; + + memset(addr2line, 0, sizeof addr2line); + memset(frames, 0, sizeof frames); + memset(buf, 0, sizeof buf); + + snprintf(buf, sizeof buf, "<118>[Crashlog]: %s\n", reason); + + strncat(buf, "<118>[Crashlog]: Backtrace:\n", MAX_MESSAGE_SIZE); + sceKernelBacktraceSelf(frames, sizeof frames, &nb_frames, 0); + for (unsigned int i = 0; i < nb_frames; i++) { + memset(&info, 0, sizeof info); + sceKernelVirtualQuery(frames[i].pc, 0, &info, sizeof info); + + snprintf(temp, sizeof temp, + "<118>[Crashlog]: #%02d %32s: 0x%lx\n", + i + 1, info.name, frames[i].pc - info.unk01 - 1); + strncat(buf, temp, MAX_MESSAGE_SIZE); + + snprintf(temp, sizeof temp, + "0x%lx ", frames[i].pc - info.unk01 - 1); + strncat(addr2line, temp, sizeof addr2line - 1); + } + + strncat(buf, "<118>[Crashlog]: addr2line: ", MAX_MESSAGE_SIZE); + strncat(buf, addr2line, MAX_MESSAGE_SIZE); + strncat(buf, "\n", MAX_MESSAGE_SIZE); + + buf[MAX_MESSAGE_SIZE + 1] = '\n'; + buf[MAX_MESSAGE_SIZE + 2] = '\0'; + + //log_error("%s", buf); + msgok(buf); +} + void is_restart_needed_for_daemon(void* arg){ @@ -35,6 +103,15 @@ void is_restart_needed_for_daemon(void* arg){ logshit("Found Daemon AppId: %x, No restart needed", appid); } +void SIG_Handler(int sig_numb) +{ + //("App got signal %i", sig_numb); + OrbisKernelBacktrace("Itemz-loader has Crashed"); + sceSystemServiceLoadExec("exit", 0); + +} + + int main(int argc, char* argv[]) { init_itemzGL_modules(); @@ -45,27 +122,42 @@ int main(int argc, char* argv[]) sceCommonDialogInitialize(); sceMsgDialogInitialize(); + //msgok("DO YOU SEE THiS??"); + //Dump code and sig hanlder + struct sigaction new_SIG_action; + + // log_info("Registering Signal handler... "); + + // new_SIG_action.sa_sigaction = SIG_IGN; + new_SIG_action.sa_handler = SIG_Handler; + sigemptyset(&new_SIG_action.sa_mask); + new_SIG_action.sa_flags = 0; + + for (int i = 0; i < 43; i++) { + // if(i != SIGQUIT) + sigaction(i, &new_SIG_action, NULL); + } + if (!LoadLangs(PS4GetLang())) { + //msgok("DD 1"); if (!LoadLangs(0x01)) { msgok("Failed to load Backup Lang...\nThe App is unable to continue"); goto exit_no_lang; } - else - logshit("Loaded the backup, lang %i failed to load", PS4GetLang()); } + libjb = sceKernelLoadStartModule("/app0/Media/jb.prx", 0, NULL, 0, 0, 0); int ret = sceKernelDlsym(libjb, "jbc_run_as_root", (void**)&jbc_run_as_root); if (ret >= 0){ - logshit("jbc_run_as_root resolved from PRX"); if (!jbc_run_as_root){ msgok("%s: %x", getLangSTR(FATAL_JB),ret); goto err; } else{ + //msgok("jbc_run_as_root %p", jbc_run_as_root); jbc_run_as_root(loader_rooted, NULL, CWD_ROOT); - logshit("jbc_run_as_root() successful"); } } else{ @@ -82,9 +174,7 @@ int main(int argc, char* argv[]) } } - logshit("[Itemz-loader:%s:%i] ----- Launching() ---", __FUNCTION__, __LINE__); - orbisPadFinish(); if (sceSystemServiceLoadExec("/app0/ItemzCore.self", 0) == 0) logshit("[Itemz-loader:%s:%i] ----- Launched (shouldnt see) ---", __FUNCTION__, __LINE__); diff --git a/itemz-loader/source/msgs.c b/itemz-loader/source/msgs.c index b35f7e3..5cdaed7 100644 --- a/itemz-loader/source/msgs.c +++ b/itemz-loader/source/msgs.c @@ -42,7 +42,7 @@ int Confirmation_Msg(const char* msg) return NO; } -int msgok(char* format, ...) +int msgok(const char* format, ...) { int ret = 0; sceSysmoduleLoadModule(ORBIS_SYSMODULE_MESSAGE_DIALOG); diff --git a/itemzflow/Makefile b/itemzflow/Makefile index 33af518..b62e17d 100644 --- a/itemzflow/Makefile +++ b/itemzflow/Makefile @@ -10,9 +10,9 @@ AUTH_INFO = 000000000000000000000000001C004000FF00000000008000000000000000000000 include $(ORBISDEV)/make/ps4sdk.mk -LinkerFlags += -ltag -lSceShellCoreUtil_stub -ldumper -lc++ -lc++abi -lc++experimental -luser_mem_sys -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 -lSceSaveData_stub -lSceShellUIUtil_stub -lSceSysCore_stub +LinkerFlags += -ltag -lSceShellCoreUtil_stub -ldumper -lc++ -lc++abi -lc++experimental -luser_mem_sys -lkernel_sys_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 -lSceSaveData_stub -lSceShellUIUtil_stub -lSceSysCore_stub CompilerFlagsCpp += -fstack-protector-all -Wall -Werror -Wno-excess-initializers -DFMT_HEADER_ONLY # -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 +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 -fexceptions -frtti CompilerFlags += -D_FILE_OFFSET_BITS=64 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/itemzflow/build.sh b/itemzflow/build.sh index 2194298..cb05b84 100644 --- a/itemzflow/build.sh +++ b/itemzflow/build.sh @@ -1 +1 @@ -cd ../libyaml-cpp && make -j24 && make install && cd ../libdumper/ && make -j24 && make install && cd ../itemz-daemon/ && make clean && make -j24 && make oelf && make eboot && cd ../itemzflow/ && make clean && make -j24 && make oelf && make eboot \ No newline at end of file +cd ../libyaml-cpp && make -j24 && make install && cd ../libdumper/ && make -j24 && make install && cd ../itemz-daemon/ && make clean && make -j24 && make oelf && make eboot && cd ../itemzflow/ && make clean && make -j24 && make oelf && make eboot && make pkg_build \ No newline at end of file diff --git a/itemzflow/include/GLES2_common.h b/itemzflow/include/GLES2_common.h index 24c8425..88cb29b 100644 --- a/itemzflow/include/GLES2_common.h +++ b/itemzflow/include/GLES2_common.h @@ -12,7 +12,9 @@ #endif #include #include - +#include +#include +#include typedef enum vbo_status { @@ -31,42 +33,165 @@ struct multi_t bool is_active = false; }; -struct item_t -{ - std::string id, name, image, package, - version, picpath, sfopath; //App Data - int token_c, HDD_count = 0; // count - GLuint texture; // the item icon - // if the item icon is cached in atlas, use UV - vec4 uv; // normalized p1, p2 - multi_t multi_sel; +struct AppIcon { + struct ImageData { + unsigned char* data = nullptr; + int w = 0, h = 0, comp= 0; + //mutable std::mutex data_mutex; // Mutable to allow locking in const functions + ImageData(){ - std::unordered_map extra_sfo_data; - bool is_ext_hdd = false, is_fpkg = true, is_vapp = false; - bool is_dumpable = false, app_vis = true, is_ph = false; - // need to add index for texture atlas + } + + ImageData(const ImageData& other) { + //std::lock_guard lock(other.data_mutex); + w = other.w; + h = other.h; + comp = other.comp; + // Perform deep copy of data if needed + if (other.data) { + data = other.data; + } + } + + ImageData& operator=(const ImageData& other) { + if (this != &other) { + // std::unique_lock lock1(data_mutex, std::defer_lock); + //std::unique_lock lock2(other.data_mutex, std::defer_lock); + //std::lock(lock1, lock2); + + w = other.w; + h = other.h; + comp = other.comp; + + if (other.data) { + data = other.data; + } else { + data = nullptr; + } + } + return *this; + } + + ~ImageData() { + // delete[] data; + } }; + std::atomic texture; // The item icon + vec4 uv; // Normalized p1, p2 + std::atomic cover_exists, turn_into_texture; + ImageData image_data; + + + AppIcon() : texture(0), cover_exists(false), turn_into_texture(false) + { + uv = {0, 0, 0, 0}; + image_data = ImageData(); // Default construct image_data + } + + // Custom copy constructor + AppIcon(const AppIcon& other) : texture(other.texture.load()), + uv(other.uv), + cover_exists(other.cover_exists.load()), + turn_into_texture(other.turn_into_texture.load()) + { + // std::lock_guard lock(other.image_data.data_mutex); + image_data = other.image_data; + } + + // Custom copy assignment operator + AppIcon& operator=(const AppIcon& other) { + if (this == &other) { + return *this; + } + + texture = other.texture.load(); + uv = other.uv; + cover_exists = other.cover_exists.load(); + turn_into_texture = other.turn_into_texture.load(); + + // std::lock(image_data.data_mutex, other.image_data.data_mutex); + //std::lock_guard lock1(image_data.data_mutex, std::adopt_lock); + //std::lock_guard lock2(other.image_data.data_mutex, std::adopt_lock); + + image_data = other.image_data; + + return *this; + } +}; + +struct ItemFlags { + bool is_ext_hdd = false; + bool is_fpkg = true; + bool is_vapp = false; + bool is_dumpable = false; + bool app_vis = true; + bool is_ph = false; + bool is_favorite = false; + bool is_all_pkg_enabled = false; +}; + +struct ItemData { + std::string id; + std::string name; + std::string image; + std::string package; + std::string version; + std::string picpath; + std::string sfopath; +}; + +struct ItemCount { + int token_c = 0; + int HDD_count = 0; +}; + +struct ItemExtraData { + std::unordered_map extra_sfo_data; +}; + +struct item_t { + ItemData info; + ItemCount count; + AppIcon icon; + multi_t multi_sel; + ItemExtraData extra_data; + ItemFlags flags; +}; + +#include "feature_classes.hpp" struct layout_t { - vec4 bound_box; // total field size, in px - ivec2 fieldsize, // total field size, in items number - item_sel, // current selected item into field - page_sel; // page_selection: current, max page - int retries; // check for what's used for and remove - // an array of items - std::vector item_d; // item_data array - int item_c, // item_count - curr_item, // current selected item: - f_size, // field size - f_sele; // selected in field - vec4* f_rect = NULL; // field rectangle array + vec4 bound_box; + ivec2 fieldsize, + item_sel, + page_sel; + int retries; + ThreadSafeVector item_d; + //item_t* item_d = NULL; + int item_c, + curr_item, + f_size, + f_sele; + std::vector f_rect; + + //vec4 f_rect; // field rectangle array // each layout will hold its ft-gl vertex buffer object - vertex_buffer_t* vbo = NULL; - enum vbo_status vbo_s; // vbo_status - // layout status - bool is_shown = false, is_active = false, fs_is_active = false; // to control selected_item (deprecated by active_p) + //vertex_buffer_t* vbo = NULL; + VertexBuffer vbo; + vbo_status vbo_s; + bool is_shown, is_active, fs_is_active; + std::recursive_mutex mtx; + + layout_t() : retries(0), item_c(0), curr_item(0), f_size(0), f_sele(0), + vbo_s(EMPTY), is_shown(false), is_active(false), fs_is_active(false) + { + bound_box = {0, 0, 0, 0}; + fieldsize = {0, 0}; + item_sel = {0, 0}; + page_sel = {0, 0}; + } }; @@ -76,25 +201,20 @@ struct _ent { int lvl; }; +typedef enum { + ITEMZCORE_GENERAL_ERROR = 0xDEADBEEF, + ITEMZCORE_PRX_RESOLVE_ERROR = 0xCCCCCCC, + ITEMZCORE_EINVAL = 0xEEEEEEE, + ITEMZCORE_QUEUE_FULL = 0xAAAAAAA, + ITEMZCORE_NULL_BUFFER = 0x123123, + ITEMZCORE_SUCCESS = 0 +} ITEMZCORE_ERRORS; +typedef struct +{ + char* name; +} entry_t; - -// freetype-gl pass last composed Text_Length in pixel, we use to align text! - -/* - GLES2 layout rework, v2 -*/ - -/* - todo: we need to differentiate and made use of vbo_status - to avoid useless pushes to gpu -*/ - -/* - auxiliary array of item_index_t: - - shared for Search/Groups - - the query to save search results; -*/ enum SH_type { USE_COLOR, @@ -120,47 +240,24 @@ typedef enum token_name // should match NUM_OF_USER_TOKENS } token_name; -typedef enum { - ITEMZCORE_GENERAL_ERROR = 0xDEADBEEF, - ITEMZCORE_PRX_RESOLVE_ERROR = 0xCCCCCCC, - ITEMZCORE_EINVAL = 0xEEEEEEE, - ITEMZCORE_QUEUE_FULL = 0xAAAAAAA, - ITEMZCORE_NULL_BUFFER = 0x123123, - ITEMZCORE_SUCCESS = 0 -} ITEMZCORE_ERRORS; - -typedef struct -{ - char* name; -} entry_t; - -typedef enum pt_status -{ - CANCELED = -1, - READY, - PAUSED, - RUNNING, - COMPLETED -} pt_status; - -typedef enum badge_t -{ - AVAILABLE = 1, - SOON, - SELECTED, - NUM_OF_BADGES -} badge_t; - typedef enum { NO_CATEGORY_FILTER, + FILTER_FAVORITES, FILTER_RETAIL_GAMES, FILTER_FPKG, FILTER_GAMES, - FILTER_HOMEBREW = 4, - NUMB_OF_CATEGORIES = 4 + FILTER_HOMEBREW, + NUMB_OF_CATEGORIES } Sort_Category; +struct IconPosition { + int offset; + float x; + float y; +}; + + #define ORBIS_LIBC_MALLOC_MANAGED_SIZE_VERSION (0x0001U) #ifndef SCE_LIBC_INIT_MALLOC_MANAGED_SIZE @@ -185,6 +282,8 @@ typedef struct LibcMallocManagedSize { size_t currentInuseSize; } LibcMallocManagedSize; + +namespace multi_select_options { typedef enum { NA_SORT = 0, TID_ALPHA_SORT, @@ -236,6 +335,7 @@ typedef enum { typedef enum { SEL_DUMP_ALL = 0, + SEL_DUMP_VALID, SEL_BASE_GAME, SEL_GAME_PATCH, SEL_REMASTER, @@ -244,22 +344,24 @@ typedef enum { SEL_RESET = 999, SEL_RUNNING_NOW = 696969 } Dump_Multi_Sels; - +}; #define FIRST_MULTI_LINE 1 #define RESET_MULTI_SEL 0 - +namespace NOTIFI{ typedef enum { - NOTIFI_INFO, - NOTIFI_WARNING, - NOTIFI_SUCCESS, - NOTIFI_ERROR, - NOTIFI_GAME, - NOTIFI_KOFI, - NOTIFI_PROFILE, + INFO, + WARNING, + SUCCESS, + ERROR, + GAME, + KOFI, + PROFILE, NUM_OF_NOTIS } NOTI_LEVELS; +}; +//NOTIFI::NOTI_LEVELS #define FIRST_MULTI_LINE 1 #define RESET_MULTI_SEL 0 @@ -299,7 +401,8 @@ typedef enum view_t ITEMzFLOW = 0, ITEM_PAGE = 1, ITEM_SETTINGS = 2, - FILE_BROWSER = 3 + FILE_BROWSER_LEFT = 3, + FILE_BROWSER_RIGHT = 4, } view_t; void GLES2_render_list(enum view_t v); @@ -315,9 +418,6 @@ enum views }; -typedef struct { float s, t; } st; -typedef struct { float x, y, z; } xyz; -typedef struct { float r, g, b, a; } rgba; typedef struct retry_t { @@ -325,27 +425,31 @@ typedef struct retry_t bool exists; } retry_t; + +namespace cf_ops { typedef enum { - Launch_Game_opt, - Dump_Game_opt, - Uninstall_opt, - Game_Save_opt, - Trainers_opt, - Hide_App_opt, - Change_icon_opt, - Change_name_opt, - Move_apps_opt, - Restore_apps_opt, + LAUNCH_GAME, + DUMP_GAME, + UNINSTALL, + GAME_SAVE, + TRAINERS, + HIDE_APP, + CHANGE_ICON, + CHANGE_NAME, + MOVE_APPS, + RESTORE_APPS, -} Ops_for_cf; +} REG_OPTS; typedef enum { - LAUNCH_HA, - REFRESH_HA, - -} Ops_for_hostapp; + LAUNCH_HA, + REFRESH_HA, +} HOSTAPP_OPTS; + + +}; // menu entry strings #define LPANEL_Y (5) @@ -403,7 +507,7 @@ void on_GLES2_Render_box(vec4* frect); void on_GLES2_Final(void); // from pixelshader.c -void pixelshader_render(GLuint SL_program, vertex_buffer_t* vbo, vec2* resolution); +void pixelshader_render(); void pixelshader_init(int width, int height); void pixelshader_fini(void); @@ -415,20 +519,20 @@ void GLES2_init_submenu(void); void ftgl_render_vbo(vertex_buffer_t* vbo, vec3* offset); void escalate_priv(void); -int Start_IF_internal_Services(int reboot_num); +int Start_IF_internal_Services(const char* launch_q); int pingtest(int libnetMemId, int libhttpCtxId, const char* src); int df(std::string mountPoint, std::string &out); -int get_item_index(layout_t* l); +int get_item_index(layout_t& l); // from GLES2_scene_v2.c -void GLES2_layout_init(int req_item_count, layout_t *l); +void GLES2_layout_init(int req_item_count, layout_t &l); -vertex_buffer_t* vbo_from_rect(vec4* rect); +VertexBuffer vbo_from_rect(vec4& rect); -void GLES2_UpdateVboForLayout(layout_t* l); +void GLES2_UpdateVboForLayout(layout_t& l); -int layout_fill_item_from_list(layout_t *l, std::vector &i_list); +int layout_fill_item_from_list(layout_t &l, std::vector &i_list); void fw_action_to_cf(int button); @@ -438,7 +542,7 @@ void GLES2_render_menu(int unused); void init_ItemzCore(int w, int h); void Render_ItemzCore(void); void fw_action_to_cf(int button); -//void X_action_dispatch(int action, layout_t *l); +//void X_action_dispatch(int action, layout_t &l); /// pthreads used in GLES2_q.c @@ -458,15 +562,15 @@ void GLES2_refresh_sysinfo(void); // disk free percentages extern double dfp_hdd, dfp_ext, dfp_now; -extern layout_t *active_p, ls_p, setting_p; +extern layout_t ls_p, setting_p; // GLES2_layout -void layout_update_fsize(layout_t* l); -void layout_update_sele(layout_t* l, int movement); +void layout_update_fsize(layout_t& l); +void layout_update_sele(layout_t& l, int movement); void GLES2_Refresh_for_settings(); -void layout_set_active(layout_t* l); +void layout_set_active(layout_t& l); void swap_panels(void); -void GLES2_render_layout_v2(layout_t* l, int unused); +void GLES2_render_layout_v2(view_t vw); // scene_v2.c @@ -492,10 +596,8 @@ typedef struct { const void* data; } RawImageData; -#if __cplusplus -extern "C" { -#endif extern float tl; +bool is_all_pkg_enabled(int panel); /* GLES2_panel.h */ extern texture_font_t *main_font, // small @@ -510,23 +612,23 @@ GLuint load_texture(const GLsizei width, const GLsizei height, // higher level helper GLuint load_png_asset_into_texture(const char* relative_path); GLuint load_png_data_into_texture(const char* data, int size); -bool is_png_vaild(const char *relative_path, int *out_width, int *out_height); +bool is_png_vaild(const char *relative_path, int *width, int *height); extern vec2 tex_size; // last loaded png size as (w, h) -int writeImage(char* filename, int width, int height, int* buffer, char* title); +int writeImage(char* filename, int width, int height, int *buffer, const char* title); /// from timing.c -unsigned int get_time_ms(void); +float get_time_ms(std::chrono::time_point &last_time); bool if_exists(const char* path); -#if __cplusplus -} -#endif -void add_text(vertex_buffer_t* buffer, texture_font_t* font, std::string text, vec4* color, vec2* pen); +extern int left_page_pos; +extern int right_page_pos; + void GLES2_ani_update(double time); void GLES2_ani_fini(void); void GLES2_ani_draw(void); -void on_GLES2_Render_icon(enum SH_type SL_program, GLuint texture, int num, vec4 *frect, vec4 *uv); + +void on_GLES2_Render_icon(enum SH_type SL_program, GLuint texture, int num, vec4 &frect, vec4 *uv); void GLES2_ani_init(int width, int height); -int ani_notify(NOTI_LEVELS lvl, std::string message, std::string detail); +int ani_notify(NOTIFI::NOTI_LEVELS lvl, std::string message, std::string detail); int index_token_from_sfo(std::string *title, std::string *apptype, std::string *version, std::string path, int lang);; /// from ls_dir() int check_stat(const char *filepath); @@ -547,21 +649,25 @@ void es2fini_lines_and_rect(void); /// GLES2_rects.c void ORBIS_RenderFillRects_init(int width, int height); void ORBIS_RenderFillRects_rndr(void); +void ORBIS_RenderDrawStar(const vec2 *coords, int size); void ORBIS_RenderFillRects_fini(void); void ORBIS_RenderDrawLines(const vec2* points, int count); -void ORBIS_RenderFillRects(enum SH_type SL_program, const vec4 *rgba, const vec4 *rects, int count); -void ORBIS_RenderDrawBox(enum SH_type SL_program, const vec4 *rgba, const vec4 *rect); -void GLES2_DrawFillingRect(vec4* frect, vec4* color, double* percentage); +void ORBIS_RenderFillRects(enum SH_type SL_program, const vec4 &rgba, std::vector &rects, int count); +void GLES2_DrawFillingRect(std::vector &r, vec4 &c, const double percentage); + void ORBIS_RenderDrawBox(enum SH_type SL_program, const vec4 &rgba, vec4 &r); void check_tex_for_reload(int idx); void check_n_load_textures(int idx); void InitScene_4(int width, int height); void InitScene_5(int width, int height); void O_action_dispatch(void); + void realloc_app_retries(); -void index_items_from_dir(std::vector &out_vec, std::string dirpath, std::string dirpath2, Sort_Category category); +void index_items_from_dir(ThreadSafeVector &out_vec, std::string dirpath, std::string dirpath2, Sort_Category category); void get_stat_from_file(std::string &out_str, std::string &filepath_str); std::string category_get_str(Sort_Category num); -std::string power_menu_get_str(Power_control_sel num); -std::string uninstall_get_str(Uninstall_Multi_Sel num); -void itemzcore_add_text(vertex_buffer_t* vert_buffer,float x, float y, std::string text); \ No newline at end of file +std::string power_menu_get_str(multi_select_options::Power_control_sel num); +std::string uninstall_get_str(multi_select_options::Uninstall_Multi_Sel num); +void itemzcore_add_text(VertexBuffer &vert_buffer,float x, float y, std::string text); +void DrawWhiteStar(); +void flip_frame(void); \ No newline at end of file diff --git a/itemzflow/include/defines.h b/itemzflow/include/defines.h index 73a3d75..c77c88f 100644 --- a/itemzflow/include/defines.h +++ b/itemzflow/include/defines.h @@ -127,7 +127,7 @@ #define APP_HOME_DATA_FOLDER "/user/app/NPXS29998" #define APP_HOME_DATA_TID "NPXS29998" #define APP_HOME_HOST_TID "NPXS29999" -#define GL_CHECK(stmt) if(glGetError() != GL_NO_ERROR) msgok(FATAL, "GL_STATEMENT %s: %x", getLangSTR(FAILED_W_CODE),glGetError()); +#define GL_CHECK(stmt) if(glGetError() != GL_NO_ERROR) msgok(MSG_DIALOG::FATAL, "GL_STATEMENT %s: %x", getLangSTR(LANG_STR::FAILED_W_CODE),glGetError()); #define PS4_OK 0 #define INIT_FAILED -1 #define DEBUG_SETTINGS_TID "NPXS20993" @@ -155,7 +155,7 @@ #define VERSION_MAJOR 1 -#define VERSION_MINOR 03 +#define VERSION_MINOR 04 #define BUILD_YEAR_CH0 (__DATE__[ 7]) #define BUILD_YEAR_CH1 (__DATE__[ 8]) @@ -341,20 +341,6 @@ typedef enum { SFO_CHANGE_GAME_NAME } SFO_PATH_OPT; -enum Settings_options -{ - CDN_SETTING, - TMP__SETTING, - HOME_MENU_SETTING, - INI_SETTING, - FNT__SETTING, - STORE_USB_SETTING, - CLEAR_CACHE_SETTING, - SHOW_INSTALL_PROG, - USE_PIXELSHADER_SETTING, - SAVE_SETTINGS, - NUM_OF_SETTINGS -}; int pingtest(int libnetMemId, int libhttpCtxId, const char* src); int32_t netInit(void); @@ -367,16 +353,15 @@ int thread_count_by_status(int req_status); int thread_dispatch_index(void); /// from GLES2_badges -int scan_for_badges(layout_t* l, item_t* apps); void GLES2_Init_badge(void); void GLES2_Render_badge(int idx, vec4* rect); /// GLES2_filemamager void fw_action_to_fm(int button); void GLES2_render_filemanager(int unused); -void GLES2_render_queue(layout_t* l, int used); +void GLES2_render_queue(layout_t& l, int used); // UI panels new way, v3 -vec4 get_rect_from_index(const int idx, const layout_t* l, vec4* io); +vec4 get_rect_from_index(const int idx, const layout_t& l, vec4* io); bool filter_entry_on_IDs(const char* entry); bool patch_sfo(const char* in_file_path, sfo_patch_t* patches); \ No newline at end of file diff --git a/itemzflow/include/feature_classes.hpp b/itemzflow/include/feature_classes.hpp new file mode 100644 index 0000000..67b01b4 --- /dev/null +++ b/itemzflow/include/feature_classes.hpp @@ -0,0 +1,527 @@ +#pragma once +#include +#include +#include +#include +#include "log.h" +#include +#include +#include +#include // links against libfreetype-gl +#include "GLES2_common.h" + +void text_gl_extra( vertex_buffer_t* buffer,int num, GLuint texture, int idx, vec4 col, GLuint sh_prog); + +class Favorites { +public: + void addFavorite(const std::string& game) { + favorites_.insert(game); + } + + void removeFavorite(const std::string& tid) { + favorites_.erase(tid); + } + + bool isFavorite(const item_t& game) const { // THIS FUNCTION IS NOT USED AFTER LOADING THE INI GAMES LIST + return favorites_.find(game.info.id) != favorites_.end(); + } + + bool saveToFile(const std::string& filename) const { + std::ofstream file(filename); + if (!file) { + log_error("Error writing file: %s", filename.c_str()); + return false; + } + for (const auto& game_name : favorites_) { + file << game_name << std::endl; + } + return true; + } + + bool loadFromFile(const std::string& filename) { + std::ifstream file(filename); + if (!file) { + log_error("Error opening file for reading: %s", filename.c_str()); + return false; + } + favorites_.clear(); + std::string line; + while (std::getline(file, line)) { + favorites_.insert(line); + } + return true; + } + +private: + std::unordered_set favorites_; +}; + +class SafeString { +public: + void set(const std::string& value) { + std::lock_guard lock(mutex_); + value_ = value; + } + + std::string get() const { + std::lock_guard lock(mutex_); + return value_; + } + +private: + mutable std::mutex mutex_; + std::string value_; +}; + + + +extern SafeString title_id; + +typedef struct { float s, t; } st; +typedef struct { float x, y, z; } xyz; +typedef struct { float r, g, b, a; } rgba; + +typedef struct +{ + xyz position, + normal; + rgba color; +} v3f3f4f_t; + +typedef struct +{ + xyz position; + st tex_uv; + rgba color; +} v3f2f4f_t; + + +// then we can print them splitted +typedef struct { + int offset; + int count; +} textline_t; + +// ------------------------------------------------------- typedef & struct --- +typedef struct { + float x, y, z; // position (3f) + float s, t; // texture (2f) + float r, g, b, a; // color (4f) +} vertex_t; + +void ftgl_render_vbo(vertex_buffer_t* vbo, vec3* offset); + +extern GLuint sh_prog1, + sh_prog2, + sh_prog3, + sh_prog4, + sh_prog5; + +class VertexBuffer { + public: std::unique_ptr < vertex_buffer_t, + decltype( & vertex_buffer_delete) > buffer { + nullptr, + vertex_buffer_delete + }; + + VertexBuffer() = default; + vec4 c[8]; + + VertexBuffer(const char * format) { + if (!buffer) + buffer.reset(vertex_buffer_new(format)); + } + + void render(GLenum mode) { + if (buffer) + vertex_buffer_render(buffer.get(), mode); + else + log_error("vertex_buffer_render failed: Buffer is NULL"); + } + + void render_setup(GLenum mode) { + if (buffer) + vertex_buffer_render_setup(buffer.get(), mode); + } + + void render_item(int numb) { + if (buffer) + vertex_buffer_render_item(buffer.get(), numb); + } + + void render_finish() { + if (buffer) + vertex_buffer_render_finish(buffer.get()); + } + + // You might need to replace texture_font_t and texture_glyph_t + // with your actual definitions + void add_text(struct texture_font_t * font, std::string text, vec4 & color, vec2 & pen) { + if (!font || !buffer) { + log_error("add_text failed: Font is %s, Buffer is %s", font ? "OK" : "NULL", buffer ? "OK" : "NULL"); + return; + } + + // log_info("message: %s", text.c_str()); + size_t i; + float r = color.r, g = color.g, b = color.b, a = color.a; + + for (i = 0; i < text.length(); ++i) { + texture_glyph_t * glyph = texture_font_get_glyph(font, (const char * ) & text.at(i)); + if (glyph) { + float kerning = 0.0f; + if (i > 0) kerning = texture_glyph_get_kerning(glyph, (const char * ) & text.at(i - 1)); + pen.x += kerning; + int x0 = (int)(pen.x + glyph -> offset_x); + int y0 = (int)(pen.y + glyph -> offset_y); + int x1 = (int)(x0 + glyph -> width); + int y1 = (int)(y0 - glyph -> height); + float s0 = glyph -> s0; + float t0 = glyph -> t0; + float s1 = glyph -> s1; + float t1 = glyph -> t1; + GLuint indices[6] = { 0, 1, 2, 0, 2,3 }; // (two triangles) + /* VBO is setup as: "vertex:3f, tex_coord:2f, color:4f" */ + vertex_t vertices[4]; + + vertices[0].x = vertices[1].x = x0; + vertices[2].x = vertices[3].x = x1; + + vertices[0].y = vertices[3].y = y0; + vertices[1].y = vertices[2].y = y1; + + vertices[0].z = vertices[1].z = vertices[2].z = vertices[3].z = 0; + + vertices[1].s = vertices[0].s = s0; + vertices[2].s = vertices[3].s = s1; + + vertices[1].t = vertices[2].t = t1; + vertices[0].t = vertices[3].t = t0; + + vertices[0].r = vertices[1].r = vertices[2].r = vertices[3].r = r; + vertices[0].g = vertices[1].g = vertices[2].g = vertices[3].g = g; + vertices[0].b = vertices[1].b = vertices[2].b = vertices[3].b = b; + vertices[0].a = vertices[1].a = vertices[2].a = vertices[3].a = a; + + vertex_buffer_push_back(buffer.get(), vertices, 4, indices, 6); + pen.x += glyph -> advance_x; + } + } + } + + void render_vbo(vec3 * offset) { + if (buffer) + ftgl_render_vbo(buffer.get(), offset); + } + + void clear() { + if (buffer) + buffer.reset(); + } + + void push_back(const void * vertices, size_t vcount, + const GLuint * indices, size_t icount) { + if (buffer) { + vertex_buffer_push_back(buffer.get(), vertices, vcount, indices, icount); + } + } + + bool empty() const { + return buffer == nullptr; + } + + explicit operator bool() const { + return static_cast < bool > (buffer); + } + +void append_to_vbo(vec3 *v) +{ + float s0 = 0., t0 = 0., + s1 = 1., t1 = 1.; + GLuint idx[6] = {0, 1, 2, 0, 2, 3}; + v3f2f4f_t vtx[4]; + + vtx[0].position = (xyz){(v + 0)->x, (v + 0)->y, (v + 0)->z}; + vtx[0].color = (rgba){c[0].r, c[0].g, c[0].b, c[0].a}; + vtx[0].tex_uv = (st){s1, t0}; + + vtx[1].position = (xyz){(v + 1)->x, (v + 1)->y, (v + 1)->z}; + vtx[1].color = (rgba){c[1].r, c[1].g, c[1].b, c[1].a}; + vtx[1].tex_uv = (st){s0, t0}; + + vtx[2].position = (xyz){(v + 2)->x, (v + 2)->y, (v + 2)->z}; + vtx[2].color = (rgba){c[2].r, c[2].g, c[2].b, c[2].a}; + vtx[2].tex_uv = (st){s0, t1}; + + vtx[3].position = (xyz){(v + 3)->x, (v + 3)->y, (v + 3)->z}; + vtx[3].color = (rgba){c[3].r, c[3].g, c[3].b, c[3].a}; + vtx[3].tex_uv = (st){s1, t1}; + + vertex_buffer_push_back(buffer.get(), vtx, 4, idx, 6); +} + +void render_tex(GLuint texture, int idx, vec4 col) +{ + // we already clean in main renderloop()! + + GLuint sh_prog = sh_prog2; + if (idx == -1) // reflection + sh_prog = sh_prog3; + else // fake shadow + if (idx == -2) + sh_prog = sh_prog4; + else + if (idx == -3) + sh_prog = sh_prog5; + + glUseProgram(sh_prog); + text_gl_extra(buffer.get(), 2, texture, idx, col, sh_prog); + +} + + +}; + + +class Base64 { + public: + + static std::string Encode(const std::string data) { + static constexpr char sEncodingTable[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + size_t in_len = data.size(); + size_t out_len = 4 * ((in_len + 2) / 3); + std::string ret(out_len, '\0'); + size_t i; + char *p = const_cast(ret.c_str()); + + for (i = 0; i < in_len - 2; i += 3) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)]; + *p++ = sEncodingTable[data[i + 2] & 0x3F]; + } + if (i < in_len) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + if (i == (in_len - 1)) { + *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; + *p++ = '='; + } + else { + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + return ret; + } + + static std::string Decode(const std::string& input, std::string& out) { + static constexpr unsigned char kDecodingTable[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + + size_t in_len = input.size(); + if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; + + size_t out_len = in_len / 4 * 3; + if (input[in_len - 1] == '=') out_len--; + if (input[in_len - 2] == '=') out_len--; + + out.resize(out_len); + + for (size_t i = 0, j = 0; i < in_len;) { + uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + + uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); + + if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; + } + + return ""; + } + +}; + +template +class ThreadSafeVector { +private: + std::vector data; + mutable std::mutex mtx; +public: + + ThreadSafeVector() = default; + ThreadSafeVector(size_t n) { + std::lock_guard lock(mtx); + data.resize(n); + } + void push_back(const T& value) { + std::lock_guard lock(mtx); + data.push_back(value); + } + + bool empty() const { + std::lock_guard lock(mtx); + return data.empty(); + } + + T& operator[](size_t i) { + static T default_value; + std::lock_guard lock(mtx); + try { + return data.at(i); + } catch (const std::out_of_range&) { + log_error("ThreadSafeVector: Out of bounds"); + return default_value; // Return the default value if the index is out of bounds + } +} + + const T& operator[](size_t i) const { + static T default_value; + std::lock_guard lock(mtx); + try { + return data.at(i); + } catch (const std::out_of_range&) { + log_error("ThreadSafeVector: Out of bounds"); + return default_value; // Return the default value if the index is out of bounds + } +} + + size_t size() const { + std::lock_guard lock(mtx); + return data.size(); + } + +ThreadSafeVector(const ThreadSafeVector& other) { + std::lock_guard lock_other(other.mtx); + data = other.data; +} + +ThreadSafeVector& operator=(const ThreadSafeVector& other) { + if (&other == this) { + return *this; + } + + std::unique_lock lock(mtx, std::defer_lock); + std::unique_lock lock_other(other.mtx, std::defer_lock); + std::lock(lock, lock_other); + data = other.data; + + return *this; +} +operator std::vector() const { + std::lock_guard lock(mtx); + return data; +} + +ThreadSafeVector& operator=(const std::vector& other) { + std::lock_guard lock(mtx); + data = other; + return *this; +} + +typename std::vector::iterator begin() { + std::lock_guard lock(mtx); + return data.begin(); +} + +typename std::vector::iterator end() { + std::lock_guard lock(mtx); + return data.end(); +} + +typename std::vector::const_iterator begin() const { + std::lock_guard lock(mtx); + return data.begin(); +} + +typename std::vector::const_iterator end() const { + std::lock_guard lock(mtx); + return data.end(); +} + +T& at(size_t i) { + static T default_value; + std::lock_guard lock(mtx); + try { + return data.at(i); + } catch (const std::out_of_range&) { + log_error("ThreadSafeVector: Out of bounds"); + return default_value; // Return the default value if the index is out of bounds + } +} + +const T& at(size_t i) const { + static T default_value; + std::lock_guard lock(mtx); + try { + return data.at(i); + } catch (const std::out_of_range&) { + log_error("ThreadSafeVector: Out of bounds"); + return default_value; // Return the default value if the index is out of bounds + } +} + +void resize(size_t new_size) { + //log_info("resize to %i", new_size); + std::lock_guard lock(mtx); + data.resize(new_size); +} + +void clear() { + std::lock_guard lock(mtx); + data.clear(); +} +}; + + +class Timer{ + public: + Timer(){ + m_StartTimepoint = std::chrono::high_resolution_clock::now(); + } + ~Timer(){ + Stop(); + } + + void Stop(){ + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - m_StartTimepoint); + double ms = duration.count() * 0.001; + + log_info("took %.2f ms", ms); + } + +private: + std::chrono::time_point m_StartTimepoint; +}; diff --git a/itemzflow/include/fmt/chrono.h b/itemzflow/include/fmt/chrono.h index 83bf4ab..65f5268 100644 --- a/itemzflow/include/fmt/chrono.h +++ b/itemzflow/include/fmt/chrono.h @@ -1373,7 +1373,7 @@ template class tm_writer { }; struct chrono_format_checker : null_chrono_spec_handler { - FMT_NORETURN void unsupported() { log_error("no date"); raise(SIGUSR2); } + FMT_NORETURN void unsupported() { log_error("no date"); raise(SIGUSR2); throw std::runtime_error("no date"); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} diff --git a/itemzflow/include/fmt/format-inl.h b/itemzflow/include/fmt/format-inl.h index 80b30b1..dded0bb 100644 --- a/itemzflow/include/fmt/format-inl.h +++ b/itemzflow/include/fmt/format-inl.h @@ -37,6 +37,7 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) { // writing to stderr fails log_error("[ERR] %s:%d: assertion failed: %s", file, line, message); raise(SIGUSR2); + throw std::runtime_error("assertion failed"); } FMT_FUNC void throw_format_error(const char* message) { diff --git a/itemzflow/include/installpkg.h b/itemzflow/include/installpkg.h index c046226..61f243c 100644 --- a/itemzflow/include/installpkg.h +++ b/itemzflow/include/installpkg.h @@ -80,7 +80,10 @@ struct bgft_init_params { }; typedef struct install_args { - char* title_id, *fname; int task_id; char* path; bool is_thread, delete_pkg; void* bgft_heap; + char* title_id, *fname,* path; + bool is_thread = false, delete_pkg = true; + void* bgft_heap; + int max_pkgs = 1, curr_pkg = 1, task_id = -1; }install_args; struct bgft_download_task_progress_info { @@ -187,4 +190,4 @@ int sceBgftServiceTerm(void); #if defined(__cplusplus) } #endif -uint32_t pkginstall(const char *fullpath, const char* filename, bool Show_install_prog, bool delete_pkg_after); \ No newline at end of file +uint32_t pkginstall(const char *fullpath, const char* filename, bool Show_install_prog, bool delete_pkg_after, int max_pkgs = 1, int curr_pkg = 1); \ No newline at end of file diff --git a/itemzflow/include/ipc_client.hpp b/itemzflow/include/ipc_client.hpp new file mode 100644 index 0000000..1ce2ee0 --- /dev/null +++ b/itemzflow/include/ipc_client.hpp @@ -0,0 +1,219 @@ +#pragma once +#ifndef IPC_HEADER_H +#define IPC_HEADER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum class IPC_Ret { + INVALID = -1, + NO_ERROR = 0, + OPERATION_FAILED = 1, + FUSE_IP_ALREADY_SET = 3, + FUSE_IP_NOT_SET = 4, + FUSE_FW_NOT_SUPPORTED = 5, + DEAMON_UPDATING = 100 +}; + +enum class IPC_Commands { + MACGUFFIN_CMD = 69, + CMD_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, +}; + +constexpr size_t DAEMON_BUFF_MAX = 0x100; // Assuming a buffer max size, you can change this. + +struct IPCMessage{ + int magic = 0xDEADBABE; + IPC_Commands cmd; + IPC_Ret error = IPC_Ret::INVALID; + char msg[DAEMON_BUFF_MAX] = {0}; +}; + + +const char IPC_SOC[] = "/system_tmp/IPC_Socket"; +static int DaemonSocket = -1; + +class IPC_Client{ +private: + mutable std::mutex socketMutex; + +public: + +// Socket Management +int OpenConnection(const char* path) { + sockaddr_un server; + int soc = socket(AF_UNIX, SOCK_STREAM, 0); + if (soc == -1) { + log_error("Failed to create socket"); + return -1; + } + server.sun_family = AF_UNIX; + strncpy(server.sun_path, path, sizeof(server.sun_path)-1); + if (connect(soc, (struct sockaddr*)&server, SUN_LEN(&server)) == -1) { + close(soc); + log_error("Failed to connect to socket"); + return -1; + } + return soc; +} + +// IPC Functions +bool IPCOpenConnection() { + DaemonSocket = OpenConnection(IPC_SOC); + return DaemonSocket >= 0; +} + +bool IPCOpenIfNotConnected() { + if(DaemonSocket >= 0) { + return true; + } + return IPCOpenConnection(); +} +int IPCReceiveData(IPCMessage& msg) { + return recv(DaemonSocket, reinterpret_cast(&msg), sizeof(msg), MSG_NOSIGNAL); +} + +int IPCSendData(const IPCMessage& msg) { + int ret = send(DaemonSocket, reinterpret_cast(&msg), sizeof(msg), MSG_NOSIGNAL); + if (ret < 0) { + log_error("IPCSendData failed with: %s", strerror(errno)); + } + return ret; +} + +IPC_Ret IPCCloseConnection() { + if (DaemonSocket < 0) { + return IPC_Ret::INVALID; + } + + close(DaemonSocket); + DaemonSocket = -1; + return IPC_Ret::NO_ERROR; +} + + +IPC_Ret IPCSendCommand(IPC_Commands cmd, std::string& ipc_msg, bool silent = false) { + std::lock_guard lock(socketMutex); + + IPCMessage msg; + msg.cmd = cmd; + // Copy the string content into the destination array + strncpy(msg.msg, ipc_msg.c_str(), sizeof(msg.msg) - 1); + // Null-terminate the destination array + msg.msg[sizeof(msg.msg) - 1] = '\0'; + + IPC_Ret error = IPC_Ret::INVALID; + if (IPCOpenIfNotConnected()) { + if(!silent) + log_debug(" Sending IPC Command"); + + IPCSendData(msg); + + if(!silent) + log_debug(" Sent IPC Command %i", static_cast(cmd)); + + ipc_msg.clear(); + + if (cmd == IPC_Commands::DEAMON_UPDATE) { + log_debug(" Daemon Updating..."); + IPCCloseConnection(); + return IPC_Ret::DEAMON_UPDATING; + } + + // Get message back from daemon + uint32_t readSize = IPCReceiveData(msg); + if (cmd == IPC_Commands::RESTART_FUSE_FS) { + log_debug("Restarting FUSE"); + IPCCloseConnection(); + } + if (static_cast(readSize) >= 0) { + if(!silent) + log_debug(" Got message with Size: %i from Daemon", readSize); + // Get IPC Error code + error = msg.error; + if (error != IPC_Ret::INVALID) { + // Modifies the Buffer to exclude the error code + if(error == IPC_Ret::NO_ERROR) { + ipc_msg = msg.msg; + } + else { + ipc_msg = "Error"; + } + if(!silent) + log_debug("[ItemzDaemon] Daemon IPC Response: %s, code: %s, readSize: %i", msg.msg, error == IPC_Ret::NO_ERROR ? "NO_ERROR" : "Other", readSize); + } else { + log_error("[ItemzDaemon] Daemon returned INVAL"); + } + } else { + if(!silent) + log_error("IPCReceiveData failed with: %s", strerror(errno)); + IPCCloseConnection(); + } + } + if (cmd == IPC_Commands::CMD_SHUTDOWN_DAEMON) { + IPCCloseConnection(); + } + + return error; +} + +IPC_Ret IPCMountFUSE(std::string ip, std::string path, bool debug_mode) { + std::string in; + IPC_Ret error = IPC_Ret::INVALID; + IPCSendCommand(IPC_Commands::FUSE_SET_SESSION_IP, ip); + if (debug_mode) { + if (IPCSendCommand(IPC_Commands::FUSE_SET_DEBUG_FLAG, in) == IPC_Ret::NO_ERROR) { + log_debug("FUSE debug mode enabled"); + } else { + log_error("FUSE failed to enable debug mode"); + } + } + + // Set the mount point + // Copy the string content into the destination array + if ((error = IPCSendCommand(IPC_Commands::FUSE_START_W_PATH, path)) == IPC_Ret::NO_ERROR) { + log_debug("FUSE started"); + } else { + log_error("FUSE failed to start with error code: %i", static_cast(error)); + } + + return error; +} +// Deleted copy constructor and assignment operator to ensure only one instance + IPC_Client& operator=(const IPC_Client&) = delete; + + // Static method to access the instance + static IPC_Client& getInstance() { + static IPC_Client instance; // Lazy-loaded instance, guaranteed to be destroyed + return instance; + } +// ... (Placeholder for remaining functions) + +}; // namespace IPC + +#endif // IPC_HEADER_H diff --git a/itemzflow/include/lang.h b/itemzflow/include/lang.h index b8406f5..3081999 100644 --- a/itemzflow/include/lang.h +++ b/itemzflow/include/lang.h @@ -1,7 +1,8 @@ #pragma once #include -enum Lang_STR +namespace LANG_STR { +typedef enum { RSA_LOAD, RSA_FAILED, @@ -161,7 +162,7 @@ enum Lang_STR DUMP_1, PSV_MENU, SELECT_INTERACT, - GAME_SAVE_OPTS, + GAME_SAVE, BACKUP_SAVE, IMPORT_SAVE, SAVE_DELETE, @@ -465,9 +466,17 @@ enum Lang_STR REFRESH_HOSTAPP_DESC, VAPP_MENU, LNC_TOO_MANY_ROOT_FILES, + INSTALL_ALL_PKGS, + INSTALL_ALL_PKGS_DEC, + FAVORITES, + FAVORITES_DESC, + FAVORITES_ADD, + FAVORITES_REMOVE, + FAVORITES_REMOVED, + SEL_DUMP_VALID1, // DO NOT DELETE THIS, ALWAYS KEEP AT THE BOTTOM LANG_NUM_OF_STRINGS -}; +} LANG_STRINGS; static const char *lang_key[LANG_NUM_OF_STRINGS] = { @@ -629,7 +638,7 @@ static const char *lang_key[LANG_NUM_OF_STRINGS] = "DUMP_1", "PSV_MENU", "SELECT_INTERACT", - "GAME_SAVE_OPTS", + "GAME_SAVES_OPTS", "BACKUP_SAVE", "IMPORT_SAVE", "SAVE_DELETE", @@ -933,6 +942,15 @@ static const char *lang_key[LANG_NUM_OF_STRINGS] = "REFRESH_HOSTAPP_DESC", "VAPP_MENU", "LNC_TOO_MANY_ROOT_FILES", + "INSTALL_ALL_PKGS", + "INSTALL_ALL_PKGS_DEC", + "FAVORITES", + "FAVORITES_DESC", + "FAVORITES_ADD", + "FAVORITES_REMOVE", + "FAVORITES_REMOVED", + "SEL_DUMP_VALID1", +}; }; #define NUMBER_OF_SETTINGS 12 @@ -942,6 +960,8 @@ static const char *lang_key[LANG_NUM_OF_STRINGS] = //#define OVERRIDE_LANG 3 // 0 JAP //#define TEST_INI_LANGS 1 bool LoadLangs(int LangCode); -std::string getLangSTR(enum Lang_STR str); +// +std::string getLangSTR(LANG_STR::LANG_STRINGS str); +std::string Language_GetName(int m_Code); int32_t PS4GetLang(); bool load_embdded_eng(); diff --git a/itemzflow/include/stb_image.h b/itemzflow/include/stb_image.h new file mode 100644 index 0000000..1f44c87 --- /dev/null +++ b/itemzflow/include/stb_image.h @@ -0,0 +1,7987 @@ +/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data); +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two signed shorts is valid, 0 on overflow. +static int stbi__mul2shorts_valid(short a, short b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + while (x == 255) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } + + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/itemzflow/include/utils.h b/itemzflow/include/utils.h index b468747..e1accaf 100644 --- a/itemzflow/include/utils.h +++ b/itemzflow/include/utils.h @@ -22,10 +22,10 @@ #include #ifdef __ORBIS__ #include +#include +#include "ipc_client.hpp" #endif -#if defined(__cplusplus) -extern "C" { -#endif +#include "ipc_client.hpp" #define DIM(x) (sizeof(x)/sizeof(*(x))) #define KB(x) ((size_t) (x) << 10) #define MB(x) ((size_t) (x) << 20) @@ -78,48 +78,17 @@ typedef struct enum AppType appType; } AppStatus; -enum MSG_DIALOG { +namespace MSG_DIALOG { + +typedef enum { NORMAL, FATAL, WARNING +} MSG_DIALOG_TYPE; + }; extern uint32_t daemon_appid; -enum IPC_Ret -{ - INVALID = -1, - NO_ERROR = 0, - OPERATION_FAILED = 1, - FUSE_IP_ALREADY_SET = 3, - FUSE_IP_NOT_SET = 4, - FUSE_FW_NOT_SUPPORTED = 5, - DEAMON_UPDATING = 100 -}; - - -enum IPC_Commands -{ - - MACGUFFIN_CMD = 69, - CMD_SHUTDOWN_DAEMON = 0, - 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, -}; - - - - bool IPCOpenIfNotConnected(); typedef struct SceNetEtherAddr { @@ -170,7 +139,7 @@ typedef enum { bool AppDBVisiable(std::string tid, APP_DB_VIS opt, int write_value); -//#define assert(expr) if (!(expr)) msgok(FATAL, "Assertion Failed!"); +//#define assert(expr) if (!(expr)) msgok(MSG_DIALOG::FATAL, "Assertion Failed!"); #define YES 1 #define NO 2 @@ -215,6 +184,7 @@ enum CHECK_OPTS #define SCE_SYSMODULE_INTERNAL_COMMON_DIALOG 0x80000018 #define SCE_SYSMODULE_INTERNAL_SYSUTIL 0x80000018 +extern int g_idx; // indexed options /* @@ -230,7 +200,7 @@ typedef enum Dump_Options { } Dump_Options; */ -extern Dump_Multi_Sels dump; +extern multi_select_options::Dump_Multi_Sels dump; enum SORT_APPS_BY { SORT_NA = -1, @@ -321,9 +291,9 @@ typedef struct std::unordered_map setting_bools; int lang, FTP_Port, ItemzCore_AppId, ItemzDaemon_AppId; - Sort_Multi_Sel sort_by; - Dump_Multi_Sels Dump_opt; - Uninstall_Multi_Sel un_opt; + multi_select_options::Sort_Multi_Sel sort_by; + multi_select_options::Dump_Multi_Sels Dump_opt; + multi_select_options::Uninstall_Multi_Sel un_opt; Sort_Category sort_cat; // more options @@ -341,36 +311,11 @@ extern bool current_app_is_fpkg; bool LoadOptions(ItemzSettings *set); bool SaveOptions(ItemzSettings *set); bool Keyboard(const char* Title, const char* initialTextBuffer, char* out_buffer, bool keypad = false); -IPC_Ret IPCMountFUSE(const char* ip, const char* path, bool debug_mode); // sysctl -uint32_t ps4_fw_version(void); -bool is_goldhen_loaded(); -bool copy_dir(const char* sourcedir, const char* destdir); extern bool is_connected_app; -size_t CalcAppsize(const char* filename); -const char* cutoff(const char* str, int from, int to); -int getjson(int Pagenumb, char* cdn, bool legacy); bool MD5_hash_compare(const char* file1, const char* hash); -bool copyFile(std::string source, std::string dest, bool show_progress); -void ProgSetMessagewText(int prog, const char* fmt, ...); -bool app_inst_util_uninstall_patch(const char* title_id, int* error); -bool app_inst_util_uninstall_game(const char *title_id, int *error); -int check_store_from_url(int page_number, char* cdn, enum CHECK_OPTS opt); -int check_download_counter(ItemzSettings* set, char* title_id); -bool rmtree(const char path[]); -void setup_store_assets(ItemzSettings* get); -int GetMessageOption(); -int OpenConnection(const char* name); -bool IPCOpenConnection(); -int IPCReceiveData(uint8_t* buffer, int32_t size); -int IPCSendData(uint8_t* buffer, int32_t size); -int IPCCloseConnection(); -#define DAEMON_BUFF_MAX 100 -void GetIPCMessageWithoutError(uint8_t* buf, uint32_t sz); -int mountfs(const char* device, const char* mountpoint, const char* fstype, const char* mode, uint64_t flags); -int check_free_space(const char* mountPoint); unsigned int usbpath(); #define MAX_MESSAGE_SIZE 0x1000 #define MAX_STACK_FRAMES 60 @@ -415,14 +360,11 @@ typedef struct OrbisUserServiceInitializeParams { } OrbisUserServiceInitializeParams; -int sceUserServiceGetRegisteredUserIdList(OrbisUserServiceRegisteredUserIdList *); -int sceUserServiceGetUserName(int, char *, const size_t); #define SCE_LNC_UTIL_ERROR_ALREADY_RUNNING 0x8094000c #define USER_PATH_HDD "/system_data/savedata/%08x/db/user/savedata.db" extern std::string title; -extern std::string title_id; extern std::string pic_path; extern std::string icon_path; @@ -441,7 +383,8 @@ void SIG_Handler(int sig_numb); int ItemzLocalKillApp(uint32_t appid); int Close_Game(); -extern pthread_mutex_t notifcation_lock, disc_lock, usb_lock; +//extern pthread_mutex_t notifcation_lock, disc_lock, usb_lock; +extern std::mutex disc_lock, notifcation_lock, usb_lock; typedef struct { unsigned int size; @@ -472,7 +415,7 @@ typedef enum typedef struct { - layout_t *last_layout; + layout_t * last_layout; int (*fs_func)(std::string file, std::string full_path); std::string selected_file, selected_full_path; view_t last_v; @@ -491,10 +434,13 @@ typedef enum OTHER_APP_OPEN, } GameStatus; -extern GameStatus app_status; +extern std::atomic app_status; bool is_sfo(const char* input_file_name); int ItemzLaunchByUri(const char *uri); +#ifdef __cplusplus +extern "C" { +#endif int sceUserServiceGetForegroundUser(u32 *userid); int32_t sceSystemServiceHideSplashScreen(); int32_t sceSystemServiceParamGetInt(int32_t paramId, int32_t *value); @@ -552,10 +498,29 @@ int64_t sys_dynlib_dlsym(int64_t moduleHandle, const char* functionName, void* d uint32_t sceKernelGetCpuTemperature(uint32_t* res); int sceKernelAvailableFlexibleMemorySize(size_t* free_mem); int sceAppInstUtilAppExists(const char* tid, int* flag); +int sceKernelOpenEventFlag(void* event, const char* name); +int sceKernelCloseEventFlag(void* event); +int sceKernelPollEventFlag(void* ef, uint64_t bitPattern, uint32_t waitMode, uint64_t *pResultPat); +int GetMountedUsbMassStorageIndex(); +int sceSystemServiceLaunchApp(const char* titleId, const char* argv[], LncAppParam* param); +int sceShellCoreUtilRequestEjectDevice(const char* devnode); +unsigned char* orbisFileGetFileContent(const char* filename); +extern size_t _orbisFile_lastopenFile_size; +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); +uint64_t sceKernelReadTsc(void); +uint64_t sceKernelGetProcessTime(void); +uint64_t sceKernelGetTscFrequency(void); +int64_t sceKernelSendNotificationRequest(int64_t unk1, const char* Buffer, size_t size, int64_t unk2); +int sceUserServiceGetRegisteredUserIdList(OrbisUserServiceRegisteredUserIdList *); +int sceUserServiceGetUserName(int, char *, const size_t); +#ifdef __cplusplus +} +#endif - -const char* Language_GetName(int m_Code); -int progstart(char* format, ...); +int progstart(const char* format, ...); int app_inst_util_get_size(const char* title_id); bool app_inst_util_uninstall_ac(const char* content_id, const char* title_id, int* error); bool app_inst_util_uninstall_game(const char *title_id, int *error); @@ -563,28 +528,14 @@ bool touch_file(const char* destfile); void dump_frame(); void trigger_dump_frame(); -/*=================== IPC SHIT================================*/ -void GetIPCMessageWithoutError(uint8_t* buf, uint32_t sz); -bool IPCOpenConnection(); -int IPCReceiveData(uint8_t* buffer, int32_t size); -int IPCSendData(uint8_t* buffer, int32_t size); -int IPCCloseConnection(); -IPC_Ret IPCSendCommand(enum IPC_Commands cmd, uint8_t* IPC_BUFFER); -/*===================++++++=================================*/ extern bool reboot_app; bool IS_ERROR(uint32_t ret); -extern vertex_buffer_t *test_v; // pixelshader vbo bool MD5_file_compare(const char* file1, const char* file2); -void Kill_BigApp(GameStatus &status); +void Kill_BigApp(std::atomic &status); void render_button(GLuint btn, float h, float w, float x, float y, float multi); bool GameSave_Info(save_entry_t *item, int off); -int sceKernelOpenEventFlag(void* event, const char* name); -int sceKernelCloseEventFlag(void* event); -int sceKernelPollEventFlag(void* ef, uint64_t bitPattern, uint32_t waitMode, uint64_t *pResultPat); -int GetMountedUsbMassStorageIndex(); -int sceSystemServiceLaunchApp(const char* titleId, const char* argv[], LncAppParam* param); bool extract_zip(const char* zip_file, const char* dest_path); -bool power_settings(Power_control_sel sel); +bool power_settings(multi_select_options::Power_control_sel sel); void relaunch_timer_thread(); enum update_ret{ IF_UPDATE_FOUND, @@ -595,15 +546,6 @@ update_ret check_update_from_url(const char* tid); void launch_update_thread(); bool shellui_for_lockdown(bool unpatch); void mkdirs(const char* dir); -int sceShellCoreUtilRequestEjectDevice(const char* devnode); - -unsigned char* orbisFileGetFileContent(const char* filename); -extern size_t _orbisFile_lastopenFile_size; -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); bool addcont_dlc_rebuild(const char* db_path, bool ext_hdd); bool Reactivate_external_content(bool is_addcont_open); void Stop_Music(); @@ -668,10 +610,6 @@ struct current_song_t { extern current_song_t current_song; -///int fuse_test(); -#if defined(__cplusplus) -} -#endif enum mp3_status_t{ MP3_STATUS_STARTED, MP3_STATUS_PLAYING, @@ -681,33 +619,40 @@ enum mp3_status_t{ #ifndef __ORBIS__ void sceMsgDialogTerminate(void); #endif -extern std::vector mp3_playlist; +extern ThreadSafeVector mp3_playlist; double CalcFreeGigs(const char* path); bool is_fpkg(std::string pkg_path); +bool app_inst_util_uninstall_patch(const char* title_id, int* error); bool patch_lnc_debug(); extern bool is_remote_va_launched; +int check_free_space(const char *mountPoint); bool do_update(std::string url); -void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path); +void SaveData_Operations(multi_select_options::SAVE_Multi_Sel sv, std::string tid, std::string path); struct sockaddr_in IPCAddress(uint16_t port); bool getEntries(std::string path, std::vector &cvEntries, FS_FILTER filter, bool for_fs_browser); bool is_if_vapp(std::string tid); -void delete_apps_array(std::vector &b); -void index_items_from_dir_v2(std::string dirpath, std::vector &out_vec); -void refresh_apps_for_cf(Sort_Multi_Sel op, Sort_Category cat = NO_CATEGORY_FILTER); +void delete_apps_array(ThreadSafeVector &b); +void index_items_from_dir_v2(std::string dirpath, ThreadSafeVector &out_vec); +void refresh_apps_for_cf(multi_select_options::Sort_Multi_Sel op, Sort_Category cat = NO_CATEGORY_FILTER); bool change_game_name(std::string new_title, std::string tid, std::string sfo_path); bool If_Save_Exists(const char *userPath, save_entry_t *item); uint32_t Launch_App(std::string TITLE_ID, bool silent = false, int index = 0 ); -bool StartFileBrowser(view_t vt, layout_t *l, int (*callback)(std::string fn, std::string fp), FS_FILTER filter); +bool StartFileBrowser(view_t vt, layout_t & l, int (*callback)(std::string fn, std::string fp), FS_FILTER filter); mp3_status_t MP3_Player(std::string path); bool install_IF_Theme(std::string theme_path); void notify(const char* message); // https://github.com/OSM-Made/PS4-Notify/blob/c6d259bc5bd4aa519f5b0ce4f5e27ef7cb01ffdd/Notify.cpp void notifywithicon(const char* IconURI, const char* MessageFMT, ...); -void msgok(enum MSG_DIALOG level, std::string in); +typedef struct Button{ + wchar_t *label; + int result; +}Button; +void msgok(MSG_DIALOG::MSG_DIALOG_TYPE level, std::string in); +int Messagebox(const char* title, const wchar_t* text, const Button* buttons, int numButtons); void loadmsg(std::string in); std::vector readFile(std::string filename); std::ifstream::pos_type file_size(const char* filename); -bool Fix_Game_In_DB(std::vector &app_info, int index, bool is_ext_hdd); +bool Fix_Game_In_DB(ThreadSafeVector &app_info, int index, bool is_ext_hdd); int check_syscalls(); int mount_fuse(const char* device, const char* mountpoint); extern int (*rejail_multi)(void); @@ -723,3 +668,16 @@ std::string sanitizeString(const std::string& str); void GLES2_Draw_idle_info(void); bool get_ip_address(std::string &ip); int get_folder_size(const char *file_path, u64 *size); +extern ThreadSafeVector all_apps; +std::vector> query_dlc_database(const std::string& title_id); +uint32_t ps4_fw_version(void); +bool is_png_vaild(const char *relative_path, int *width, int *height); +layout_t& getLayoutbyView(view_t view); +bool copyFile(std::string source, std::string dest, bool show_progress); +void ProgSetMessagewText(int prog, const char* fmt, ...); +bool rmtree(const char path[]); +void scan_for_disc(); +unsigned char* load_png_asset(const char* relative_path, std::atomic &is_loaded, struct AppIcon::ImageData& data); +void load_png_cover_data_into_texture(struct AppIcon::ImageData& data, std::atomic& needs_loaded, std::atomic& tex); +void ProgressUpdate(uint32_t prog, std::string fmt); +#define UNUSED(x) (void)X \ No newline at end of file diff --git a/itemzflow/lang.ini b/itemzflow/lang.ini index ba06d2b..2c9c464 100644 --- a/itemzflow/lang.ini +++ b/itemzflow/lang.ini @@ -55,7 +55,7 @@ PIG_FAIL=Piglet (retail) failed to load with NO_USB=No USB Detected, Exiting... DUMP=AFTER the Game launches when you press OK wait few seconds then go back to the Store and the game will be begin dumping DO NOT CLOSE THE GAME OR STORE UNTIL THE DUMP IS COMPLETE UNINSTALL_UPDATE=Game Update Uninstalled successfully -UNINSTAL_UPDATE_FAILED=Game Update Uninstall +UNINSTAL_UPDATE_FAILED=Game Update Uninstall failed UNINSTALL_SUCCESS=Game Uninstalled successfully, the installed App list will now reload UNINSTALL_FAILED=Game Uninstall failed COMING_SOON=Coming soon @@ -150,12 +150,12 @@ SETTINGS=Settings FTP_NOT_RUNNING=FTP Not Running (OFF) FTP_RUNNING=FTP Is Running (ON) DUMPER_PATH_DESC=Select a Custom Dump Path -REBUILD_DB_OPTION_DESC=Tools to repair the PS4s Database(s) +REBUILD_DB_OPTION_DESC=Rebuild the PS4s App Database (app.db) SORT_BY_OPTION_DESC=Select How Itemzflow Sorts Apps (slows down opening IF) HOME_MENU_OPTION_DESC=Redirects the PS Home Button to this App FTP_SERVICE_OPTION_DESC=Turn ON or OFF FTP Temporarily TTF_OPTION_DESC=Select a Custom TTF Font Path -BACKGROUND_MP3_OPTION_DESC=Select a single 48Hz MP3 or a whole folder of MP3s to play +BACKGROUND_MP3_OPTION_DESC=Select a Custom 48Hz MP3 to play in the Background SHOW_BUTTON_OPTION_DESC=Show or Hide Button Controls OPEN_DEBUG_MENU_DESC=Opens the PS4s Debug Or Settings Menu RDB=Rebuild FPKG Database @@ -172,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 @@ -253,7 +253,7 @@ USB_NOTIFY1=USB is Ready to use NOT_ENOUGH_FREE_SPACE=Not Enough Free Space UNHIDE_NOTIFY=Visibility Changed UNHIDE_SUCCESS_NOTIFY=App is now Visible -HIDE_SUCCESS_NOTIFY=App is now Hidden +HIDE_SUCCESS_NOTIFY=App is now Hideen UNHIDE_FAILED_NOTIFY=Failed to Change Visibility FAILED_TO_CLOSE=Failed to Close App OPT_NOT_PERMITTED=Option not available here @@ -406,7 +406,6 @@ PATCH_NUM=Patch PATCH_GAME_TITLE=Patch Game Title: PATCH_NAME=Patch Name: PATCH_APP_VER=Patch App Version: -PATCH_APP_ELF=Patch Elf: PATCH_AUTHOR=Patch Author: PATCH_NOTE=Patch Note: PATCH_VER=Patch Version: @@ -448,7 +447,7 @@ X_DL_COVER=Press X to download covers FAILED_TO_DOWNLOAD_COVER=Failed to download covers COVERS_UP_TO_DATE=Covers are now up to date! IMAGE_APPLIED=Image Applied! - +#new strings DISABLE_MUSIC=Disable Music REBUILD_DB_OPT_1=Rebuild Internal Database REBUILD_DB_OPT_2=Rebuild only DLCs (Inc. ext hdd) @@ -461,13 +460,14 @@ ADDCONT_REBUILT= DLC(s) have been rebuilt successfully! ADDCONT_REACTED=External Content has been re-activated successfully! ADDCONT_REACT_FAILED=Failure, External Content has not been re-activated! ADDCONT_FAILED=Failure, Some DLC(s) have not been added! +SHUTDOWN_DAEMON=Stop & Exit Daemon DB_MOD_WARNING=This will modify the PS4s Database. ANY instability or issues caused by this are at the users discretion. SIGN_IN_USER_NOTIF=Profile Signed in MUSIC_Q=Music Queued PLAYING_NEXT=Playing Next +DELETING_GAME_FOLDER=Dump already Exists, Deleting Game Folder # UPDATE 1.03 NEW OR CHANGED STRINGS EXTRA_SETTING_3=Show Button Controls -DELETING_GAME_FOLDER=Dump already Exists, Deleting Game Folder REMOTE_APP=Remote App SETTINGS_8=Fuse NFS IP FUSE_OPTION_DESC=Automatically connect a NFS (Network FS) share directly to /hostapp @@ -481,13 +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 ... @@ -496,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/itemzflow/source/GLES2_ani.cpp b/itemzflow/source/GLES2_ani.cpp index f79129f..70da302 100644 --- a/itemzflow/source/GLES2_ani.cpp +++ b/itemzflow/source/GLES2_ani.cpp @@ -16,19 +16,9 @@ #include #include -extern texture_atlas_t *atlas; -extern vertex_buffer_t *buffer; - std::vector cv; -extern pthread_mutex_t notifcation_lock; - -// ------------------------------------------------------- typedef & struct --- -typedef struct { - float x, y, z; // position (3f) - float s, t; - float r, g, b, a; // color (4f) -} vertex_t; +std::mutex notifcation_lock; extern GLuint btn_X; @@ -40,23 +30,22 @@ int last_num = 0; static GLuint shader_fx = 0; // text_ani.[vf] static float g_Time = 0.f; static GLuint meta_Slot = 0; -static mat4 model, view, projection; +mat4 model_ani, view_ani, projection_ani; // ---------------------------------------------------------------- reshape --- static void reshape(int width, int height) { glViewport(0, 0, width, height); - mat4_set_orthographic( &projection, 0, width, 0, height, -1, 1); + mat4_set_orthographic( &projection_ani, 0, width, 0, height, -1, 1); } // ---------------------------------------------------------- animation --- static fx_entry_t *ani; // the fx info -static vertex_buffer_t *line_buffer, - *vbo = NULL; +static VertexBuffer line_buffer, vbo; // bool flag static bool ani_is_running = false; -static NOTI_LEVELS level; +static NOTIFI::NOTI_LEVELS level; static GLuint game_noti = GL_NULL, warning_icon = GL_NULL, Success_tex = GL_NULL, Info_tex = GL_NULL, kofi = GL_NULL, profile_pic = GL_NULL; // callback when done looping all effect status @@ -68,7 +57,7 @@ static void ani_post_cb(void) #define MAX_QUEUE 10 #define MAX_MESSAGES 12 - +extern texture_atlas_t *atlas; int rear = -1; int front = -1; @@ -81,7 +70,7 @@ static vec4 notify_rect, // x1,y1 x2,y2 in px static int max_width = 0; // ---------------------------------------------------------------- display --- -static void render_ani(NOTI_LEVELS level, int text_num, int type_num ) +static void render_ani(NOTIFI::NOTI_LEVELS level, int text_num, int type_num ) { // we already clean in main render loop! @@ -118,11 +107,11 @@ static void render_ani(NOTI_LEVELS level, int text_num, int type_num ) switch (level) { - case NOTIFI_INFO: //rgba = (vec4){ 0, 0, 0, 0.7 }; + case NOTIFI::INFO: //rgba = (vec4){ 0, 0, 0, 0.7 }; // break; - case NOTIFI_SUCCESS: //rgba = (vec4){ 0, 150, 0, 0.5 }; + case NOTIFI::SUCCESS: //rgba = (vec4){ 0, 150, 0, 0.5 }; // break; - case NOTIFI_ERROR:// rgba = (vec4){ 1, 0, 0, 0.3 }; + case NOTIFI::ERROR:// rgba = (vec4){ 1, 0, 0, 0.3 }; // rgba = (vec4){ 0, 0, 0, 0.7 }; // break; default: @@ -160,28 +149,30 @@ static void render_ani(NOTI_LEVELS level, int text_num, int type_num ) } /* draw animated background rectangle */ notify_rect.xz = rect.xy; // grab x1,x2 - ORBIS_RenderFillRects(USE_COLOR, &rgba, ¬ify_rect, 1); + std::vector rect_vec; + rect_vec.push_back(notify_rect); + ORBIS_RenderFillRects(USE_COLOR, rgba, rect_vec, 1); /* draw the icon */ notify_icon.xz = rect.zw; // grab x1,x2 switch (level) { - case NOTIFI_SUCCESS: on_GLES2_Render_icon(USE_COLOR, Success_tex/*texnum*/, 2/*tex_unit*/, ¬ify_icon, NULL); + case NOTIFI::SUCCESS: on_GLES2_Render_icon(USE_COLOR, Success_tex/*texnum*/, 2/*tex_unit*/, notify_icon, NULL); break; - case NOTIFI_ERROR: on_GLES2_Render_icon(USE_COLOR, btn_X/*texnum*/, 2/*tex_unit*/, ¬ify_icon, NULL); + case NOTIFI::ERROR: on_GLES2_Render_icon(USE_COLOR, btn_X/*texnum*/, 2/*tex_unit*/, notify_icon, NULL); break; - case NOTIFI_WARNING: on_GLES2_Render_icon(USE_COLOR, warning_icon/*texnum*/, 2/*tex_unit*/, ¬ify_icon, NULL); + case NOTIFI::WARNING: on_GLES2_Render_icon(USE_COLOR, warning_icon/*texnum*/, 2/*tex_unit*/, notify_icon, NULL); break; - case NOTIFI_GAME: on_GLES2_Render_icon(USE_COLOR, game_noti/*texnum*/, 2/*tex_unit*/, ¬ify_icon, NULL); + case NOTIFI::GAME: on_GLES2_Render_icon(USE_COLOR, game_noti/*texnum*/, 2/*tex_unit*/, notify_icon, NULL); break; - case NOTIFI_KOFI: on_GLES2_Render_icon(USE_COLOR, kofi/*texnum*/, 2/*tex_unit*/, ¬ify_icon, NULL); + case NOTIFI::KOFI: on_GLES2_Render_icon(USE_COLOR, kofi/*texnum*/, 2/*tex_unit*/, notify_icon, NULL); break; - case NOTIFI_PROFILE: on_GLES2_Render_icon(USE_COLOR, profile_pic/*texnum*/, 2/*tex_unit*/, ¬ify_icon, NULL); + case NOTIFI::PROFILE: on_GLES2_Render_icon(USE_COLOR, profile_pic/*texnum*/, 2/*tex_unit*/, notify_icon, NULL); break; - case NOTIFI_INFO: + case NOTIFI::INFO: default: - on_GLES2_Render_icon(USE_COLOR, Info_tex/*texnum*/, 2/*tex_unit*/, ¬ify_icon, NULL); + on_GLES2_Render_icon(USE_COLOR, Info_tex/*texnum*/, 2/*tex_unit*/, notify_icon, NULL); break; } @@ -194,9 +185,9 @@ static void render_ani(NOTI_LEVELS level, int text_num, int type_num ) glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); { glUniform1i ( glGetUniformLocation( program, "texture" ), 0 ); - glUniformMatrix4fv( glGetUniformLocation( program, "model" ), 1, 0, model.data); - glUniformMatrix4fv( glGetUniformLocation( program, "view" ), 1, 0, view.data); - glUniformMatrix4fv( glGetUniformLocation( program, "projection" ), 1, 0, projection.data); + glUniformMatrix4fv( glGetUniformLocation( program, "model" ), 1, 0, model_ani.data); + glUniformMatrix4fv( glGetUniformLocation( program, "view" ), 1, 0, view_ani.data); + glUniformMatrix4fv( glGetUniformLocation( program, "projection" ), 1, 0, projection_ani.data); glUniform4f ( glGetUniformLocation( program, "meta"), ani->t_now, ani->status /10., // we use float on SL, switching fx state @@ -204,7 +195,8 @@ static void render_ani(NOTI_LEVELS level, int text_num, int type_num ) type_num /10.); if(1) /* draw whole VBO (storing all added texts) */ { - vertex_buffer_render( vbo, GL_TRIANGLES ); + // vertex_buffer_render( vbo, GL_TRIANGLES ); + vbo.render( GL_TRIANGLES); } } glDisable( GL_BLEND ); @@ -248,7 +240,7 @@ extern float tl; void GLES2_ani_init(int width, int height) { vec4 color = { 200., 0., 200., 1. }; - line_buffer = vertex_buffer_new( "vertex:3f,color:4f" ); + line_buffer = VertexBuffer( "vertex:3f,color:4f" ); #if 0 // rects vec2 pen = { .5, .5 }; @@ -350,7 +342,8 @@ for (int i = 0; i < 10; ++i) }; GLuint indices [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12, 13,14,15,16,17,18,19,20,21,22,23,24,25 }; - vertex_buffer_push_back( line_buffer, vertices, 26, indices, 26 ); + // vertex_buffer_push_back( line_buffer, vertices, 26, indices, 26 ); + line_buffer.push_back(vertices, 26, indices, 26); #endif /* shader program is custom, so @@ -366,9 +359,9 @@ for (int i = 0; i < 10; ++i) ani->t_now = 0.f; // set actual time ani->t_life = 2.f; // duration in seconds - mat4_set_identity( &projection ); - mat4_set_identity( &model ); - mat4_set_identity( &view ); + mat4_set_identity( &projection_ani ); + mat4_set_identity( &model_ani ); + mat4_set_identity( &view_ani ); // attach our "custom infos" meta_Slot = glGetUniformLocation(shader_fx, "meta"); @@ -400,7 +393,7 @@ void ani_reset(void) ani_is_running = false; } -static void add_new_queue_item(NOTI_LEVELS lvl, std::string message, std::string detail) { +static void add_new_queue_item(NOTIFI::NOTI_LEVELS lvl, std::string message, std::string detail) { struct _ent tmp_cv; // fname 1 is message, 2 is detail @@ -412,7 +405,7 @@ static void add_new_queue_item(NOTI_LEVELS lvl, std::string message, std::string // Append current queue cv.push_back(tmp_cv); // if controller dc msg, don't print alld etails - if(message != getLangSTR(CON_DIS)){ + if(message != getLangSTR(LANG_STR::CON_DIS)){ fmt::print("[ANI][NOTIFY] Message: {}", tmp_cv.fname); fmt::print("[ANI][NOTIFY] Detail: {}", tmp_cv.fname2); fmt::print("[ANI][NOTIFY] MSG LVL: {}", tmp_cv.lvl); @@ -422,43 +415,38 @@ static void add_new_queue_item(NOTI_LEVELS lvl, std::string message, std::string log_info("Pad DC'ed"); } // trigger start if not already flagged running -int ani_notify(NOTI_LEVELS lvl, std::string message, std::string detail) +int ani_notify(NOTIFI::NOTI_LEVELS lvl, std::string message, std::string detail) { + std::lock_guard lock(notifcation_lock); // Check if message is empty - if ((message.empty() && detail.empty()) || lvl > NUM_OF_NOTIS) { + if ((message.empty() && detail.empty()) || lvl > NOTIFI::NUM_OF_NOTIS) { log_info("[ANI][NOTIFY] Message is EINVAL"); return ITEMZCORE_EINVAL; } - pthread_mutex_lock(¬ifcation_lock); - int QueueTotal = cv.size(); if (QueueTotal > MAX_QUEUE) { log_info("[ANI][NOTIFY] Queue is full %i", cv.size()); if (QueueTotal < MAX_MESSAGES) { //Fill new Queue item - add_new_queue_item(NOTIFI_ERROR, "Queue is Full", "Full"); + add_new_queue_item(NOTIFI::ERROR, "Queue is Full", "Full"); } else log_info("[ANI][NOTIFY] MAX_MESSAGES reached!"); - pthread_mutex_unlock(¬ifcation_lock); return ITEMZCORE_QUEUE_FULL; } add_new_queue_item(lvl, message, detail); - pthread_mutex_unlock(¬ifcation_lock); - return ITEMZCORE_SUCCESS; } // draw one effect loop void GLES2_ani_draw(void) { - - pthread_mutex_lock(¬ifcation_lock); + std::lock_guard lock(notifcation_lock); uint32_t entCt = cv.size(); @@ -473,15 +461,15 @@ void GLES2_ani_draw(void) } else { log_info("deleting vbo"); - if (vbo) vertex_buffer_delete(vbo), vbo = NULL; + vbo.clear(); ani_is_running = true; - level = (NOTI_LEVELS)cv.at(last_num).lvl; + level = (NOTIFI::NOTI_LEVELS )cv.at(last_num).lvl; if (!vbo) // build text message { max_width = 0; - vbo = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); + vbo = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); texture_font_load_glyphs(sub_font, cv.at(last_num).fname.c_str()); // set textures, update 'tl' if (tl > max_width) max_width = tl; @@ -489,7 +477,7 @@ void GLES2_ani_draw(void) // text position, 1st line vec2 pen = (vec2){ 100, 905 }; - add_text(vbo, sub_font, cv.at(last_num).fname, &col, &pen); // set vertexes + vbo.add_text(sub_font, cv.at(last_num).fname, col, pen); // set vertexes if (!cv.at(last_num).fname2.empty()) // second line { pen.x = 100, @@ -497,13 +485,11 @@ void GLES2_ani_draw(void) texture_font_load_glyphs(main_font, cv.at(last_num).fname2.c_str()); // set textures, update 'tl' if (tl > max_width) max_width = tl; - add_text(vbo, main_font, cv.at(last_num).fname2, &col, &pen); // set vertexes + vbo.add_text(main_font, cv.at(last_num).fname2, col, pen); // set vertexes } refresh_atlas(); last_num++; } } } - - pthread_mutex_unlock(¬ifcation_lock); } diff --git a/itemzflow/source/GLES2_dump_frame.cpp b/itemzflow/source/GLES2_dump_frame.cpp index 921f898..7af2886 100644 --- a/itemzflow/source/GLES2_dump_frame.cpp +++ b/itemzflow/source/GLES2_dump_frame.cpp @@ -47,10 +47,10 @@ void save_dumped_frame(void) log_info( "written %zub to: %s", res, tmp); fclose(fp); #else - writeImage(&tmp[0], resolution.x, resolution.y, dump_buffer, (char*)"HB Store"); + writeImage(&tmp[0], resolution.x, resolution.y, dump_buffer, "HB Store"); #endif // feedback! - ani_notify(NOTIFI_INFO, "Frame dumped!", "Dumped to USB"); + ani_notify(NOTIFI::INFO, "Frame dumped!", "Dumped to USB"); } free(dump_buffer), dump_buffer = NULL; } diff --git a/itemzflow/source/GLES2_filemanager.cpp b/itemzflow/source/GLES2_filemanager.cpp index 20f1e0f..19ff933 100644 --- a/itemzflow/source/GLES2_filemanager.cpp +++ b/itemzflow/source/GLES2_filemanager.cpp @@ -2,6 +2,9 @@ #include "defines.h" #include #include +#if defined(__ORBIS__) +#include +#endif bool file_options = false; @@ -13,6 +16,9 @@ extern vec2 resolution, extern vec3 p_off; fs_res fs_ret; +std::vector global_pkg_folder_right; +std::vector global_pkg_folder_left; + /* normalized rgba colors */ extern vec4 sele, // default for selection (blue) grey, // rect fg @@ -26,35 +32,60 @@ extern texture_font_t *main_font, // small /* a double side (L/R) panel filemanager */ layout_t fm_lp, fm_rp; -bool StartFileBrowser(view_t vt, layout_t *l, +std::vector get_active_pkg_folder(){ + if(fm_lp.fs_is_active) + return global_pkg_folder_left; + else + return global_pkg_folder_right; +} + +void push_back_to_active_pkg_folder(const std::string& pkg) { + if(fm_lp.fs_is_active) + global_pkg_folder_left.push_back(pkg); + else + global_pkg_folder_right.push_back(pkg); +} + +void clear_all_active_pkg_folder(){ + global_pkg_folder_left.clear(); + global_pkg_folder_right.clear(); +} + +void clear_active_pkg_folder(){ + if(fm_lp.fs_is_active) + global_pkg_folder_left.clear(); + else + global_pkg_folder_right.clear(); +} + +bool StartFileBrowser(view_t vt, layout_t & l, int (*callback)(std::string fn, std::string fp), FS_FILTER filter) { if (fs_ret.status == FS_NOT_STARTED) { fs_ret.last_v = vt; - v_curr = FILE_BROWSER; + v_curr = FILE_BROWSER_LEFT; fs_ret.fs_func = callback; - fs_ret.last_layout = l; + fs_ret.last_layout = &l; fs_ret.filter = filter; fs_ret.status = FS_STARTED; } else{ - ani_notify(NOTIFI_WARNING, getLangSTR(FS_SEL_ERROR1), getLangSTR(FS_SEL_ERROR2)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FS_SEL_ERROR1), getLangSTR(LANG_STR::FS_SEL_ERROR2)); return false; } return true; } -vertex_buffer_t *test_v; // pixelshader vbo +VertexBuffer test_v; // pixelshader vbo extern std::string path; // build an item_data array from folder list -void index_items_from_dir_v2(std::string dirpath, std::vector &out_vec) +void index_items_from_dir_v2(std::string dirpath, ThreadSafeVector &out_vec) { // grab, count and sort entry list by name log_info("Loading items with Filter: %i", fs_ret.filter); - std::vector cvEntries; if (!getEntries(dirpath, cvEntries, fs_ret.filter, true)) @@ -62,15 +93,31 @@ void index_items_from_dir_v2(std::string dirpath, std::vector &out_vec) else fmt::print("Found {} in: {}", cvEntries.size(), dirpath); + if(fs_ret.filter == FS_FILTER::FS_PKG){ + for (const std::string &entry : cvEntries) { + if (entry.size() >= 4 && entry.substr(entry.size() - 4) == ".pkg") { + push_back_to_active_pkg_folder( dirpath + "/" + entry); + log_info("Found PKG: %s %i", entry.c_str(), get_active_pkg_folder().size()); + } + } + + if(get_active_pkg_folder().size() > 1){ + log_info("Install ALL PKGs active"); + cvEntries.insert(cvEntries.begin(), getLangSTR(LANG_STR::INSTALL_ALL_PKGS)); + // log_info(cvEntries[0].c_str()); + } + } + out_vec.resize(cvEntries.size() + 1); // +1 for reserved_index for (int i = 1; i < cvEntries.size() + 1; i++) - { // dynalloc for user tokens - out_vec[i].token_c = 1; - out_vec[i].id = cvEntries[i - 1]; + { + out_vec[i].count.token_c = 1; + out_vec[i].info.id = cvEntries[i - 1]; + (get_active_pkg_folder().size() > 1) ? out_vec[1].flags.is_all_pkg_enabled = true : out_vec[1].flags.is_all_pkg_enabled = false; } // save path and counted items in first reserved_index - out_vec[0].id = dirpath; - out_vec[0].token_c = cvEntries.size(); // don't count the reserved_index! + out_vec[0].info.id = dirpath; + out_vec[0].count.token_c = cvEntries.size(); // don't count the reserved_index! } // imported @@ -103,27 +150,28 @@ static bool update_rela_path(std::string &rela_path, std::string &append) } +int left_page_pos = 0, right_page_pos = 0; +bool is_all_pkg_enabled(int panel){ + return panel == 0 ? global_pkg_folder_right.size() > 1 : global_pkg_folder_left.size() > 1; +} + void fw_action_to_fm(int button) { int ret = 0; ivec2 posi = (0); // we address this pointer to the active panel - layout_t *l = NULL; update_idx: - - // detect the active one (L/R?) - fm_lp.fs_is_active ? l = &fm_lp : l = &fm_rp; + layout_t & l = (fm_lp.fs_is_active ? fm_lp : fm_rp); // get item index // compute some indexes, first one of layout: // which icon is selected in field X*Y ? // which item is selected over all ? - int idx = (l->item_sel.x * l->fieldsize.y + l->item_sel.y) + (l->fieldsize.y * l->fieldsize.x * l->page_sel.x); /// vertically!!!! - // update path on selected panel - path = l->item_d[0].id; + int idx = (l.item_sel.x * l.fieldsize.y + l.item_sel.y) + (l.fieldsize.y * l.fieldsize.x * l.page_sel.x); /// vertically!!!! + fm_lp.fs_is_active ? left_page_pos = idx : right_page_pos = idx; - if (test_v) - vertex_buffer_delete(test_v), test_v = NULL; + // update path on selected panel + path = l.item_d[0].info.id; switch (button) { // movement @@ -144,17 +192,17 @@ update_idx: //(&path[0] char tmp[255], folder_name[255]; #if defined(__ORBIS__) - if(!Keyboard(getLangSTR(CREATE_CUSTOM_DIR).c_str(), NULL, &folder_name[0])) + if(!Keyboard(getLangSTR(LANG_STR::CREATE_CUSTOM_DIR).c_str(), NULL, &folder_name[0])) return; snprintf(&tmp[0], 254, "%s/%s", &path[0], &folder_name[0]); #endif if (mkdir(&tmp[0], 0777) == 0){ std::string tmp = fmt::format("{0:.20}: {1:.20}", &path[0], &folder_name[0]); - ani_notify(NOTIFI_SUCCESS, getLangSTR(FOLDER_MADE_SUCCESS), tmp); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::FOLDER_MADE_SUCCESS), tmp); } else - ani_notify(NOTIFI_WARNING, getLangSTR(FOLDER_MAKE_FAIL), strerror(errno)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FOLDER_MAKE_FAIL), strerror(errno)); goto update_path; @@ -162,7 +210,7 @@ update_idx: } case SQU: { // select item - update_rela_path(path, l->item_d[idx].id); + update_rela_path(path, l.item_d[idx].info.id); if (fs_ret.filter == FS_NONE && !file_options) { @@ -179,7 +227,7 @@ update_idx: return; fs_ret.selected_full_path = path; - fs_ret.selected_file = l->item_d[idx].id; + fs_ret.selected_file = l.item_d[idx].info.id; fs_ret.status = FS_DONE; return; @@ -189,8 +237,8 @@ update_idx: { p_off.x = (!p_off.x) ? 200. : 0.; // refresh VBOs - GLES2_UpdateVboForLayout(&fm_lp); - GLES2_UpdateVboForLayout(&fm_rp); + GLES2_UpdateVboForLayout(fm_lp); + GLES2_UpdateVboForLayout(fm_rp); } break; case 58: @@ -199,29 +247,59 @@ update_idx: break; case CRO: { - // sprintf(path, "%s", l->item_d[0].token_d[0].off.c_str()); + // sprintf(path, "%s", l.item_d[0].token_d[0].off.c_str()); if (idx < 1) break; // on title + if(fs_ret.filter == FS_FILTER::FS_PKG && + idx == 1 && get_active_pkg_folder().size() > 1){ + + log_info("[FS_DEBUG] Installing %i PKGs", get_active_pkg_folder().size() ); + int i = 1; + for (const std::string &entry : get_active_pkg_folder()) { + log_info("[FS_DEBUG] Install PKG: %s", entry.c_str()); + #if defined(__ORBIS__) + std::size_t pos = entry.find_last_of("/\\"); // Find last occurrence of directory separator character + std::string filename = (pos == std::string::npos) ? entry : entry.substr(pos + 1); // Extract substring after directory separator character + pkginstall(entry.c_str(), filename.c_str(), true, false, get_active_pkg_folder().size(), i); + sleep(1); + i++; + #endif + } + + break; + } + // update filepath, append item_data filename - update_rela_path(path, l->item_d[idx].id); - // sprintf(path, "%s/%s", l->item_d[ 0 ].token_d[0].off.c_str(), - // l->item_d[ idx ].token_d[0].off.c_str()); + update_rela_path(path, l.item_d[idx].info.id); + // sprintf(path, "%s/%s", l.item_d[ 0 ].token_d[0].off.c_str(), + // l.item_d[ idx ].token_d[0].off.c_str()); if (check_stat(path.c_str()) == S_IFREG && fs_ret.filter != FS_FOLDER) { - fw_action_to_fm(SQU); + if(fs_ret.filter != FS_FILTER::FS_PKG) + fw_action_to_fm(SQU); + else{ +#ifdef __ORBIS__ + if ((ret = pkginstall(path.c_str(), l.item_d[idx].info.id.c_str(), true, false)) != ITEMZCORE_SUCCESS){ + if (ret == IS_DISC_GAME) + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OPTION_DOESNT_SUPPORT_DISC), fmt::format("{0:#x}", ret)); + } +#endif + } + } else /* if a folder go to path */ if (check_stat(path.c_str()) == S_IFDIR) { update_path: // recreate current item_data array - l->item_d.clear(); + clear_active_pkg_folder(); + l.item_d.clear(); // use updated path - index_items_from_dir_v2(path, l->item_d); + index_items_from_dir_v2(path, l.item_d); // item count is know now, copy it - l->item_c = l->item_d[0].token_c; - l->vbo_s = ASK_REFRESH; + l.item_c = l.item_d[0].count.token_c; + l.vbo_s = ASK_REFRESH; // flag to refresh VBO, reset to first item ret = 1, idx = 0; goto update_pos; @@ -230,7 +308,7 @@ update_idx: break; case CIR: { - // sprintf(path, "%s", l->item_d[0].token_d[0].off.c_str()); + // sprintf(path, "%s", l.item_d[0].token_d[0].off.c_str()); // update filepath, step folder back std::string tmp = ""; bool x1 = update_rela_path(path, tmp); @@ -249,10 +327,11 @@ update_idx: { fm_lp.item_d.clear(); fm_rp.item_d.clear(); + clear_all_active_pkg_folder(); fs_ret.status = FS_CANCELLED; if(fs_ret.filter != FS_NONE) - ani_notify(NOTIFI_WARNING, getLangSTR(OP_CANNED), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OP_CANNED), getLangSTR(LANG_STR::OP_CANNED_1)); } return; } @@ -272,31 +351,33 @@ update_idx: idx += posi.y; update_pos: - l->vbo_s = ASK_REFRESH; + l.vbo_s = ASK_REFRESH; // check for bounds, flag to refresh VBO! - if (idx > l->item_c) + if (idx > l.item_c){ idx = 0, ret = 1; + } else if (idx < 0) - idx = l->item_c, ret = 1; + idx = l.item_c, ret = 1; - ivec2 x = (ivec2){idx % l->fieldsize.x, idx / l->fieldsize.x}; - //log_info("x (%d, %d), %d %d\n", x.x, x.y,x.y % l->fieldsize.y, x.y / l->fieldsize.y); + ivec2 x = (ivec2){idx % l.fieldsize.x, idx / l.fieldsize.x}; + //log_info("x (%d, %d), %d %d\n", x.x, x.y,x.y % l.fieldsize.y, x.y / l.fieldsize.y); ivec3 X = (ivec3){x.x, - x.y % l->fieldsize.y, - x.y / l->fieldsize.y}; + x.y % l.fieldsize.y, + x.y / l.fieldsize.y}; // printf("(x:%d, y:%d, z:%d)\n", X.x, X.y, X.z); // update selector pos - l->item_sel = X.xy; + l.item_sel = X.xy; // on page change flag to refresh VBO! - if (l->page_sel.x != X.z) + if (l.page_sel.x != X.z) ret = 1; // update current page - l->page_sel.x = X.z; + l.page_sel.x = X.z; + fm_lp.fs_is_active ? left_page_pos = idx : right_page_pos = idx; // all done - //log_info("item_d[%d]: y:%d p:%d", idx, l->item_sel.y, l->page_sel.x); + // log_info("item_d[%d]: y:%d p:%d", idx, l.item_sel.y, l.page_sel.x); } // trigger VBO refresh @@ -305,8 +386,8 @@ update_idx: // feedback #if 0 - if (l->item_c > 0) - log_info("Selected: %s", l->item_d[idx].id.c_str()); + if (l.item_c > 0) + log_info("Selected: %s", l.item_d[idx].info.id.c_str()); else log_info("no items"); #endif diff --git a/itemzflow/source/GLES2_fonts.cpp b/itemzflow/source/GLES2_fonts.cpp index aad74a4..2fc5aeb 100644 --- a/itemzflow/source/GLES2_fonts.cpp +++ b/itemzflow/source/GLES2_fonts.cpp @@ -125,21 +125,21 @@ void refresh_atlas(void) } } -void clean_textures(layout_t *l) +void clean_textures(layout_t & l) { /* - if(l->texture) + if(l.texture) { - log_info("%s: layout: %p, %p", __FUNCTION__, l, l->texture); - for(int i = 0; i < l->fieldsize.x * l->fieldsize.y; i++) + log_info("%s: layout: %p, %p", __FUNCTION__, l, l.texture); + for(int i = 0; i < l.fieldsize.x * l.fieldsize.y; i++) { - if(l->texture[i] != 0) + if(l.texture[i] != 0) { - log_info("glDeleteTextures texture[%2d]: %d", i, l->texture[i]); - glDeleteTextures(1, &l->texture[i] ), l->texture[i] = 0; + log_info("glDeleteTextures texture[%2d]: %d", i, l.texture[i]); + glDeleteTextures(1, &l.texture[i] ), l.texture[i] = 0; } } - free(l->texture), l->texture = NULL; + free(l.texture), l.texture = NULL; } */ } @@ -168,7 +168,7 @@ default_embedded: // fallback on error // prepare our set of different size if( ! ftgl_init_fonts( ttf, size ) ) { // custom data failed... - ani_notify(NOTIFI_WARNING, getLangSTR(TTF_ERROR_NOTIFY), getLangSTR(TTF_ERROR_NOTIFY2)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::TTF_ERROR_NOTIFY), getLangSTR(LANG_STR::TTF_ERROR_NOTIFY2)); #if defined (__ORBIS__) sceKernelIccSetBuzzer(3); diff --git a/itemzflow/source/GLES2_itemzflow.cpp b/itemzflow/source/GLES2_itemzflow.cpp index 9f5b8ef..10ee9e4 100644 --- a/itemzflow/source/GLES2_itemzflow.cpp +++ b/itemzflow/source/GLES2_itemzflow.cpp @@ -10,6 +10,8 @@ #include "utils.h" #include #include +#include +#include "feature_classes.hpp" #if defined(__ORBIS__) #include "shaders.h" #include "net.h" @@ -20,6 +22,7 @@ #endif #include +#include #include #include #include @@ -32,10 +35,14 @@ #include "../external/pugixml/pugixml.hpp" extern int retry_mp3_count; #endif +#include "utils.h" +#include extern vec2 resolution; -GameStatus app_status = APP_NA_STATUS; +std::mutex disc_lock; + +std::atomic app_status(APP_NA_STATUS); extern int numb_of_settings; extern std::atomic_bool is_disc_inserted; // redef for yaml-cpp @@ -58,7 +65,7 @@ std::unordered_map> m; std::wstring_convert, char16_t> convert; u32 num_title_id = 0; u32 patch_current_index = 0; -std::string key_title = "title"; +std::string key_title = "name"; std::string key_app_ver = "app_ver"; std::string key_patch_ver = "patch_ver"; std::string key_name = "name"; @@ -89,59 +96,51 @@ struct _trainer_struct trs; #endif // ------------------------------------------------------- global variables --- // the Settings -char ipc_msg[100]; + +std::string ipc_msg; // Assuming buffer size of 100 extern ItemzSettings set, *get; extern double u_t; extern std::vector gm_p_text; +using namespace multi_select_options; -static GLuint sh_prog1, +static GLuint rdisc_tex; + +extern ThreadSafeVector all_apps; + +VertexBuffer cover_o, cover_t, cover_i, title_vbo, rdisc_i, trainer_btns, launch_text, as_text, remove_music, app_home_refresh, favs; +double rdisc_rz = 0.; +std::atomic_int inserted_disc(0); + +int v_curr = ITEMzFLOW; +static mat4 model_main, + view_main, + projection_main; + +GLuint sh_prog1, sh_prog2, sh_prog3, sh_prog4, - sh_prog5, - rdisc_tex; + sh_prog5; -extern std::vector all_apps; - -vertex_buffer_t *cover_o, // object, with depth: 6 faces - *cover_t, // full coverbox, template - *cover_i, // just icon coverbox, overlayed - *title_vbo = NULL, - *rdisc_i, - *trainer_btns = NULL, *launch_text = NULL, - *as_text = NULL, *remove_music = NULL, - *app_home_refresh = NULL;; -double rdisc_rz = 0.; -std::atomic_int inserted_disc = 0; - -int v_curr = ITEMzFLOW; -static mat4 model, - view, - projection; - -struct retry_t *cf_tex = NULL; bool skipped_first_X = false; // ------------------------------------------------------ freetype-gl shaders --- -typedef struct -{ - xyz position, - normal; - rgba color; -} v3f3f4f_t; - -typedef struct -{ - xyz position; - st tex_uv; - rgba color; -} v3f2f4f_t; +std::array heart_icon_positions{ + IconPosition{-3, 10, 850}, + IconPosition{-2, 270, 845}, + IconPosition{-1, 550, 830}, + IconPosition{0, 830, 810}, + IconPosition{1, 1300, 830}, + IconPosition{2, 1590, 845}, + IconPosition{3, 1850, 850} +}; vec4 c[8]; int up_next = ITEMzFLOW; GLuint btn_X = GL_NULL, btn_options = GL_NULL, btn_tri = GL_NULL, btn_down = GL_NULL, - btn_up = GL_NULL, btn_right = GL_NULL, btn_left, btn_o = GL_NULL, btn_sq = GL_NULL, btn_l1 = GL_NULL, btn_r1 = GL_NULL, btn_l2 = GL_NULL; + btn_up = GL_NULL, btn_right = GL_NULL, btn_left, btn_o = GL_NULL, + btn_sq = GL_NULL, btn_l1 = GL_NULL, btn_r1 = GL_NULL, btn_l2 = GL_NULL, debug_icon = GL_NULL, heart_icon = GL_NULL; bool Download_icons = true; int old_curr = -1; @@ -166,6 +165,49 @@ typedef struct vec3 off = (vec3){1.05, 0, 0}; // item position offset +layout_t& getLayoutbyView(view_t view) +{ + switch (view) + { + case ITEMzFLOW: + return ls_p; + case ITEM_PAGE: + return ls_p; + case ITEM_SETTINGS: + return setting_p; + case RANDOM: + return ls_p; + case FILE_BROWSER_RIGHT: + return fm_rp; + case FILE_BROWSER_LEFT: + return fm_lp; + } + + return ls_p; +} + +void text_gl_extra( vertex_buffer_t* buffer,int num, GLuint texture, int idx, vec4 col, GLuint sh_prog){ + // int num = 2; + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glActiveTexture(GL_TEXTURE0 + num); + glBindTexture(GL_TEXTURE_2D, texture); + { + glUniform1i(glGetUniformLocation(sh_prog, "texture"), num); + glUniformMatrix4fv(glGetUniformLocation(sh_prog, "model"), 1, 0, model_main.data); + glUniformMatrix4fv(glGetUniformLocation(sh_prog, "view"), 1, 0, view_main.data); + glUniformMatrix4fv(glGetUniformLocation(sh_prog, "projection"), 1, 0, projection_main.data); + + glUniform4f(glGetUniformLocation(sh_prog, "Color"), col.r, col.g, col.b, col.a); + + /* draw whole VBO (storing all added) */ + vertex_buffer_render(buffer, GL_TRIANGLES); + } + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); +} + + /* for animations */ extern "C" typedef struct ani_t { @@ -180,14 +222,14 @@ extern "C" typedef struct ani_t // we have two animation pointers, and the pointer to current one std::vector ani; -/* view (matrix) vectors */ +/* view_main (matrix) vectors */ vec3 v0 = (vec3)(0), v1, v2; // ---------------------------------------------------------------- display --- -/* model rotation angles and camera position */ +/* model_main rotation angles and camera position */ float gx = 0., gy = 90., gz = 90.; @@ -206,63 +248,70 @@ bool use_reflection = true, extern ivec4 menu_pos; -layout_t gm_p, ls_p, setting_p; // settings +layout_t ls_p, setting_p; // settings #if defined(__ORBIS__) extern std::atomic_int mins_played; -extern std::atomic_int AppVis; #else -std::atomic_int mins_played = 2; -std::atomic_bool AppVis = 0; +std::atomic_int mins_played(2); #endif -bool current_app_is_fpkg = false; -bool is_dumpable = false; -std::string title; -std::string title_id; -std::string app_version; std::string tmp; -std::string pkg_path; -std::string icon_path; extern int setting_on; +SafeString title_id; int wait_time = 0; bool confirm_close = false; /************************ END OF GLOBALS ******************/ +void rescan_for_apps(multi_select_options::Sort_Multi_Sel op, Sort_Category cat ); +void refresh_apps_for_cf(multi_select_options::Sort_Multi_Sel op, Sort_Category cat ){ + rescan_for_apps(op, cat); + // load textures of new apps + for (int i = 1; i < all_apps[0].count.token_c + 1; i++) + { + check_tex_for_reload(i); + check_n_load_textures(i); + } + log_info("Done reloading # of App: %i", all_apps[0].count.token_c); +} -void refresh_apps_for_cf(Sort_Multi_Sel op, Sort_Category cat ) +void rescan_for_apps(multi_select_options::Sort_Multi_Sel op, Sort_Category cat ) { - pthread_mutex_lock(&disc_lock); - int before = all_apps[0].token_c; + std::lock_guard lock(disc_lock); + + int before = all_apps[0].count.token_c; log_debug("Reloading Installed Apps before: %i", before); #ifdef __ORBIS__ - loadmsg(getLangSTR(RELOAD_LIST)); + loadmsg(getLangSTR(LANG_STR::RELOAD_LIST)); #endif // leak not so much // print_Apps_Array(all_apps, before); delete_apps_array(all_apps); - +#ifdef __ORBIS__ index_items_from_dir(all_apps, APP_PATH("../"), "/mnt/ext0/user/app", cat); - log_info("=== %i", all_apps[0].token_c); +#else + index_items_from_dir(all_apps, APP_PATH("apps"), "/user/app", cat); +#endif + log_info("=== %i", all_apps[0].count.token_c); //std::vector sort; //print_Apps_Array(all_apps); switch (op) { - case TID_ALPHA_SORT: + case multi_select_options::TID_ALPHA_SORT: { std::sort(all_apps.begin() + 1, all_apps.end(), [](item_t a, item_t b) { - return a.id.size() < b.id.size(); + return a.info.id.size() < b.info.id.size(); }); break; } - case TITLE_ALPHA_SORT: + case multi_select_options::TITLE_ALPHA_SORT: { std::sort(all_apps.begin() + 1, all_apps.end(), [](item_t a, item_t b) { - std::string tmp1 = a.name; - std::string tmp2 = b.name; + std::string tmp1 = a.info.name; + std::string tmp2 = b.info.name; std::transform(tmp1.begin(), tmp1.end(), tmp1.begin(), ::tolower); std::transform(tmp2.begin(), tmp2.end(), tmp2.begin(), ::tolower); @@ -271,78 +320,26 @@ void refresh_apps_for_cf(Sort_Multi_Sel op, Sort_Category cat ) }); break; } - case NA_SORT: + case multi_select_options::NA_SORT: default: break; } - realloc_app_retries(); InitScene_4(1920, 1080); - for (int i = 1; i < all_apps[0].token_c + 1; i++) - { - check_tex_for_reload(i); - check_n_load_textures(i); - } - sceMsgDialogTerminate(); - log_debug("Done reloading # of App: %i, # of Apps added/removed: %i", all_apps[0].token_c, all_apps[0].token_c - before); + log_debug("Done reloading # of App: %i, # of Apps added/removed: %i", all_apps[0].count.token_c, all_apps[0].count.token_c - before); is_disc_inserted = false; inserted_disc = -999; g_idx = 1; - pthread_mutex_unlock(&disc_lock); } - - - -void render_button(GLuint btn, float h, float w, float x, float y, float multi) -{ - - vec4 r; - vec2 s = (vec2){w / multi, h / multi}, p = (resolution - s); - p.y = y; - p.x = x; - s += p; - // convert to normalized coordinates - r.xy = px_pos_to_normalized(&p); - r.zw = px_pos_to_normalized(&s); - on_GLES2_Render_icon(USE_COLOR, btn, 2, &r, NULL); -} - -void itemzcore_add_text(vertex_buffer_t* vert_buffer,float x, float y, std::string text) +void itemzcore_add_text(VertexBuffer &vert_buffer, float x, float y, std::string text) { vec2 pen_s = (vec2){0., 0.}; pen_s.x = x; pen_s.y = y; texture_font_load_glyphs(sub_font, text.c_str()); - add_text(vert_buffer, sub_font, text, &col, &pen_s); -} - -static void append_to_vbo(vertex_buffer_t *vbo, vec3 *v) -{ - float s0 = 0., t0 = 0., - s1 = 1., t1 = 1.; - GLuint idx[6] = {0, 1, 2, 0, 2, 3}; - v3f2f4f_t vtx[4]; - - vtx[0].position = (xyz){(v + 0)->x, (v + 0)->y, (v + 0)->z}; - vtx[0].color = (rgba){c[0].r, c[0].g, c[0].b, c[0].a}; - vtx[0].tex_uv = (st){s1, t0}; - - vtx[1].position = (xyz){(v + 1)->x, (v + 1)->y, (v + 1)->z}; - vtx[1].color = (rgba){c[1].r, c[1].g, c[1].b, c[1].a}; - vtx[1].tex_uv = (st){s0, t0}; - - vtx[2].position = (xyz){(v + 2)->x, (v + 2)->y, (v + 2)->z}; - vtx[2].color = (rgba){c[2].r, c[2].g, c[2].b, c[2].a}; - vtx[2].tex_uv = (st){s0, t1}; - - vtx[3].position = (xyz){(v + 3)->x, (v + 3)->y, (v + 3)->z}; - vtx[3].color = (rgba){c[3].r, c[3].g, c[3].b, c[3].a}; - vtx[3].tex_uv = (st){s1, t1}; - - - vertex_buffer_push_back(vbo, vtx, 4, idx, 6); + vert_buffer.add_text(sub_font, text, col, pen_s); } // add to vbo from rectangle, in px coordinates @@ -364,14 +361,6 @@ extern "C" void vbo_add_from_rect(vertex_buffer_t *vbo, vec4 *rect) GLuint idx[6] = {0, 1, 2, 0, 2, 3}; vertex_buffer_push_back(vbo, vtx, 4, idx, 6); } -void realloc_app_retries() -{ - if (cf_tex) - { - free(cf_tex); - cf_tex = (struct retry_t *)calloc(all_apps[0].token_c /* 1st is reseved */ + 1, sizeof(retry_t)); - } -} void InitScene_5(int width, int height) { glFrontFace(GL_CCW); @@ -382,7 +371,7 @@ void InitScene_5(int width, int height) fs_ret.last_layout = NULL; fs_ret.filter = FS_NONE; - // reset view + // reset view_main v2 = v1 = v0; ani.resize(1); @@ -408,14 +397,14 @@ void InitScene_5(int width, int height) c[7] = (vec4){0, 0, 0, 1}; // 8. black // the object - cover_o = vertex_buffer_new("vertex:3f,color:4f"); + cover_o = VertexBuffer("vertex:3f,color:4f"); // the textuted objects - cover_t = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); // owo template - cover_i = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); // single diff item + cover_t = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); // owo template + cover_i = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); // single diff item if (1) /* the single rotating disc icon texture (overlayed) */ { - rdisc_i = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); // rotating disc icon + rdisc_i = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); // rotating disc icon vec3 rdisc_vtx[4]; // keep center dividing by 2.f, CW, squared texture @@ -423,7 +412,7 @@ void InitScene_5(int width, int height) rdisc_vtx[1] = (vec3){-.7 /2., .7 /2., .0 /2.}; // up L rdisc_vtx[2] = (vec3){-.7 /2., -.7 /2., .0 /2.}; // bm L rdisc_vtx[3] = (vec3){ .7 /2., -.7 /2., .0 /2.}; // bm R - append_to_vbo(rdisc_i, &rdisc_vtx[0]); + rdisc_i.append_to_vbo(&rdisc_vtx[0]); } // one rect @@ -436,7 +425,8 @@ void InitScene_5(int width, int height) cover_item_v[1] = (vec3){-.991 /2., .925 /2., .0 /2.}; // up L cover_item_v[2] = (vec3){-.991 /2., -1.195 /2., .0 /2.}; // bm L cover_item_v[3] = (vec3){ .956 /2., -1.195 /2., .0 /2.}; // bm R - append_to_vbo(cover_i, &cover_item_v[0]); + + cover_i.append_to_vbo(&cover_item_v[0]); /* xyz v[] = { { 1, 1, 1}, {-1, 1, 1}, {-1,-1, 1}, { 1,-1, 1}, { 1,-1,-1}, { 1, 1,-1}, {-1, 1,-1}, {-1,-1,-1} }; @@ -448,7 +438,7 @@ void InitScene_5(int width, int height) cover_v[2] = (vec3){-1. / 2., -1.25 / 2., .0 / 2.}; cover_v[3] = (vec3){1. / 2., -1.25 / 2., .0 / 2.}; // for shared/template cover box - append_to_vbo(cover_t, &cover_v[0]); + cover_t.append_to_vbo(&cover_v[0]); // for the other 5 faces of the object cover_v[4] = (vec3){1. / 2., -1.25 / 2., -.2 / 2.}; @@ -499,7 +489,7 @@ void InitScene_5(int width, int height) 20, 21, 22, 20, 22, 23 // Back: 476 rwb, 465 rbb -> CW is drawn }; - vertex_buffer_push_back(cover_o, cover_vtx, 24, cover_idx, 36); + cover_o.push_back(cover_vtx, 24, cover_idx, 36); // we have shapes/objects } @@ -516,10 +506,6 @@ void InitScene_5(int width, int height) if (!get->setting_bools[using_sb] && get->setting_bools[has_image] && bg_tex == GL_NULL) bg_tex = load_png_asset_into_texture(get->setting_strings[IMAGE_PATH].c_str()); - // we will need at max all_apps count coverbox - if (!cf_tex) - cf_tex = (struct retry_t *)calloc(all_apps[0].token_c /* 1st is reseved */ + 1, sizeof(retry_t)); - if (!rdisc_tex) rdisc_tex = load_png_asset_into_texture(asset_path("disc.png")); @@ -550,75 +536,28 @@ void InitScene_4(int width, int height) if (!sh_prog5) sh_prog5 = BuildProgram(coverflow_vs4, coverflow_fs4, coverflow_vs4_length, coverflow_fs4_length); - mat4_set_identity(&projection); - mat4_set_identity(&model); - mat4_set_identity(&view); + mat4_set_identity(&projection_main); + mat4_set_identity(&model_main); + mat4_set_identity(&view_main); // reshape glViewport(0, 0, width, height); - mat4_set_perspective(&projection, 60.0f, width / (float)height, 1.0, 10.0); + mat4_set_perspective(&projection_main, 60.0f, width / (float)height, 1.0, 10.0); } extern GLuint shader; -static void render_tex(GLuint texture, int idx, vertex_buffer_t *vbo, vec4 col) -{ - // we already clean in main renderloop()! - - GLuint sh_prog = sh_prog2; - - if (idx == -1) // reflection - sh_prog = sh_prog3; - else // fake shadow - if (idx == -2) - sh_prog = sh_prog4; - else - if (idx == -3) - sh_prog = sh_prog5; - - glUseProgram(sh_prog); - - int num = 2; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glActiveTexture(GL_TEXTURE0 + num); - glBindTexture(GL_TEXTURE_2D, texture); - { - glUniform1i(glGetUniformLocation(sh_prog, "texture"), num); - glUniformMatrix4fv(glGetUniformLocation(sh_prog, "model"), 1, 0, model.data); - glUniformMatrix4fv(glGetUniformLocation(sh_prog, "view"), 1, 0, view.data); - glUniformMatrix4fv(glGetUniformLocation(sh_prog, "projection"), 1, 0, projection.data); - - glUniform4f(glGetUniformLocation(sh_prog, "Color"), col.r, col.g, col.b, col.a); - - /* draw whole VBO (storing all added) */ - vertex_buffer_render(vbo, GL_TRIANGLES); - } - glBindTexture(GL_TEXTURE_2D, 0); - glUseProgram(0); -} static bool reset_tex_slot(int idx) { bool res = false; - if (cf_tex[idx].tex > 0) - { - // log_debug( "%s[%3d]: %3d, %3d", __FUNCTION__, idx, cf_tex[ idx ].tex, all_apps[ idx ].texture); - if (cf_tex[idx].tex != cb_tex) // keep the coverbox template - { - // log_debug( "%s[%3d]: (%d) %d ", __FUNCTION__, idx, cf_tex[ idx ].tex, all_apps[ idx ].texture); - glDeleteTextures(1, &cf_tex[idx].tex), cf_tex[idx].tex = 0; - res = true; - } - else - { - if (all_apps[idx].texture > GL_NULL && all_apps[idx].texture != fallback_t) // keep icon0 template - { // discard icon0.png - // log_debug( "%s[%3d]: %d (%d)", __FUNCTION__, idx, cf_tex[ idx ].tex, all_apps[ idx ].texture); - glDeleteTextures(1, &all_apps[idx].texture), all_apps[idx].texture = GL_NULL; - res = true; - } - } + GLuint tex = all_apps[idx].icon.texture.load(); + + if (tex > GL_NULL && tex != fallback_t) // keep icon0 template + { // discard icon0.png + glDeleteTextures(1, &tex), all_apps[idx].icon.texture = GL_NULL; + res = true; } + return res; } @@ -626,21 +565,14 @@ static bool reset_tex_slot(int idx) void drop_some_coverbox(void) { int count = 0; - bool locked_by_func = false; + std::lock_guard lock(disc_lock); - if (pthread_mutex_trylock(&disc_lock) == 0) + if (all_apps[0].count.token_c < 100) { - locked_by_func = true; - log_warn("Mutex not locked!, Locked it"); - } - - if (all_apps[0].token_c < 100) - { - pthread_mutex_unlock(&disc_lock); return; } - for (int i = 1; i < all_apps[0].token_c + 1; i++) + for (int i = 1; i < all_apps[0].count.token_c + 1; i++) { if (i > g_idx - pm_r - 1 && i < g_idx + pm_r + 1) continue; @@ -649,9 +581,6 @@ void drop_some_coverbox(void) count++; } - if (locked_by_func) - pthread_mutex_unlock(&disc_lock); - if (count > 0) log_info("%s discarded %d textures", __FUNCTION__, count); } @@ -659,64 +588,47 @@ void drop_some_coverbox(void) void check_tex_for_reload(int idx) { std::string cb_path; - - bool locked_by_func = false; - if (pthread_mutex_trylock(&disc_lock) == 0) - { - locked_by_func = true; - log_warn("Mutex not locked!, Locked it"); - } + //std::lock_guard lock(disc_lock); int w, h; - cb_path = fmt::format("{}/{}.png", APP_PATH("covers"), all_apps[idx].id); + cb_path = fmt::format("{}/{}.png", APP_PATH("covers"), all_apps[idx].info.id); - if (!if_exists(cb_path.c_str()) && !is_png_vaild(cb_path.c_str(), &w, &h)) + if (!if_exists(cb_path.c_str()) || !is_png_vaild(cb_path.c_str(), &w, &h)) { - log_info("%s doesnt exist", cb_path.c_str()); - cf_tex[idx].exists = false; + // log_info("%s doesnt exist", cb_path.c_str()); + all_apps[idx].icon.cover_exists = false; } else - cf_tex[idx].exists = true; - - if (locked_by_func) - pthread_mutex_unlock(&disc_lock); + all_apps[idx].icon.cover_exists = true; } void check_n_load_textures(int idx) { - bool locked_by_func = false; - if (pthread_mutex_trylock(&disc_lock) == 0) + // std::lock_guard lock(disc_lock); + if (all_apps[idx].icon.texture.load() == GL_NULL) { - locked_by_func = true; - log_warn("Mutex not locked!, Locked it"); + //log_info("%s: %s", __FUNCTION__, all_apps[idx].info.id.c_str()); + std::string cb_path = fmt::format("{}/{}.png", APP_PATH("covers"), all_apps[idx].info.id); + if (all_apps[idx].icon.cover_exists.load()){ + // log_info("loading cover"); + all_apps[idx].icon.image_data.data = load_png_asset(cb_path.c_str(), all_apps[idx].icon.turn_into_texture, all_apps[idx].icon.image_data); + } + else // load icon0 if cover doesnt exist + { + // log_info("Loading data for %s", all_apps[idx].info.picpath.c_str()); + all_apps[idx].icon.image_data.data = load_png_asset(all_apps[idx].info.picpath.c_str(), all_apps[idx].icon.turn_into_texture, all_apps[idx].icon.image_data); + // log_info("Done loading data for %s", all_apps[idx].info.picpath.c_str()); + } } - - if (!cf_tex[idx].tex) - { - //log_info("%s: %s", __FUNCTION__, all_apps[idx].id.c_str()); - - std::string cb_path = fmt::format("{}/{}.png", APP_PATH("covers"), all_apps[idx].id); - - //log_info(cb_path.c_str()); - cf_tex[idx].tex = cb_tex; - int w, h; - if (cf_tex[idx].exists && is_png_vaild(cb_path.c_str(), &w, &h)) - cf_tex[idx].tex = load_png_asset_into_texture(cb_path.c_str()); - } - if (cf_tex[idx].tex == 0) - cf_tex[idx].tex = cb_tex; // as final fallback - - if (locked_by_func) - pthread_mutex_unlock(&disc_lock); } bool is_remote_va_launched = false; std::string vapp_title_launched; void vapp_launch_event(bool is_launch = true){ - if(is_vapp(title_id)){ + if(is_vapp(all_apps[g_idx].info.id)){ if(is_launch){ is_remote_va_launched = true; - vapp_title_launched = title_id; + vapp_title_launched = all_apps[g_idx].info.id; } else{ is_remote_va_launched = false; @@ -724,43 +636,44 @@ void vapp_launch_event(bool is_launch = true){ } } } -void itemzCore_Launch_util(layout_t * l) { +void itemzCore_Launch_util(layout_t & l) { if (app_status == RESUMABLE) { - if (l -> item_d[l -> curr_item].multi_sel.is_active) { + if (l. item_d[l. curr_item].multi_sel.is_active) { if (skipped_first_X) { - int multi_opt = l -> item_d[l -> curr_item].multi_sel.pos.y + 1; // +1 skip first option + int multi_opt = l. item_d[l. curr_item].multi_sel.pos.y + 1; // +1 skip first option log_info("ll option: %i ...", multi_opt); if (multi_opt == RESUMABLE) { #if defined(__ORBIS__) - if (get_patch_path(title_id)) { + if (get_patch_path(all_apps[g_idx].info.id)) { is_first_eboot = true; trainer_launcher(); } - if(Launch_App(title_id, (bool)(app_status == RESUMABLE), g_idx) == ITEMZCORE_SUCCESS) + if(Launch_App(all_apps[g_idx].info.id, (bool)(app_status == RESUMABLE), g_idx) == ITEMZCORE_SUCCESS) { vapp_launch_event(); } else - log_info("Failed to launch %s", title_id.c_str()); + log_info("Failed to launch %s", all_apps[g_idx].info.id.c_str()); #else log_info("[PC DEV] *********** RESUMED"); + ani_notify(NOTIFI::NOTI_LEVELS::GAME, "RESUMED", ""); #endif } else if (multi_opt == OTHER_APP_OPEN) { #if defined(__ORBIS__) Kill_BigApp(app_status); vapp_launch_event(false); #else - log_info("[PC DEV] *********** killed"); + ani_notify(NOTIFI::NOTI_LEVELS::GAME, "KILLED", ""); #endif } skipped_first_X = false; - (l -> item_d[l -> curr_item].multi_sel.pos.x == OTHER_APP_OPEN) ? l -> item_d[l -> curr_item].multi_sel.pos.x = RESUMABLE: l -> item_d[l -> curr_item].multi_sel.pos.x = OTHER_APP_OPEN; + (l. item_d[l. curr_item].multi_sel.pos.x == OTHER_APP_OPEN) ? l. item_d[l. curr_item].multi_sel.pos.x = RESUMABLE: l. item_d[l. curr_item].multi_sel.pos.x = OTHER_APP_OPEN; } else { skipped_first_X = true; - l -> item_d[l -> curr_item].multi_sel.pos.x = RESUMABLE; - // refresh view + l. item_d[l. curr_item].multi_sel.pos.x = RESUMABLE; + // refresh view_main GLES2_render_list((view_t) v_curr); } } @@ -770,17 +683,17 @@ void itemzCore_Launch_util(layout_t * l) { #if defined(__ORBIS__) // vapp func - if (!is_vapp(title_id) && - !is_dumpable && Confirmation_Msg(getLangSTR(CLOSE_GAME)) == NO) return; + if (!is_vapp(all_apps[g_idx].info.id) && + !all_apps[g_idx].flags.is_dumpable && Confirmation_Msg(getLangSTR(LANG_STR::CLOSE_GAME)) == NO) return; // ItemCore virtual apps arnt actual ps4 apps - if (is_if_vapp(title_id)) { + if (is_if_vapp(all_apps[g_idx].info.id)) { log_debug("ItemzCore Virtual App tried to be launched"); // smth in the future return; } - ret = Launch_App(title_id, (bool)(app_status == RESUMABLE), g_idx); + ret = Launch_App(all_apps[g_idx].info.id, (bool)(app_status == RESUMABLE), g_idx); if (!(ret & 0x80000000)){ vapp_launch_event(); app_status = RESUMABLE; @@ -808,39 +721,31 @@ void itemzCore_Launch_util(layout_t * l) { static int download_texture(int idx) { - bool locked_by_func = false; - if (pthread_mutex_trylock(&disc_lock) == 0) + std::lock_guard lock(disc_lock); + if (idx) { - locked_by_func = true; - log_warn("Mutex not locked!, Locked it"); - } - - if (1) - { - std::string cb_path = fmt::format("{}/{}.png", APP_PATH("covers"), all_apps[idx].id); + std::string cb_path = fmt::format("{}/{}.png", APP_PATH("covers"), all_apps[idx].info.id); - log_info("Trying to Download cover for %s", all_apps[idx].id.c_str()); + log_info("Trying to Download cover for %s", all_apps[idx].info.id.c_str()); - loadmsg(getLangSTR(DL_COVERS)); + loadmsg(getLangSTR(LANG_STR::DL_COVERS)); int w, h = 0; if (!if_exists(cb_path.c_str()) || !is_png_vaild(cb_path.c_str(), &w, &h)) // download { - cf_tex[idx].exists = false; - std::string cb_url = fmt::format("https://api.pkg-zone.com/download?cover_tid={}", all_apps[idx].id); + all_apps[idx].icon.cover_exists = false; + std::string cb_url = fmt::format("https://api.pkg-zone.com/download?cover_tid={}", all_apps[idx].info.id); int ret = dl_from_url(cb_url.c_str(), cb_path.c_str()); if (ret != 0) { - log_warn("dl_from_url for %s failed with %i", all_apps[idx].id.c_str(), ret); + log_warn("dl_from_url for %s failed with %i", all_apps[idx].info.id.c_str(), ret); return ret; } else if (ret == 0) - cf_tex[idx].exists = true; + all_apps[idx].icon.cover_exists = true; } else - cf_tex[idx].exists = true; + all_apps[idx].icon.cover_exists = true; } - if (locked_by_func) - pthread_mutex_unlock(&disc_lock); return ITEMZCORE_SUCCESS; } @@ -850,32 +755,24 @@ static int download_texture(int idx) static void check_n_draw_textures(int idx, int SH_type, vec4 col) { - bool locked_by_func = false; - if (pthread_mutex_trylock(&disc_lock) == 0) +// std::lock_guard lock(disc_lock); + if(all_apps[idx].icon.turn_into_texture.load()){ + //log_info("Loadding ...."); + load_png_cover_data_into_texture(all_apps[idx].icon.image_data, all_apps[idx].icon.turn_into_texture, all_apps[idx].icon.texture); + } + + if (!all_apps[idx].icon.cover_exists) // craft a coverbox stretching the icon0 { - locked_by_func = true; - log_warn("Mutex not locked!, Locked it"); - } + cover_t.render_tex(cb_tex, SH_type, col); - if (cf_tex[idx].tex == cb_tex) // craft a coverbox stretching the icon0 - { - - render_tex(cb_tex, /* template */ SH_type, cover_t, col); - - if (all_apps[idx].texture == GL_NULL) - all_apps[idx].texture = load_png_asset_into_texture(all_apps[idx].picpath.c_str()); - - if (all_apps[idx].texture == GL_NULL) - all_apps[idx].texture = fallback_t; // fallback icon0 + if (all_apps[idx].icon.texture.load() == GL_NULL) + cover_i.render_tex(fallback_t, SH_type, col); // overlayed stretched icon0 - render_tex(all_apps[idx].texture, SH_type, cover_i, col); + cover_i.render_tex(all_apps[idx].icon.texture.load(), SH_type, col); } else // available cover box texture - render_tex(cf_tex[idx].tex, SH_type, cover_t, col); - - if (locked_by_func) - pthread_mutex_unlock(&disc_lock); + cover_t.render_tex(all_apps[idx].icon.texture.load(), SH_type, col); } static void send_cf_action(int plus_or_minus_one, int type) @@ -910,11 +807,11 @@ static void send_cf_action(int plus_or_minus_one, int type) //log_info("%d, %s, %s, %d", g_idx, // all_apps[g_idx].name.c_str(), - // all_apps[g_idx].id.c_str(), - // all_apps[g_idx].texture); + // all_apps[g_idx].info.id.c_str(), + // all_apps[g_idx].icon.texture); } -static vec3 set_view(enum view_t vt) // view type +static vec3 set_view_main(view_t vt) // view_main type { vec3 ret = (0); @@ -947,7 +844,7 @@ static vec3 set_view(enum view_t vt) // view type case ITEM_SETTINGS: { ret = (vec3){0, 3, 0}; - // v_curr = ITEM_SETTINGS; + //v_curr = ITEM_SETTINGS; break; } default: @@ -960,18 +857,18 @@ static vec3 set_view(enum view_t vt) // view type return ret; } -static void O_action_settings(layout_t *l) +static void O_action_settings(layout_t & l) { - switch (l->curr_item) + switch (l.curr_item) { case REBUILD_DB_OPTION: case OPEN_SHELLUI_MENU: case POWER_CONTROL_OPTION: case SORT_BY_OPTION: { - if (l->item_d[l->curr_item].multi_sel.is_active && - l->item_d[l->curr_item].multi_sel.pos.x != RESET_MULTI_SEL) + if (l.item_d[l.curr_item].multi_sel.is_active && + l.item_d[l.curr_item].multi_sel.pos.x != RESET_MULTI_SEL) { log_info("Operation is Cancelled!"); goto refresh; @@ -983,33 +880,30 @@ static void O_action_settings(layout_t *l) }; - //log_info("Settings: %d, %i", l->curr_item, numb_of_settings); + //log_info("Settings: %d, %i", l.curr_item, numb_of_settings); numb_of_settings = NUMBER_OF_SETTINGS; - ITEMZ_SETTING_TEXT[11] = fmt::format("{0:.30}", getLangSTR(SETTINGS_12)); + ITEMZ_SETTING_TEXT[11] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_12)); - /* DELETE SETTINGS VIEW */ - if (setting_p.vbo) - vertex_buffer_delete(setting_p.vbo), setting_p.vbo = NULL; - - if (setting_p.f_rect) - free(setting_p.f_rect), setting_p.f_rect = NULL; - + /* DELETE SETTINGS view_main */ + setting_p.vbo.clear(); + setting_p.f_rect.clear(); setting_p.item_d.clear(); - v2 = set_view(ITEMzFLOW); + + v2 = set_view_main(ITEMzFLOW); return; refresh: - if (l->item_d[l->curr_item].multi_sel.is_active && l->item_d[l->curr_item].multi_sel.pos.x != RESET_MULTI_SEL) + if (l.item_d[l.curr_item].multi_sel.is_active && l.item_d[l.curr_item].multi_sel.pos.x != RESET_MULTI_SEL) { - l->item_d[l->curr_item].multi_sel.pos.x = RESET_MULTI_SEL; - l->vbo_s = ASK_REFRESH; + l.item_d[l.curr_item].multi_sel.pos.x = RESET_MULTI_SEL; + l.vbo_s = ASK_REFRESH; skipped_first_X = false; } } int SETTING_V_CURR = -1; int GAME_PANEL_V_CURR = -1; -SAVE_Multi_Sel SAVE_CB = GM_INVAL; +multi_select_options::SAVE_Multi_Sel SAVE_CB = multi_select_options::GM_INVAL; int FS_GP_Callback(std::string filename, std::string fullpath) { @@ -1017,44 +911,44 @@ int FS_GP_Callback(std::string filename, std::string fullpath) switch (GAME_PANEL_V_CURR) { - case Change_icon_opt: + case cf_ops::REG_OPTS::CHANGE_ICON: { int w, h; if (is_png_vaild(fullpath.c_str(), &w, &h) && h == 512 && w == 512) { #if defined(__ORBIS__) - if (!copyFile(fullpath, icon_path, true)) - ani_notify(NOTIFI_WARNING, getLangSTR(ICON_CHANGE_FAILED), getLangSTR(FAILED_TO_COPY)); + if (!copyFile(fullpath, all_apps[g_idx].info.picpath, true)) + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::ICON_CHANGE_FAILED), getLangSTR(LANG_STR::FAILED_TO_COPY)); else { - ani_notify(NOTIFI_SUCCESS, getLangSTR(ICON_CHANGED_SUCCESS), getLangSTR(PS4_REBOOT_REQ)); - fmt::print("[ICON] copied {} >> {}", fullpath, icon_path); - icon_path = fullpath; + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::ICON_CHANGED_SUCCESS), getLangSTR(LANG_STR::PS4_REBOOT_REQ)); + fmt::print("[ICON] copied {} >> {}", fullpath, all_apps[g_idx].info.picpath); + all_apps[g_idx].info.picpath = fullpath; } #else - ani_notify(NOTIFI_SUCCESS, getLangSTR(ICON_CHANGED_SUCCESS), getLangSTR(PS4_REBOOT_REQ)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::ICON_CHANGED_SUCCESS), getLangSTR(LANG_STR::PS4_REBOOT_REQ)); #endif } else - ani_notify(NOTIFI_WARNING, getLangSTR(ICON_CHANGE_FAILED), getLangSTR(PNG_NOT_VAILD)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::ICON_CHANGE_FAILED), getLangSTR(LANG_STR::PNG_NOT_VAILD)); // log_info("w: %i h: %i", h, w); break; } - case Game_Save_opt: + case cf_ops::REG_OPTS::GAME_SAVE: { #if defined(__ORBIS__) std::vector save_list; if(is_save_folder(fullpath)) - SaveData_Operations(RESTORE_GAME_SAVE, title_id, fullpath); + SaveData_Operations(RESTORE_GAME_SAVE, all_apps[g_idx].info.id, fullpath); else { if(is_nested_saves_recursive(fullpath, save_list)){ for(auto &save_path : save_list){ log_info("[*] Running bulk save cmd: curr save_path: %s", save_path.c_str()); - SaveData_Operations(RESTORE_GAME_SAVE, title_id, save_path); + SaveData_Operations(RESTORE_GAME_SAVE, all_apps[g_idx].info.id, save_path); } } else - ani_notify(NOTIFI_WARNING, getLangSTR(NO_SAVES), getLangSTR(CANT_FIND_SAVE)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_SAVES), getLangSTR(LANG_STR::CANT_FIND_SAVE)); } save_list.clear(); #endif @@ -1078,19 +972,19 @@ int FS_Setting_Callback(std::string filename, std::string fullpath) case CHANGE_BACKGROUND_OPTION: //get->setting_bools[using_sb] = false; if(is_png_vaild(fullpath.c_str(), &bg_w, &bg_h)){ - ani_notify(NOTIFI_SUCCESS, getLangSTR(IMAGE_APPLIED), ""); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::IMAGE_APPLIED), ""); get->setting_bools[has_image] = true; get->setting_strings[IMAGE_PATH] = fullpath; bg_tex = load_png_asset_into_texture(get->setting_strings[IMAGE_PATH].c_str()); } else - ani_notify(NOTIFI_WARNING, getLangSTR(PNG_NOT_VAILD), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::PNG_NOT_VAILD), ""); log_info("IMAGE_PATH %s", get->setting_strings[IMAGE_PATH].c_str()); break; case DUMPER_PATH_OPTION: get->setting_strings[DUMPER_PATH] = fullpath; - ani_notify(NOTIFI_SUCCESS, getLangSTR(DUMPER_PATH_UPDATED), fmt::format("{0:.20}: {1:.20}", getLangSTR(NEW_PATH_STRING), fullpath)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::DUMPER_PATH_UPDATED), fmt::format("{0:.20}: {1:.20}", getLangSTR(LANG_STR::NEW_PATH_STRING), fullpath)); break; case THEME_OPTION:{ #if defined(__ORBIS__) @@ -1103,18 +997,18 @@ int FS_Setting_Callback(std::string filename, std::string fullpath) if(!SaveOptions(get)) log_error("[THEME] Failed to save Theme Options"); else{ - ani_notify(NOTIFI_SUCCESS, getLangSTR(THEME_RESET), getLangSTR(RELAUCHING_IN_5)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::THEME_RESET), getLangSTR(LANG_STR::RELAUCHING_IN_5)); relaunch_timer_thread(); } break; } if(install_IF_Theme(fullpath)){ - ani_notify(NOTIFI_SUCCESS, getLangSTR(THEME_INSTALL_SUCCESS), getLangSTR(RELAUCHING_IN_5)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::THEME_INSTALL_SUCCESS), getLangSTR(LANG_STR::RELAUCHING_IN_5)); relaunch_timer_thread(); } else - ani_notify(NOTIFI_WARNING, getLangSTR(THEME_NOT_VAILD), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::THEME_NOT_VAILD), ""); #endif break; } @@ -1122,10 +1016,9 @@ int FS_Setting_Callback(std::string filename, std::string fullpath) { if (GLES2_fonts_from_ttf(fullpath.c_str())){ get->setting_strings[FNT_PATH] = fullpath; - if (title_vbo) - vertex_buffer_delete(title_vbo), title_vbo = NULL; + title_vbo.clear(); - ani_notify(NOTIFI_SUCCESS, getLangSTR(NEW_FONT_STRING), fmt::format("{0:.20}: {1:.20}", getLangSTR(NEW_FONT_STRING), filename)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::NEW_FONT_STRING), fmt::format("{0:.20}: {1:.20}", getLangSTR(LANG_STR::NEW_FONT_STRING), filename)); } break; } @@ -1137,9 +1030,9 @@ int FS_Setting_Callback(std::string filename, std::string fullpath) retry_mp3_count = 0; mp3_playlist.clear(); if ((status = MP3_Player(get->setting_strings[MP3_PATH])) == MP3_STATUS_STARTED) - ani_notify(NOTIFI_SUCCESS, getLangSTR(MP3_PLAYING), getLangSTR(MP3_LOADED)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::MP3_PLAYING), getLangSTR(LANG_STR::MP3_LOADED)); else if (status == MP3_STATUS_PLAYING){ - ani_notify(NOTIFI_SUCCESS, getLangSTR(MUSIC_Q), getLangSTR(PLAYING_NEXT)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::MUSIC_Q), getLangSTR(LANG_STR::PLAYING_NEXT)); } #endif break; @@ -1149,7 +1042,7 @@ int FS_Setting_Callback(std::string filename, std::string fullpath) if ((ret = pkginstall(fullpath.c_str(), filename.c_str(), true, false)) != ITEMZCORE_SUCCESS){ if (ret == IS_DISC_GAME) - ani_notify(NOTIFI_WARNING, getLangSTR(OPTION_DOESNT_SUPPORT_DISC), fmt::format("{0:#x}", ret)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OPTION_DOESNT_SUPPORT_DISC), fmt::format("{0:#x}", ret)); } #endif @@ -1162,63 +1055,63 @@ if ((ret = pkginstall(fullpath.c_str(), filename.c_str(), true, false)) != ITEMZ return 0; } -static void X_action_settings(int action, layout_t *l) +static void X_action_settings(int action, layout_t & l) { // selected item from active panel - log_info("execute setting %d -> '%s' for 'ItemzCore'", l->curr_item, - l->item_d[l->curr_item].name.c_str()); + log_info("execute setting %d -> '%s' for 'ItemzCore'", l.curr_item, + l.item_d[l.curr_item].info.name.c_str()); - l->vbo_s = ASK_REFRESH; - SETTING_V_CURR = l->curr_item; - switch (l->curr_item) + l.vbo_s = ASK_REFRESH; + SETTING_V_CURR = l.curr_item; + switch (l.curr_item) { case POWER_CONTROL_OPTION: case SORT_BY_OPTION: case REBUILD_DB_OPTION: case OPEN_SHELLUI_MENU: - if (l->item_d[l->curr_item].multi_sel.is_active) + if (l.item_d[l.curr_item].multi_sel.is_active) { const char *uri = NULL; log_info("[OP]: opt: %i | Skip X?: %s", skipped_first_X, skipped_first_X ? "YES" : "NO"); if (skipped_first_X) { - if (l->curr_item == REBUILD_DB_OPTION) + if (l.curr_item == REBUILD_DB_OPTION) { - switch (l->item_d[l->curr_item].multi_sel.pos.y) + switch (l.item_d[l.curr_item].multi_sel.pos.y) { - case REBUILD_ALL: + case multi_select_options::REBUILD_ALL: log_info("REBUILD ALL"); #ifdef __ORBIS__ rebuild_db(); #endif break; - case REBUILD_DLC: + case multi_select_options::REBUILD_DLC: log_info("REBUILD DLC"); #ifdef __ORBIS__ rebuild_dlc_db(); #endif break; - case REBUILD_REACT: + case multi_select_options::REBUILD_REACT: log_info("REBUILD REACTPSN"); #ifdef __ORBIS__ if(Reactivate_external_content(false)) - ani_notify(NOTIFI_SUCCESS, getLangSTR(ADDCONT_REACTED), ""); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::ADDCONT_REACTED), ""); else - ani_notify(NOTIFI_WARNING, getLangSTR(ADDCONT_REACT_FAILED), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::ADDCONT_REACT_FAILED), ""); #endif break; } } - else if (l->curr_item == OPEN_SHELLUI_MENU) + else if (l.curr_item == OPEN_SHELLUI_MENU) { - switch (l->item_d[l->curr_item].multi_sel.pos.y) + switch (l.item_d[l.curr_item].multi_sel.pos.y) { - case SHELLUI_SETTINGS_MENU: + case multi_select_options::SHELLUI_SETTINGS_MENU: log_info("SETTINGS MENU"); uri = "pssettings:play?mode=settings"; break; - case SHELLUI_POWER_SAVE: + case multi_select_options::SHELLUI_POWER_SAVE: log_info("POWER SAVE MENU"); uri = "pssettings:play?mode=settings&function=power_save_standby"; break; @@ -1229,20 +1122,20 @@ static void X_action_settings(int action, layout_t *l) if (uri != NULL) { if ((ret = ItemzLaunchByUri(uri)) != ITEMZCORE_SUCCESS) - ani_notify(NOTIFI_WARNING, getLangSTR(FAILED_MENU_OPEN), fmt::format("{0:.20}: {1:#x}", getLangSTR(ERROR_CODE), ret)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_MENU_OPEN), fmt::format("{0:.20}: {1:#x}", getLangSTR(LANG_STR::ERROR_CODE), ret)); } #endif } - else if (l->curr_item == SORT_BY_OPTION) + else if (l.curr_item == SORT_BY_OPTION) { - get->sort_by = (Sort_Multi_Sel)l->item_d[l->curr_item].multi_sel.pos.y; + get->sort_by = (multi_select_options::Sort_Multi_Sel)l.item_d[l.curr_item].multi_sel.pos.y; refresh_apps_for_cf(get->sort_by, get->sort_cat); } - else if (l->curr_item == POWER_CONTROL_OPTION) + else if (l.curr_item == POWER_CONTROL_OPTION) { #if defined(__ORBIS__) - if(!power_settings((Power_control_sel)l->item_d[l->curr_item].multi_sel.pos.y)) - ani_notify(NOTIFI_WARNING, getLangSTR(ERROR_POWER_CONTROL), fmt::format("{}", strerror(errno))); + if(!power_settings((Power_control_sel)l.item_d[l.curr_item].multi_sel.pos.y)) + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::ERROR_POWER_CONTROL), fmt::format("{}", strerror(errno))); #endif } skipped_first_X = false; @@ -1250,38 +1143,38 @@ static void X_action_settings(int action, layout_t *l) else skipped_first_X = true; - if (l->item_d[l->curr_item].multi_sel.pos.x == 0) + if (l.item_d[l.curr_item].multi_sel.pos.x == 0) { - l->item_d[l->curr_item].multi_sel.pos.x = 1; // EDITABLE + l.item_d[l.curr_item].multi_sel.pos.x = 1; // EDITABLE } else - l->item_d[l->curr_item].multi_sel.pos.x = 0; + l.item_d[l.curr_item].multi_sel.pos.x = 0; } break; case BACKGROUND_MP3_OPTION: - StartFileBrowser((view_t)v_curr, active_p, FS_Setting_Callback, FS_MP3); + StartFileBrowser((view_t)v_curr, setting_p, FS_Setting_Callback, FS_MP3); break; case DUMPER_PATH_OPTION: - StartFileBrowser((view_t)v_curr, active_p, FS_Setting_Callback, FS_FOLDER); + StartFileBrowser((view_t)v_curr, setting_p, FS_Setting_Callback, FS_FOLDER); break; // ps4 keyboard func case IF_PKG_INSTALLER_OPTION: - StartFileBrowser((view_t)v_curr, active_p, FS_Setting_Callback, FS_PKG); + StartFileBrowser((view_t)v_curr, setting_p, FS_Setting_Callback, FS_PKG); break; case THEME_OPTION: - StartFileBrowser((view_t)v_curr, active_p, FS_Setting_Callback, FS_ZIP); + StartFileBrowser((view_t)v_curr, setting_p, FS_Setting_Callback, FS_ZIP); break; case CHECK_FOR_UPDATE_OPTION: { #if defined(__ORBIS__) - loadmsg(getLangSTR(CHECH_UPDATE_IN_PROGRESS)); + loadmsg(getLangSTR(LANG_STR::CHECH_UPDATE_IN_PROGRESS)); if(get->setting_bools[INTERNAL_UPDATE] || (ret = check_update_from_url(ITEMZFLOW_TID)) == IF_UPDATE_FOUND) { - if(get->setting_bools[INTERNAL_UPDATE] || Confirmation_Msg(getLangSTR(OPT_UPDATE)) == YES) + if(get->setting_bools[INTERNAL_UPDATE] || Confirmation_Msg(getLangSTR(LANG_STR::OPT_UPDATE)) == YES) { - loadmsg(getLangSTR(CHECH_UPDATE_IN_PROGRESS)); + loadmsg(getLangSTR(LANG_STR::CHECH_UPDATE_IN_PROGRESS)); mkdir("/user/update_tmp", 0777); mkdir("/user/update_tmp/covers", 0777); mkdir("/user/update_tmp/custom_app_names", 0777); @@ -1299,18 +1192,18 @@ static void X_action_settings(int action, layout_t *l) if(do_update(get->setting_bools[INTERNAL_UPDATE] ? "http://192.168.123.176" : "https://api.pkg-zone.com")) { - ani_notify(NOTIFI_SUCCESS, getLangSTR(IF_UPDATE_SUCESS), getLangSTR(RELAUCHING_IN_5)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::IF_UPDATE_SUCESS), getLangSTR(LANG_STR::RELAUCHING_IN_5)); } else - ani_notify(NOTIFI_WARNING, getLangSTR(IF_UPDATE_FAILD), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::IF_UPDATE_FAILD), getLangSTR(LANG_STR::OP_CANNED_1)); } else - ani_notify(NOTIFI_WARNING, getLangSTR(OP_CANNED), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OP_CANNED), getLangSTR(LANG_STR::OP_CANNED_1)); } else if (ret == IF_NO_UPDATE) - ani_notify(NOTIFI_WARNING, getLangSTR(NO_UPDATE_FOUND), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_UPDATE_FOUND), ""); else if (ret == IF_UPDATE_ERROR) - ani_notify(NOTIFI_WARNING, getLangSTR(NO_INTERNET), getLangSTR(BE_SURE_INTERNET)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_INTERNET), getLangSTR(LANG_STR::BE_SURE_INTERNET)); sceMsgDialogTerminate(); #endif @@ -1318,6 +1211,7 @@ static void X_action_settings(int action, layout_t *l) } case HOME_MENU_OPTION: { + IPC_Client& ipc = IPC_Client::getInstance(); get->setting_bools[HomeMenu_Redirection] = get->setting_bools[HomeMenu_Redirection] ? false : true; #if defined(__ORBIS__) if (is_connected_app) @@ -1326,17 +1220,17 @@ static void X_action_settings(int action, layout_t *l) if (get->setting_bools[HomeMenu_Redirection]) { - if (IPCSendCommand(ENABLE_HOME_REDIRECT, (uint8_t*)&ipc_msg[0]) == NO_ERROR) - ani_notify(NOTIFI_INFO, getLangSTR(REDIRECT_TURNED_ON), ""); + if (ipc.IPCSendCommand(IPC_Commands::ENABLE_HOME_REDIRECT, ipc_msg) == IPC_Ret::NO_ERROR) + ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::REDIRECT_TURNED_ON), ""); } else { - if (IPCSendCommand(DISABLE_HOME_REDIRECT, (uint8_t*)&ipc_msg[0]) == NO_ERROR) - ani_notify(NOTIFI_INFO, getLangSTR(REDIRECT_TURNED_OFF), ""); + if (ipc.IPCSendCommand(IPC_Commands::DISABLE_HOME_REDIRECT, ipc_msg) == IPC_Ret::NO_ERROR) + ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::REDIRECT_TURNED_OFF), ""); } } else - ani_notify(NOTIFI_WARNING, getLangSTR(DAEMON_FAILED_NOTIFY), getLangSTR(DAEMON_FAILED_NOTIFY2)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::DAEMON_FAILED_NOTIFY), getLangSTR(LANG_STR::DAEMON_FAILED_NOTIFY2)); #endif break; } @@ -1344,8 +1238,7 @@ static void X_action_settings(int action, layout_t *l) { get->setting_bools[Show_Buttons] = get->setting_bools[Show_Buttons] ? false : true; - if (title_vbo) - vertex_buffer_delete(title_vbo), title_vbo = NULL; + title_vbo.clear(); break; } case COVER_MESSAGE_OPTION: @@ -1357,37 +1250,37 @@ static void X_action_settings(int action, layout_t *l) { // use_reflection = (use_reflection) ? false : true; char out[1024]; + IPC_Client& ipc = IPC_Client::getInstance(); #ifdef __ORBIS__ // FOR NOW NO KEYPAD BECAUSE THERES NO LETTERS FOR OPTIONAL NFS SHARES - if(Keyboard(getLangSTR(SETTINGS_8).c_str(), get->setting_strings[FUSE_PC_NFS_IP].c_str(), &out[0])){ - int ret = NO_ERROR; - loadmsg(getLangSTR(CONNECTING_TO_FUSE)); - if((ret = IPCMountFUSE(&out[0], "/hostapp", false)) == NO_ERROR){ + if(Keyboard(getLangSTR(LANG_STR::SETTINGS_8).c_str(), get->setting_strings[FUSE_PC_NFS_IP].c_str(), &out[0])){ + IPC_Ret ret = IPC_Ret::NO_ERROR; + loadmsg(getLangSTR(LANG_STR::CONNECTING_TO_FUSE)); + if((ret = ipc.IPCMountFUSE(out, "/hostapp", false)) == IPC_Ret::NO_ERROR){ get->setting_strings[FUSE_PC_NFS_IP] = out; auto it = std::find_if(all_apps.begin(), all_apps.end(), [](const item_t& app) { - return app.id == APP_HOME_HOST_TID; + return app.info.id == APP_HOME_HOST_TID; }); sceMsgDialogTerminate(); if(it == all_apps.end()) refresh_apps_for_cf(get->sort_by, get->sort_cat); - ani_notify(NOTIFI_SUCCESS, getLangSTR(NFS_CONNTECTED), "/hostapp"); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::NFS_CONNTECTED), "/hostapp"); } - else if(ret == FUSE_FW_NOT_SUPPORTED) - msgok(NORMAL, fmt::format("{0:#x} {1:}", (ps4_fw_version() >> 16), getLangSTR(HEN_IS_NOT_SUPPORTED))); + else if(ret == IPC_Ret::FUSE_FW_NOT_SUPPORTED) + msgok(MSG_DIALOG::NORMAL, fmt::format("{0:#x} {1:}", (ps4_fw_version() >> 16), getLangSTR(LANG_STR::HEN_IS_NOT_SUPPORTED))); else - msgok(NORMAL, fmt::format("{0:}\n\nRet: {1:d}", getLangSTR(NFS_CONNECT_FAILED),ret)); + msgok(MSG_DIALOG::NORMAL, fmt::format("{0:}\n\nRet: {1:d}", getLangSTR(LANG_STR::NFS_CONNECT_FAILED), (int)ret)); } else - ani_notify(NOTIFI_WARNING, getLangSTR(OP_CANNED), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OP_CANNED), getLangSTR(LANG_STR::OP_CANNED_1)); #endif - if (title_vbo) - vertex_buffer_delete(title_vbo), title_vbo = NULL; + title_vbo.clear(); break; } case CHANGE_BACKGROUND_OPTION: - StartFileBrowser((view_t)v_curr, active_p, FS_Setting_Callback, FS_PNG); + StartFileBrowser((view_t)v_curr, setting_p, FS_Setting_Callback, FS_PNG); break; case RESET_THEME_OPTION: { @@ -1402,7 +1295,7 @@ static void X_action_settings(int action, layout_t *l) if(!SaveOptions(get)) log_error("[THEME] Failed to save Theme Options"); else{ - ani_notify(NOTIFI_SUCCESS, getLangSTR(THEME_RESET), getLangSTR(RELAUCHING_IN_5)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::THEME_RESET), getLangSTR(LANG_STR::RELAUCHING_IN_5)); relaunch_timer_thread(); } #endif @@ -1411,24 +1304,24 @@ static void X_action_settings(int action, layout_t *l) case DOWNLOAD_COVERS_OPTION: #if defined(__ORBIS__) - if (Confirmation_Msg(getLangSTR(DOWNLOAD_COVERS)) == YES) + if (Confirmation_Msg(getLangSTR(LANG_STR::DOWNLOAD_COVERS)) == YES) { - loadmsg(getLangSTR(DOWNLOADING_COVERS)); + loadmsg(getLangSTR(LANG_STR::DOWNLOADING_COVERS)); log_info("Downloading covers..."); - for (int i = 1; i < all_apps[0].token_c + 1; i++){ + for (int i = 1; i < all_apps[0].count.token_c + 1; i++){ int ret = download_texture(i); if(ret != 0 && ret != 404){ - log_error("Failed to download cover for %s", all_apps[i].name.c_str()); + log_error("Failed to download cover for %s", all_apps[i].info.name.c_str()); sceMsgDialogTerminate(); - ani_notify(NOTIFI_WARNING, getLangSTR(FAILED_TO_DOWNLOAD_COVER), fmt::format("{0:d}", ret)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_TO_DOWNLOAD_COVER), fmt::format("{0:d}", ret)); goto tex_err; } } sceMsgDialogTerminate(); log_info("Downloaded covers"); - ani_notify(NOTIFI_GAME, getLangSTR(COVERS_UP_TO_DATE), ""); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::COVERS_UP_TO_DATE), ""); } else log_info("Download covers canceled"); @@ -1442,10 +1335,10 @@ tex_err: { save_setting: if (!SaveOptions(get)) - ani_notify(NOTIFI_WARNING, getLangSTR(SAVE_ERROR), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::SAVE_ERROR), ""); else { - ani_notify(NOTIFI_SUCCESS, getLangSTR(SAVE_SUCCESS), (get->sort_cat == NO_CATEGORY_FILTER) ? "" : getLangSTR(INC_CAT)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::SAVE_SUCCESS), (get->sort_cat == NO_CATEGORY_FILTER) ? "" : getLangSTR(LANG_STR::INC_CAT)); fmt::print("Settings saved to {}", get->setting_strings[INI_PATH]); // reload settings LoadOptions(get); @@ -1453,7 +1346,7 @@ save_setting: } else { log_info("nu %i", numb_of_settings); - StartFileBrowser((view_t)v_curr, active_p, FS_Setting_Callback, FS_FONT); + StartFileBrowser((view_t)v_curr, setting_p, FS_Setting_Callback, FS_FONT); } break; @@ -1462,102 +1355,192 @@ save_setting: if (numb_of_settings > NUMBER_OF_SETTINGS) // is advanced settings??? goto save_setting; else - StartFileBrowser((view_t)v_curr, active_p, FS_Setting_Callback, FS_FONT); + StartFileBrowser((view_t)v_curr, setting_p, FS_Setting_Callback, FS_FONT); break; default: break; }; } -// else if (is_sub_enabled && l->curr_item == NA_SORT && ) -void Start_Dump(std::string name, std::string path, Dump_Multi_Sels opt) -{ -#if defined(__ORBIS__) - if (path.find("/mnt/usb") != std::string::npos) - { - if (usbpath() == -1) - { - fmt::print("path is %s However, no USB is connected...", path); - ani_notify(NOTIFI_WARNING, getLangSTR(NO_USB), ""); +void printDirContentRecursively(const std::filesystem::path& path, const std::string& indent = "") { + try { + if (!std::filesystem::exists(path) || !std::filesystem::is_directory(path)) { + //std::cerr << "Path does not exist or is not a directory: " << path << std::endl; + log_error("Path does not exist or is not a directory: %s", path.c_str()); return; } + + for (const auto& entry : std::filesystem::directory_iterator(path)) { + // std::cout << indent << entry.path().filename() << std::endl; + log_info("Path: %s", entry.path().filename().string().c_str()); + if (std::filesystem::is_directory(entry.status())) { + printDirContentRecursively(entry.path(), indent + " "); + } + } + } catch (std::filesystem::filesystem_error& e) { + // std::cerr << "Filesystem error: " << e.what() << std::endl; + log_error("Filesystem error"); + } catch (std::exception& e) { + log_error("UNKNOWN FS ERROR"); } +} - if (if_exists(path.c_str())) - { - if (check_free_space(path.c_str()) >= app_inst_util_get_size(title_id.c_str())) - { - if (opt == SEL_FPKG) { - tmp = fmt::format("{}/{}.pkg", path, title_id); +void Start_Dump(std::string name, std::string path, multi_select_options::Dump_Multi_Sels opt) { + #if defined(__ORBIS__) - if(copyFile(pkg_path, tmp, true)) - ani_notify(NOTIFI_SUCCESS, getLangSTR(FPKG_COPY_SUCCESS), ""); - else - ani_notify(NOTIFI_WARNING, getLangSTR(FPKG_COPY_FAILED), ""); + if (path.find("/mnt/usb") != std::string::npos) { + if (usbpath() == -1) { + fmt::print("path is %s However, no USB is connected...", path); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_USB), ""); + return; + } + } - return; - } - else if(opt == SEL_GAME_PATCH){ - tmp = fmt::format("/user/patch/{}/patch.pkg", title_id); - if(!if_exists(tmp.c_str())){ - ani_notify(NOTIFI_WARNING, getLangSTR(NO_PATCH_AVAIL), ""); - return; - } - } - log_info("Enough free space"); - log_info("Setting dump flag to: %i", opt); - dump = opt; - if(app_status != RESUMABLE){ - //auto close app before doing a crit suspend - Kill_BigApp(app_status); - if((ret = IPCSendCommand(CRITICAL_SUSPEND, (uint8_t*)&ipc_msg[0]) != NO_ERROR)){ - log_error("Daemon command failed with: %i", ret); - msgok(NORMAL, getLangSTR(DUMP)); - } - Launch_App(title_id, true); + if (if_exists(path.c_str())) { + if (check_free_space(path.c_str()) >= app_inst_util_get_size(all_apps[g_idx].info.id.c_str())) { + if (opt == SEL_FPKG) { + tmp = fmt::format("{}/{}.pkg", path, all_apps[g_idx].info.id); - }else{ - log_info("App is already running, Sending Dump signal with opt: %i ...", dump); - raise(SIGCONT); - } - return; - } + if (copyFile(all_apps[g_idx].info.package, tmp, true)) + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::FPKG_COPY_SUCCESS), ""); else - { - ani_notify(NOTIFI_WARNING, getLangSTR(NOT_ENOUGH_FREE_SPACE), fmt::format("{0:d} GBs | {1:.10}: {2:d} GBs", check_free_space(path.c_str()), title_id, app_inst_util_get_size(title_id.c_str()))); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FPKG_COPY_FAILED), ""); - return; + return; + } else if (opt == SEL_GAME_PATCH) { + tmp = fmt::format("/user/patch/{}/patch.pkg", all_apps[g_idx].info.id); + if (!if_exists(tmp.c_str())) { + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_PATCH_AVAIL), ""); + return; } + } else if (opt == SEL_DLC) { + int vaild_dlcs = 0; + int avail_dlcs = 0; + std::vector> dlc_info = query_dlc_database(title_id.get()); + //print out dlc_info + for (const auto& result: dlc_info) { + std::cout << "PKG: " << std::get<0>(result) << " | DLC Name: " << std::get<1>(result) << " | Full Content ID: " << std::get<2>(result) << std::endl; } - else - { - ani_notify(NOTIFI_WARNING, getLangSTR(DUMPER_PATH_NO_AVAIL), ""); + + std::string addcont_path_str; + std::string user_addcont_dir = "/user/addcont/" + title_id.get(); + std::string ext_addcont_dir = "/mnt/ext0/user/addcont/" + title_id.get(); + std::string disc_addcont_dir = "/mnt/disc/addcont/" + title_id.get(); + + if(if_exists(user_addcont_dir.c_str())){ + addcont_path_str = user_addcont_dir; + } + else if(if_exists(ext_addcont_dir.c_str())){ + addcont_path_str = ext_addcont_dir; + } + else { + addcont_path_str = disc_addcont_dir; + } + //printDirContentRecursively("/mnt/sandbox/pfsmnt/"); + //printDirContentRecursively("/user/addcont/"); + ///addcont_path /= title_id.get(); + + std::vector unloaded_dlcs; + std::vector loaded_dlcs; + + for (const auto& result: dlc_info) { + std::filesystem::path pkg_path(addcont_path_str + "/" + std::get<0>(result)); + if (std::filesystem::is_directory(pkg_path)) { + pkg_path /= "ac.pkg"; + if (std::filesystem::exists(pkg_path) && std::filesystem::is_regular_file(pkg_path)) { + // Construct path to pfs_image.dat file + avail_dlcs++; + if(file_size(pkg_path.c_str()) < MB(5)){ + log_debug("AC PKG is likly just a license"); + vaild_dlcs++; + loaded_dlcs.push_back(std::get<1>(result)); + continue; + } + std::filesystem::path pfs_path("/mnt/sandbox/pfsmnt/"); + std::string full_content_id = std::get<2>(result); + pfs_path /= full_content_id + "-ac-nest"; // 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 | full_content_id %s | pkg %s ", pfs_path.string().c_str(), full_content_id.c_str(), pkg_path.string().c_str()); + log_info("DLC Name: %s", std::get<1>(result).c_str()); + vaild_dlcs++; + loaded_dlcs.push_back(std::get<1>(result)); + } else { + unloaded_dlcs.push_back(std::get<1>(result)); + log_error("PFS file not found"); + } + } + } else { + log_error("Path does not exist or is not a directory: %s", addcont_path_str.c_str()); + ani_notify(NOTIFI::WARNING, "No DLC is Installed", ""); + } + } + + if (avail_dlcs == 0) { + ani_notify(NOTIFI::WARNING, "No DLC is Installed", ""); + return; + } + + if (vaild_dlcs < avail_dlcs) { + std::string unloaded_dlcs_str = fmt::format("Unloaded DLCs: {}\n{}", unloaded_dlcs.size(), fmt::join(unloaded_dlcs, "\n")); + std::string loaded_dlcs_str = fmt::format("Loaded DLCs: {}\n{}", loaded_dlcs.size(), fmt::join(loaded_dlcs, "\n")); + if (Confirmation_Msg(fmt::format("Not all available DLC is loaded by the game and cannot be dumped do you want to continue?\n\nAvailable DLCs: {}\n{}\n{}", avail_dlcs, loaded_dlcs_str, unloaded_dlcs_str)) == NO) return; } -#else - //log_info("Dumping %s to %s, opt: %d", name, path, opt); -#endif +} + log_info("Enough free space"); + log_info("Setting dump flag to: %i", opt); + dump = opt; + if (app_status != RESUMABLE) { + //auto close app before doing a crit suspend + IPC_Client& ipc = IPC_Client::getInstance(); + + Kill_BigApp(app_status); + if (opt != SEL_DLC && (ret = ipc.IPCSendCommand(IPC_Commands::CRITICAL_SUSPEND, ipc_msg) != IPC_Ret::NO_ERROR)) { + log_error("Daemon command failed with: %i", ret); + msgok(MSG_DIALOG::NORMAL, getLangSTR(LANG_STR::DUMP)); + } + Launch_App(title_id.get(), true); + + } else { + log_info("App is already running, Sending Dump signal with opt: %i ...", dump); + raise(SIGCONT); + } + return; + } else { + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NOT_ENOUGH_FREE_SPACE), fmt::format("{0:d} GBs | {1:.10}: {2:d} GBs", check_free_space(path.c_str()), title_id.get(), app_inst_util_get_size(all_apps[g_idx].info.id.c_str()))); + + return; + } + } else { + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::DUMPER_PATH_NO_AVAIL), ""); + return; + } + #else + //log_info("Dumping %s to %s, opt: %d", name, path, opt); + #endif } static void -hostapp_vapp_X_dispatch(int action, layout_t *l) +hostapp_vapp_X_dispatch(int action, layout_t & l) { + IPC_Client& ipc = IPC_Client::getInstance(); - fmt::print("execute {} -> '{}' for '{}'", l->curr_item, l->item_d[l->curr_item].name, all_apps[g_idx].name); - l->vbo_s = ASK_REFRESH; - GAME_PANEL_V_CURR = l->curr_item; - switch (l->curr_item) + fmt::print("execute {} -> '{}' for '{}'", l.curr_item, l.item_d[l.curr_item].info.name, all_apps[g_idx].info.name); + l.vbo_s = ASK_REFRESH; + GAME_PANEL_V_CURR = l.curr_item; + switch (l.curr_item) { - case LAUNCH_HA: + case cf_ops::HOSTAPP_OPTS::LAUNCH_HA: itemzCore_Launch_util(l); break; - case REFRESH_HA: + case cf_ops::HOSTAPP_OPTS::REFRESH_HA: log_info("Refreshing Host App"); #ifdef __ORBIS__ - char ipc_msg[128]; int retry_limit = 0; - while(IPCSendCommand(RESTART_FUSE_FS, (uint8_t*)&ipc_msg[0]) != NO_ERROR && retry_limit < 3){ + while(ipc.IPCSendCommand(IPC_Commands::RESTART_FUSE_FS, ipc_msg) != IPC_Ret::NO_ERROR && retry_limit < 3){ loadmsg("..."); log_info("Awaiting Fuse restart"); sleep(1); @@ -1566,109 +1549,109 @@ hostapp_vapp_X_dispatch(int action, layout_t *l) sceMsgDialogTerminate(); if(retry_limit >= 3){ log_error("Fuse restart failed"); - msgok(NORMAL, fmt::format("{0:}\n\nRet: {1:d}", getLangSTR(NFS_CONNECT_FAILED),ret)); + msgok(MSG_DIALOG::NORMAL, fmt::format("{0:}\n\nRet: {1:d}", getLangSTR(LANG_STR::NFS_CONNECT_FAILED),ret)); } else - ani_notify(NOTIFI_SUCCESS, getLangSTR(NFS_CONNTECTED), "/hostapp"); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::NFS_CONNTECTED), "/hostapp"); #endif break; } } static void -X_action_dispatch(int action, layout_t *l) +X_action_dispatch(int action, layout_t & l) { int ret; int error = -1; // avoid not used warning ret = -1; - fmt::print("execute {} -> '{}' for '{}'", l->curr_item, l->item_d[l->curr_item].name, all_apps[g_idx].name); + fmt::print("execute {} -> '{}' for '{}'", l.curr_item, l.item_d[l.curr_item].info.name, all_apps[g_idx].info.name); std::string dst; std::string src; std::string tmp; std::string usb; - fmt::print("title_id: {} title: {}", title_id, title); + fmt::print("all_apps[g_idx].info.id: {} all_apps[g_idx].info.name: {}", all_apps[g_idx].info.id, all_apps[g_idx].info.name); usb = fmt::format("/mnt/usb{}/", usbpath()); - l->vbo_s = ASK_REFRESH; - GAME_PANEL_V_CURR = l->curr_item; - switch (l->curr_item) + l.vbo_s = ASK_REFRESH; + GAME_PANEL_V_CURR = l.curr_item; + switch (l.curr_item) { // ALL THE 3 MULTI SELS - case Move_apps_opt: - case Game_Save_opt: - case Uninstall_opt: - case Restore_apps_opt: + case cf_ops::REG_OPTS::MOVE_APPS: + case cf_ops::REG_OPTS::GAME_SAVE: + case cf_ops::REG_OPTS::UNINSTALL: + case cf_ops::REG_OPTS::RESTORE_APPS: { - if (l->item_d[l->curr_item].multi_sel.is_active) + if (l.item_d[l.curr_item].multi_sel.is_active) { log_info("Multi sel is active"); if (skipped_first_X) { - switch (l->curr_item) + switch (l.curr_item) { - case Game_Save_opt:{ + case cf_ops::REG_OPTS::GAME_SAVE:{ #if defined(__ORBIS__) if (!gm_save.is_loaded){ - StartFileBrowser((view_t)v_curr, active_p, FS_GP_Callback, FS_FOLDER); + StartFileBrowser((view_t)v_curr, setting_p, FS_GP_Callback, FS_FOLDER); } - else if (l->item_d[l->curr_item].multi_sel.pos.y == RESTORE_GAME_SAVE){ - StartFileBrowser((view_t)v_curr, active_p, FS_GP_Callback, FS_FOLDER); + else if (l.item_d[l.curr_item].multi_sel.pos.y == RESTORE_GAME_SAVE){ + StartFileBrowser((view_t)v_curr, setting_p, FS_GP_Callback, FS_FOLDER); } else - SaveData_Operations((SAVE_Multi_Sel)l->item_d[l->curr_item].multi_sel.pos.y, title_id, ""); + SaveData_Operations((SAVE_Multi_Sel)l.item_d[l.curr_item].multi_sel.pos.y, all_apps[g_idx].info.id, ""); #endif break; } - case Move_apps_opt: + case cf_ops::REG_OPTS::MOVE_APPS: { #if defined(__ORBIS__) if (usbpath() == -1) { log_info("No USB is connected..."); - ani_notify(NOTIFI_WARNING, getLangSTR(NO_USB), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_USB), ""); return; } - else if (l->item_d[l->curr_item].is_ext_hdd) + else if (l.item_d[l.curr_item].flags.is_ext_hdd) { - ani_notify(NOTIFI_WARNING, getLangSTR(EXT_MOVE_NOT_SUPPORTED), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::EXT_MOVE_NOT_SUPPORTED), ""); return; } - if (l->item_d[l->curr_item].multi_sel.pos.y == MOVE_APP && check_free_space(usb.c_str()) < app_inst_util_get_size(title_id.c_str())) + if (l.item_d[l.curr_item].multi_sel.pos.y == multi_select_options::MOVE_APP && check_free_space(usb.c_str()) < app_inst_util_get_size(all_apps[g_idx].info.id.c_str())) { - ani_notify(NOTIFI_WARNING, getLangSTR(NOT_ENOUGH_FREE_SPACE), fmt::format("USB: {0:d} GBs | {1:}: {2:d} GBs", check_free_space(usb.c_str()), title_id, app_inst_util_get_size(title_id.c_str()))); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NOT_ENOUGH_FREE_SPACE), fmt::format("USB: {0:d} GBs | {1:}: {2:d} GBs", check_free_space(usb.c_str()), all_apps[g_idx].info.id, app_inst_util_get_size(all_apps[g_idx].info.id.c_str()))); return; } - dst = fmt::format("{}/itemzflow/apps/{}/", usb, title_id); + dst = fmt::format("{}/itemzflow/apps/{}/", usb, all_apps[g_idx].info.id); std::filesystem::create_directories(std::filesystem::path(dst)); - switch (l->item_d[l->curr_item].multi_sel.pos.y) + switch (l.item_d[l.curr_item].multi_sel.pos.y) { - case MOVE_APP: - src = fmt::format("/user/app/{}/app.pkg", title_id); - dst = fmt::format("{}/itemzflow/apps/{}/app.pkg", usb, title_id); + case multi_select_options::MOVE_APP: + src = fmt::format("/user/app/{}/app.pkg", all_apps[g_idx].info.id); + dst = fmt::format("{}/itemzflow/apps/{}/app.pkg", usb, all_apps[g_idx].info.id); break; - case MOVE_DLC: - src = fmt::format("/user/addcont/{}/ac.pkg", title_id); - dst = fmt::format("{}/itemzflow/apps/{}/app_dlc.pkg", usb, title_id); + case multi_select_options::MOVE_DLC: + src = fmt::format("/user/addcont/{}/ac.pkg", all_apps[g_idx].info.id); + dst = fmt::format("{}/itemzflow/apps/{}/app_dlc.pkg", usb, all_apps[g_idx].info.id); break; - case MOVE_PATCH: - src = fmt::format("/user/patch/{}/patch.pkg", title_id); - dst = fmt::format("{}/itemzflow/apps/{}/app_patch.pkg", usb, title_id); + case multi_select_options::MOVE_PATCH: + src = fmt::format("/user/patch/{}/patch.pkg", all_apps[g_idx].info.id); + dst = fmt::format("{}/itemzflow/apps/{}/app_patch.pkg", usb, all_apps[g_idx].info.id); break; default: break; } if(if_exists(dst.c_str())) { //ALREADY BACKED UP - ani_notify(NOTIFI_WARNING, getLangSTR(THE_ITEM_IS_ALREADY_BP), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::THE_ITEM_IS_ALREADY_BP), getLangSTR(LANG_STR::OP_CANNED_1)); break; } if (!copyFile(src, dst, true)) - ani_notify(NOTIFI_WARNING, getLangSTR(MOVE_FAILED), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::MOVE_FAILED), getLangSTR(LANG_STR::OP_CANNED_1)); else { loadmsg("Validating Move..."); @@ -1682,73 +1665,73 @@ X_action_dispatch(int action, layout_t *l) dst = fmt::format("{}/itemzflow/licenses", usb); copy_dir((char*)"/user/license", (char*)dst.c_str()); - switch (l->item_d[l->curr_item].multi_sel.pos.y) + switch (l.item_d[l.curr_item].multi_sel.pos.y) { case MOVE_APP: - ani_notify(NOTIFI_GAME, getLangSTR(APP_MOVE_NOTIFY), title_id); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::APP_MOVE_NOTIFY), all_apps[g_idx].info.id); break; case MOVE_DLC: - ani_notify(NOTIFI_GAME, getLangSTR(DLC_MOVE_NOTIFY), title_id); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::DLC_MOVE_NOTIFY), all_apps[g_idx].info.id); break; case MOVE_PATCH: - ani_notify(NOTIFI_GAME, getLangSTR(PATCH_MOVE_NOTIFY), title_id); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::PATCH_MOVE_NOTIFY), all_apps[g_idx].info.id); break; } } else { - ani_notify(NOTIFI_WARNING, getLangSTR(MOVE_FAILED), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::MOVE_FAILED), getLangSTR(LANG_STR::OP_CANNED_1)); } } sceMsgDialogTerminate(); #endif break; } - case Restore_apps_opt: + case cf_ops::REG_OPTS::RESTORE_APPS: { #if defined(__ORBIS__) if (usbpath() == -1) { log_info("No USB is connected..."); - ani_notify(NOTIFI_WARNING, getLangSTR(NO_USB), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_USB), ""); return; } - src = fmt::format("{}/itemzflow/apps/{}/app.pkg", usb, title_id); + src = fmt::format("{}/itemzflow/apps/{}/app.pkg", usb, all_apps[g_idx].info.id); int len = (int)CalcFreeGigs(src.c_str()); bool app_moved = if_exists(src.c_str()); - src = fmt::format("{}/itemzflow/apps/{}/app_dlc.pkg", usb, title_id); + src = fmt::format("{}/itemzflow/apps/{}/app_dlc.pkg", usb, all_apps[g_idx].info.id); bool dlc_moved = if_exists(src.c_str()); - src = fmt::format("{}/itemzflow/apps/{}/app_patch.pkg", usb, title_id); + src = fmt::format("{}/itemzflow/apps/{}/app_patch.pkg", usb, all_apps[g_idx].info.id); bool patch_moved = if_exists(src.c_str()); if (!app_moved && !dlc_moved && !patch_moved) { - ani_notify(NOTIFI_WARNING, getLangSTR(APP_NOT_MOVED), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::APP_NOT_MOVED), ""); return; } - if (l->item_d[l->curr_item].multi_sel.pos.y == RESTORE_APP && len > check_free_space(usb.c_str())) + if (l.item_d[l.curr_item].multi_sel.pos.y == RESTORE_APP && len > check_free_space(usb.c_str())) { - ani_notify(NOTIFI_WARNING, getLangSTR(NOT_ENOUGH_FREE_SPACE), fmt::format("{0:d} GBs | /user/app: {1:d} GBs", check_free_space(usb.c_str()), len)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NOT_ENOUGH_FREE_SPACE), fmt::format("{0:d} GBs | /user/app: {1:d} GBs", check_free_space(usb.c_str()), len)); return; } - switch (l->item_d[l->curr_item].multi_sel.pos.y) + switch (l.item_d[l.curr_item].multi_sel.pos.y) { - case RESTORE_APP:{ - src = fmt::format("{}/itemzflow/apps/{}/app.pkg", usb, title_id); - dst = fmt::format("/user/app/{}/app.pkg", title_id); + case multi_select_options::RESTORE_APP:{ + src = fmt::format("{}/itemzflow/apps/{}/app.pkg", usb, all_apps[g_idx].info.id); + dst = fmt::format("/user/app/{}/app.pkg", all_apps[g_idx].info.id); break; } - case RESTORE_DLC:{ - src = fmt::format("{}/itemzflow/apps/{}/app_dlc.pkg", usb, title_id); - dst = fmt::format("/user/addcont/{}/ac.pkg", title_id); + case multi_select_options::RESTORE_DLC:{ + src = fmt::format("{}/itemzflow/apps/{}/app_dlc.pkg", usb, all_apps[g_idx].info.id); + dst = fmt::format("/user/addcont/{}/ac.pkg", all_apps[g_idx].info.id); break; } - case RESTORE_PATCH:{ - src = fmt::format("{}/itemzflow/apps/{}/app_patch.pkg", usb, title_id); - dst = fmt::format("/user/patch/{}/patch.pkg", title_id); + case multi_select_options::RESTORE_PATCH:{ + src = fmt::format("{}/itemzflow/apps/{}/app_patch.pkg", usb, all_apps[g_idx].info.id); + dst = fmt::format("/user/patch/{}/patch.pkg", all_apps[g_idx].info.id); break; } default: @@ -1759,36 +1742,36 @@ X_action_dispatch(int action, layout_t *l) // RESTORE the License files after reinstalling dst = fmt::format("{}/itemzflow/licenses", usb); // Reinstall PKG (Uninstalls app first) with progress bar - if ((ret = pkginstall(src.c_str(), title_id.c_str(), true, true)) != ITEMZCORE_SUCCESS && copy_dir((char*)dst.c_str(), (char*)"/user/license")){ + if ((ret = pkginstall(src.c_str(), all_apps[g_idx].info.id.c_str(), true, true)) != ITEMZCORE_SUCCESS && copy_dir(dst, "/user/license")){ if (ret == IS_DISC_GAME) - ani_notify(NOTIFI_WARNING, getLangSTR(OPTION_DOESNT_SUPPORT_DISC), fmt::format("{0:#x}", ret)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OPTION_DOESNT_SUPPORT_DISC), fmt::format("{0:#x}", ret)); else - ani_notify(NOTIFI_WARNING, getLangSTR(RESTORE_FAILED), fmt::format("{0:#x}", ret)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::RESTORE_FAILED), fmt::format("{0:#x}", ret)); } else if (ret == 0) { fmt::print("[RESTORE] installed {} >> {}", src, dst); - ani_notify(NOTIFI_GAME, getLangSTR(RESTORE_SUCCESS), fmt::format("@ {0:}", dst)); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::RESTORE_SUCCESS), fmt::format("@ {0:}", dst)); sceMsgDialogTerminate(); } #endif break; } - case Uninstall_opt: + case cf_ops::REG_OPTS::UNINSTALL: { #if defined(__ORBIS__) - switch ((Uninstall_Multi_Sel)get->un_opt) + switch ((multi_select_options::Uninstall_Multi_Sel)get->un_opt) { case UNINSTALL_PATCH: { - if (Confirmation_Msg(uninstall_get_str((Uninstall_Multi_Sel)get->un_opt)) == NO) + if (Confirmation_Msg(uninstall_get_str((multi_select_options::Uninstall_Multi_Sel)get->un_opt)) == NO) { break; } - if (app_inst_util_uninstall_patch(title_id.c_str(), &error) && error == 0) - ani_notify(NOTIFI_GAME, getLangSTR(UNINSTALL_UPDATE), ""); + if (app_inst_util_uninstall_patch(all_apps[g_idx].info.id.c_str(), &error) && error == 0) + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::UNINSTALL_UPDATE), ""); else - ani_notify(NOTIFI_WARNING, getLangSTR(UNINSTAL_UPDATE_FAILED), fmt::format("{0:}: {1:#x} ({2:d})", getLangSTR(ERROR_CODE), error, error)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::UNINSTAL_UPDATE_FAILED), fmt::format("{0:}: {1:#x} ({2:d})", getLangSTR(LANG_STR::ERROR_CODE), error, error)); break; } @@ -1798,15 +1781,15 @@ X_action_dispatch(int action, layout_t *l) { break; } - if (app_inst_util_uninstall_game(title_id.c_str(), &error) && error == 0) + if (app_inst_util_uninstall_game(all_apps[g_idx].info.id.c_str(), &error) && error == 0) { - ani_notify(NOTIFI_GAME, getLangSTR(UNINSTALL_GAME1), ""); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::UNINSTALL_GAME1), ""); fw_action_to_cf(0x1337); - ani_notify(NOTIFI_GAME, getLangSTR(APPS_RELOADED), getLangSTR(APPS_RELOADED_2)); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::APPS_RELOADED), getLangSTR(LANG_STR::APPS_RELOADED_2)); refresh_apps_for_cf(get->sort_by, get->sort_cat); } else - ani_notify(NOTIFI_WARNING, getLangSTR(UNINSTALL_FAILED), fmt::format("{0:}: {1:#x} ({2:d})", getLangSTR(ERROR_CODE), error, error)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::UNINSTALL_FAILED), fmt::format("{0:}: {1:#x} ({2:d})", getLangSTR(LANG_STR::ERROR_CODE), error, error)); break; } @@ -1829,37 +1812,37 @@ X_action_dispatch(int action, layout_t *l) skipped_first_X = true; } - if (l->item_d[l->curr_item].multi_sel.pos.x == 0) - l->item_d[l->curr_item].multi_sel.pos.x = 1; // EDITABLE + if (l.item_d[l.curr_item].multi_sel.pos.x == 0) + l.item_d[l.curr_item].multi_sel.pos.x = 1; // EDITABLE else - l->item_d[l->curr_item].multi_sel.pos.x = 0; + l.item_d[l.curr_item].multi_sel.pos.x = 0; } break; } // end of huge switch - case Dump_Game_opt: + case cf_ops::REG_OPTS::DUMP_GAME: { - if (l->item_d[l->curr_item].multi_sel.is_active) + if (l.item_d[l.curr_item].multi_sel.is_active) { - int last_opt = SEL_DLC; - fmt::print("$$$$ item {} is Multi {}", l->curr_item, skipped_first_X); + int last_opt = multi_select_options::SEL_DLC; + fmt::print("$$$$ item {} is Multi {}", l.curr_item, skipped_first_X); - if (l->item_d[l->curr_item].is_fpkg) - last_opt = SEL_FPKG; + if (l.item_d[l.curr_item].flags.is_fpkg) + last_opt = multi_select_options::SEL_FPKG; - if (l->item_d[l->curr_item].multi_sel.pos.x == last_opt) - l->item_d[l->curr_item].multi_sel.pos.x = SEL_DUMP_ALL; - else if (l->item_d[l->curr_item].multi_sel.pos.x == SEL_DUMP_ALL) - l->item_d[l->curr_item].multi_sel.pos.x++; + if (l.item_d[l.curr_item].multi_sel.pos.x == last_opt) + l.item_d[l.curr_item].multi_sel.pos.x = multi_select_options::SEL_DUMP_ALL; + else if (l.item_d[l.curr_item].multi_sel.pos.x == multi_select_options::SEL_DUMP_ALL) + l.item_d[l.curr_item].multi_sel.pos.x++; else - l->item_d[l->curr_item].multi_sel.pos.x--; + l.item_d[l.curr_item].multi_sel.pos.x--; if (skipped_first_X) { log_info("dumping option: %i ...", get->Dump_opt); #if defined(__ORBIS__) - Start_Dump(title, get->setting_strings[DUMPER_PATH], get->Dump_opt); + Start_Dump(all_apps[g_idx].info.name, get->setting_strings[DUMPER_PATH], get->Dump_opt); #else - Start_Dump(title, "NOT A REAL PATH", get->Dump_opt); + Start_Dump(all_apps[g_idx].info.name, "NOT A REAL PATH", get->Dump_opt); #endif skipped_first_X = false; } @@ -1868,18 +1851,18 @@ X_action_dispatch(int action, layout_t *l) } break; } - case Launch_Game_opt: + case cf_ops::REG_OPTS::LAUNCH_GAME: { itemzCore_Launch_util(l); break; } - case Trainers_opt: + case cf_ops::REG_OPTS::TRAINERS: { #if defined(__ORBIS__) - if (get_patch_path(title_id)){ - patch_current_index = l->item_d[l->curr_item].multi_sel.pos.y; + if (get_patch_path(all_apps[g_idx].info.id)){ + patch_current_index = l.item_d[l.curr_item].multi_sel.pos.y; if (skipped_first_X) { - std::string patch_file = fmt::format("{}/patches/xml/{}.xml", patch_path_base, title_id); + std::string patch_file = fmt::format("{}/patches/xml/{}.xml", patch_path_base, all_apps[g_idx].info.id); std::string patch_title = trs.patcher_title.at(patch_current_index); std::string patch_name = trs.patcher_name.at(patch_current_index); std::string patch_app_ver = trs.patcher_app_ver.at(patch_current_index); @@ -1898,53 +1881,53 @@ X_action_dispatch(int action, layout_t *l) trs.patcher_note.clear(); trs.patcher_enablement.clear(); trs.controls_text.clear(); - l->item_d[l->curr_item].multi_sel.pos.x = RESET_MULTI_SEL; - l->vbo_s = ASK_REFRESH; + l.item_d[l.curr_item].multi_sel.pos.x = RESET_MULTI_SEL; + l.vbo_s = ASK_REFRESH; skipped_first_X = false; break; } - std::string patch_file = fmt::format("{}/patches/xml/{}.xml", patch_path_base, title_id); + std::string patch_file = fmt::format("{}/patches/xml/{}.xml", patch_path_base, all_apps[g_idx].info.id); pugi::xml_document doc; if (!doc.load_file(patch_file.c_str())) { - msgok(NORMAL, fmt::format("File {} open failed", patch_file)); + msgok(MSG_DIALOG::NORMAL, fmt::format("File {} open failed", patch_file)); } pugi::xml_node patches = doc.child("Patch"); u32 patch_count = 0; for (pugi::xml_node patch = patches.child("Metadata"); patch; patch = patch.next_sibling("Metadata")) { - std::string gameTitle = patch.attribute("Title").value(); + std::string gameTitle = patch.attribute("all_apps[g_idx].info.name").value(); std::string gameName = patch.attribute("Name").value(); std::string gameNote = patch.attribute("Note").value(); std::string gameAuthor = patch.attribute("Author").value(); std::string gamePatchVer = patch.attribute("PatchVer").value(); std::string gameAppver = patch.attribute("AppVer").value(); std::string gameAppElf = patch.attribute("AppElf").value(); - if (app_version.compare(gameAppver) != 0) continue; + if (all_apps[g_idx].info.version.compare(gameAppver) != 0) continue; // don't assign if patch version and app version don't match get_metadata1(&trs, gameAppver, gameAppElf, gameTitle, gamePatchVer, gameName, gameAuthor, gameNote); patch_count++; - std::string patch_control_title = fmt::format("{1} {0}", patch_count, getLangSTR(PATCH_NUM)); + std::string patch_control_title = fmt::format("{1} {0}", patch_count, getLangSTR(LANG_STR::PATCH_NUM)); trs.controls_text.push_back(patch_control_title); } skipped_first_X = true; - if (l->item_d[l->curr_item].multi_sel.pos.x == trs.controls_text.size()) { - l->item_d[l->curr_item].multi_sel.pos.x = 0; + if (l.item_d[l.curr_item].multi_sel.pos.x == trs.controls_text.size()) { + l.item_d[l.curr_item].multi_sel.pos.x = 0; } - else if (l->item_d[l->curr_item].multi_sel.pos.x == 0) { - l->item_d[l->curr_item].multi_sel.pos.x++; + else if (l.item_d[l.curr_item].multi_sel.pos.x == 0) { + l.item_d[l.curr_item].multi_sel.pos.x++; } else { - l->item_d[l->curr_item].multi_sel.pos.x--; + l.item_d[l.curr_item].multi_sel.pos.x--; } break; } else { - std::string patchq = fmt::format("{}\n{}", getLangSTR(NO_PATCHES), getLangSTR(PATCH_DL_QUESTION)); + std::string patchq = fmt::format("{}\n{}", getLangSTR(LANG_STR::NO_PATCHES), getLangSTR(LANG_STR::PATCH_DL_QUESTION)); int conf = Confirmation_Msg(patchq); if(conf == YES){ dl_patches(); } else if (conf == NO) { - msgok(NORMAL, getLangSTR(PATCH_DL_QUESTION_NO)); + msgok(MSG_DIALOG::NORMAL, getLangSTR(LANG_STR::PATCH_DL_QUESTION_NO)); } sceMsgDialogTerminate(); break; @@ -1953,71 +1936,67 @@ X_action_dispatch(int action, layout_t *l) break; } #if defined(__ORBIS__) - case Hide_App_opt: + case cf_ops::REG_OPTS::HIDE_APP: { - log_info("App vis b4: %i", AppVis.load()); - msgok(WARNING, getLangSTR(DB_MOD_WARNING)); + log_info("App vis b4: %i", all_apps[g_idx].flags.app_vis); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::DB_MOD_WARNING)); - if (!AppDBVisiable(title, VIS_WRTIE, AppVis ? 0 : 1)) - ani_notify(NOTIFI_WARNING, getLangSTR(UNHIDE_FAILED_NOTIFY), ""); + if (!AppDBVisiable(all_apps[g_idx].info.name, VIS_WRTIE, all_apps[g_idx].flags.app_vis ? 0 : 1)) + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::UNHIDE_FAILED_NOTIFY), ""); else { - if (AppVis) - ani_notify(NOTIFI_GAME, getLangSTR(UNHIDE_NOTIFY), getLangSTR(HIDE_SUCCESS_NOTIFY)); + if (all_apps[g_idx].flags.app_vis) + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::UNHIDE_NOTIFY), getLangSTR(LANG_STR::HIDE_SUCCESS_NOTIFY)); else - ani_notify(NOTIFI_GAME, getLangSTR(UNHIDE_NOTIFY), getLangSTR(UNHIDE_SUCCESS_NOTIFY)); - - AppVis = !AppVis; - all_apps[g_idx].app_vis = AppVis; + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::UNHIDE_NOTIFY), getLangSTR(LANG_STR::UNHIDE_SUCCESS_NOTIFY)); } break; } - case Change_name_opt: + case cf_ops::REG_OPTS::CHANGE_NAME: { // SCE Keyboard C Function needs a char array - msgok(WARNING, getLangSTR(DB_MOD_WARNING)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::DB_MOD_WARNING)); char buf[255]; - if (!Keyboard(getLangSTR(CHANGE_APP_NAME).c_str(), title.c_str(), &buf[0])) + if (!Keyboard(getLangSTR(LANG_STR::CHANGE_APP_NAME).c_str(), all_apps[g_idx].info.name.c_str(), &buf[0])) return; tmp = std::string(&buf[0]); - if (change_game_name(tmp, title_id, all_apps[g_idx].sfopath)) + if (change_game_name(tmp, all_apps[g_idx].info.id, all_apps[g_idx].info.sfopath)) { - pthread_mutex_lock(&disc_lock); - - all_apps[g_idx].name = tmp; - - pthread_mutex_unlock(&disc_lock); + { + std::lock_guard lock(disc_lock); + all_apps[g_idx].info.name= tmp; + } - ani_notify(NOTIFI_GAME, getLangSTR(TITLE_CHANGED), getLangSTR(PS4_REBOOT_REQ)); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::TITLE_CHANGED), getLangSTR(LANG_STR::PS4_REBOOT_REQ)); } else - ani_notify(NOTIFI_WARNING, getLangSTR(FAILED_TITLE_CHANGE), "0xDEADBEEF"); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_TITLE_CHANGE), "0xDEADBEEF"); break; } #endif - case Change_icon_opt: - StartFileBrowser((view_t)v_curr, active_p, FS_GP_Callback, FS_PNG); + case cf_ops::REG_OPTS::CHANGE_ICON: + StartFileBrowser((view_t)v_curr, ls_p, FS_GP_Callback, FS_PNG); break; default: break; } } -static void item_page_Square_action(layout_t *l) +static void item_page_Square_action(layout_t & l) { #if defined(__ORBIS__) - if (l->item_d[l->curr_item].multi_sel.is_active && l->item_d[l->curr_item].multi_sel.pos.x != RESET_MULTI_SEL) + if (l.item_d[l.curr_item].multi_sel.is_active && l.item_d[l.curr_item].multi_sel.pos.x != RESET_MULTI_SEL) { - switch (l->curr_item) + switch (l.curr_item) { - case Trainers_opt: - if (get_patch_path(title_id)){ - patch_current_index = l->item_d[l->curr_item].multi_sel.pos.y; + case cf_ops::REG_OPTS::TRAINERS: + if (get_patch_path(all_apps[g_idx].info.id)){ + patch_current_index = l.item_d[l.curr_item].multi_sel.pos.y; std::string patch_title = trs.patcher_title.at(patch_current_index); std::string patch_name = trs.patcher_name.at(patch_current_index); std::string patch_app_ver = trs.patcher_app_ver.at(patch_current_index); @@ -2032,44 +2011,62 @@ static void item_page_Square_action(layout_t *l) "{} {}\n" "{} {}\n" "{} {}\n", - getLangSTR(PATCH_GAME_TITLE), + getLangSTR(LANG_STR::PATCH_GAME_TITLE), patch_title, - getLangSTR(PATCH_NAME), + getLangSTR(LANG_STR::PATCH_NAME), patch_name, - getLangSTR(PATCH_APP_VER), + getLangSTR(LANG_STR::PATCH_APP_VER), patch_app_ver, - getLangSTR(PATCH_AUTHOR), + getLangSTR(LANG_STR::PATCH_AUTHOR), patch_author, - getLangSTR(PATCH_NOTE), + getLangSTR(LANG_STR::PATCH_NOTE), patch_note, - getLangSTR(PATCH_VER), + getLangSTR(LANG_STR::PATCH_VER), patch_ver, - getLangSTR(PATCH_APP_ELF), + getLangSTR(LANG_STR::PATCH_APP_ELF), patch_app_elf); - msgok(NORMAL, patch_msg); + msgok(MSG_DIALOG::NORMAL, patch_msg); } break; default: break; } + + return; } #endif + Favorites favorites; + if(favorites.loadFromFile(APP_PATH("favorites.dat"))) + log_info("Favorites have beend loaded!"); + else + log_warn("Failed to load favorites.dat does it exist??"); + + + all_apps[g_idx].flags.is_favorite ? favorites.removeFavorite(all_apps[g_idx].info.id) : favorites.addFavorite(all_apps[g_idx].info.id); + all_apps[g_idx].flags.is_favorite = !all_apps[g_idx].flags.is_favorite; + + favs.clear(); + + favorites.saveToFile(APP_PATH("favorites.dat")); + + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::FAVORITES), all_apps[g_idx].flags.is_favorite ? getLangSTR(LANG_STR::FAVORITES_ADD) : getLangSTR(LANG_STR::FAVORITES_REMOVED)); + return; } -static void item_page_Triangle_action(layout_t *l) +static void item_page_Triangle_action(layout_t & l) { #if defined(__ORBIS__) - if (l->item_d[l->curr_item].multi_sel.is_active && l->item_d[l->curr_item].multi_sel.pos.x != RESET_MULTI_SEL) + if (l.item_d[l.curr_item].multi_sel.is_active && l.item_d[l.curr_item].multi_sel.pos.x != RESET_MULTI_SEL) { - switch (l->curr_item) + switch (l.curr_item) { - case Trainers_opt: + case cf_ops::REG_OPTS::TRAINERS: { - int conf = Confirmation_Msg(getLangSTR(PATCH_DL_QUESTION)); + int conf = Confirmation_Msg(getLangSTR(LANG_STR::PATCH_DL_QUESTION)); if(conf == YES){ dl_patches(); } else if (conf == NO) { - msgok(NORMAL, getLangSTR(PATCH_DL_QUESTION_NO)); + msgok(MSG_DIALOG::NORMAL, getLangSTR(LANG_STR::PATCH_DL_QUESTION_NO)); } sceMsgDialogTerminate(); break; @@ -2082,34 +2079,35 @@ static void item_page_Triangle_action(layout_t *l) return; } -static void item_page_O_action(layout_t *l) +static void item_page_O_action(layout_t & l) { - if (l->item_d.size() >= l->curr_item && l->item_d[l->curr_item].multi_sel.is_active - &&l->item_d[l->curr_item].multi_sel.pos.x != RESET_MULTI_SEL) + if (l.item_d.size() >= l.curr_item && l.item_d[l.curr_item].multi_sel.is_active + &&l.item_d[l.curr_item].multi_sel.pos.x != RESET_MULTI_SEL) { - l->item_d[l->curr_item].multi_sel.pos.y = l->item_d[l->curr_item].multi_sel.pos.x = RESET_MULTI_SEL; - l->item_d[l->curr_item].multi_sel.is_active = false; - switch (l->curr_item) + l.item_d[l.curr_item].multi_sel.pos.y = l.item_d[l.curr_item].multi_sel.pos.x = RESET_MULTI_SEL; + l.item_d[l.curr_item].multi_sel.is_active = false; + switch (l.curr_item) { - case Uninstall_opt: get->un_opt = UNINSTALL_GAME; - case Trainers_opt: - case Dump_Game_opt: get->Dump_opt = SEL_DUMP_ALL; - case Game_Save_opt: - case Launch_Game_opt: - case Restore_apps_opt: - case Move_apps_opt: + case cf_ops::REG_OPTS::UNINSTALL: get->un_opt = multi_select_options::UNINSTALL_GAME; + case cf_ops::REG_OPTS::TRAINERS: + case cf_ops::REG_OPTS::DUMP_GAME: get->Dump_opt = multi_select_options::SEL_DUMP_ALL; + case cf_ops::REG_OPTS::GAME_SAVE: + case cf_ops::REG_OPTS::LAUNCH_GAME: + case cf_ops::REG_OPTS::RESTORE_APPS: + case cf_ops::REG_OPTS::MOVE_APPS: goto multi_back; default: break; } } - l->is_active = l->is_shown = false; + l.is_active = l.is_shown = false; ls_p.curr_item = 0; + setting_p.curr_item = 0; - gm_p.is_active = false; - gm_p.vbo_s = ASK_REFRESH; - v2 = set_view(ITEMzFLOW); + ls_p.is_active = false; + ls_p.vbo_s = ASK_REFRESH; + v2 = set_view_main(ITEMzFLOW); return; @@ -2130,24 +2128,27 @@ multi_back: trs.controls_text.clear(); patch_current_index = 0; #endif - l->vbo_s = ASK_REFRESH; + l.vbo_s = ASK_REFRESH; skipped_first_X = false; } void fw_action_to_cf(int button) { - // ask to refresh vbo to update title + // ask to refresh vbo to update all_apps[g_idx].info.name // avoid not used warning + IPC_Client& ipc = IPC_Client::getInstance(); + bool is_file_manager = (v_curr == FILE_BROWSER_LEFT || v_curr == FILE_BROWSER_RIGHT); + ret = -1; - if (v_curr == FILE_BROWSER) + if (is_file_manager) { fw_action_to_fm(button); return; } - GLES2_UpdateVboForLayout(&gm_p); - GLES2_UpdateVboForLayout(&ls_p); - GLES2_UpdateVboForLayout(&setting_p); + GLES2_UpdateVboForLayout(ls_p); + GLES2_UpdateVboForLayout(ls_p); + GLES2_UpdateVboForLayout(setting_p); GLES2_refresh_sysinfo(); @@ -2156,14 +2157,13 @@ void fw_action_to_cf(int button) if (v_curr == ITEMzFLOW) { - active_p = NULL; // switch (button) { // movement case L2: if (get->sort_cat != NO_CATEGORY_FILTER){ get->sort_cat = NO_CATEGORY_FILTER; refresh_apps_for_cf(get->sort_by); - v2 = set_view(ITEMzFLOW); + v2 = set_view_main(ITEMzFLOW); } break; case LEF: @@ -2174,11 +2174,11 @@ void fw_action_to_cf(int button) break; case OPT: confirm_close = false; - active_p = &setting_p; //reset selection pos - active_p->curr_item = 0; - active_p->f_sele = 0; - v2 = set_view(ITEM_SETTINGS); + setting_p.curr_item = 0; + setting_p.f_sele = 0; + v2 = set_view_main(ITEM_SETTINGS); + //StartFileBrowser((view_t)v_curr, active_p, NULL, FS_NONE); break; @@ -2187,52 +2187,44 @@ void fw_action_to_cf(int button) { confirm_close = false; // Placeholder games are not interactable - if (all_apps[g_idx].is_ph){ - ani_notify(NOTIFI_WARNING, getLangSTR(PLACEHOLDER_NOT_ENTER), ""); + if (all_apps[g_idx].flags.is_ph){ + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::PLACEHOLDER_NOT_ENTER), ""); return; } - active_p = &ls_p; // control cf_game_options - + //reset selection pos - active_p->curr_item = 0; - active_p->f_sele = 0; - - pthread_mutex_lock(&disc_lock); - - pkg_path = all_apps[g_idx].package; - icon_path = all_apps[g_idx].picpath; - title = all_apps[g_idx].name; - title_id = all_apps[g_idx].id; - app_version = all_apps[g_idx].version; - is_dumpable = all_apps[g_idx].is_dumpable; + ls_p.curr_item = 0; + ls_p.f_sele = 0; + std::lock_guard lock(disc_lock); + title_id.set(all_apps[g_idx].info.id); started_epoch = mins_played = MIN_STATUS_RESET; - current_app_is_fpkg = all_apps[g_idx].is_fpkg; - AppVis = all_apps[g_idx].app_vis; + + favs.clear(); // game save clear gm_save.is_loaded = false; - if(is_vapp(title_id)){ // REFRESH_HOSTAPP=Refresh Hostapp - if(title_id == APP_HOME_HOST_TID) - gm_p_text[1] = fmt::format("{0:.20}", getLangSTR(REFRESH_HOSTAPP)); + if(is_vapp(all_apps[g_idx].info.id)){ // REFRESH_HOSTAPP=Refresh Hostapp + if(all_apps[g_idx].info.id == APP_HOME_HOST_TID) + gm_p_text[1] = fmt::format("{0:.20}", getLangSTR(LANG_STR::REFRESH_HOSTAPP)); } else - gm_p_text[1] = fmt::format("{0:.20}", getLangSTR(DUMP_1)); + gm_p_text[1] = fmt::format("{0:.20}", getLangSTR(LANG_STR::DUMP_1)); // reset game panel to not active so it gets refreshed ls_p.is_active = false; - fmt::print("|{}|: is_fpkg: {} AppVis: {} {}", title, current_app_is_fpkg, AppVis.load(), g_idx); + fmt::print("|{}|: is_fpkg: {} all_apps[g_idx].flags.app_vis: {} {}", all_apps[g_idx].info.name, all_apps[g_idx].flags.is_fpkg, all_apps[g_idx].flags.app_vis, g_idx); // Every X press on a Game case, we will check if the game selected is opened or not - // If the App Status changed while in this view the Signal Handler will update the app status and view + // If the App Status changed while in this view_main the Signal Handler will update the app status and view_main // then the render function will redraw text if the app status changed // First we check if the game is opened, if not sceLncUtilGetAppId will return -1 // After we check if the game id is == to the currently opened bigapps id #if defined(__ORBIS__) - int id = sceLncUtilGetAppId(title_id.c_str()); - if ((title_id == vapp_title_launched && is_remote_va_launched) || + int id = sceLncUtilGetAppId(all_apps[g_idx].info.id.c_str()); + if ((all_apps[g_idx].info.id == vapp_title_launched && is_remote_va_launched) || ((id & ~0xFFFFFF) == 0x60000000 && id == sceSystemServiceGetAppIdOfBigApp())) { - fmt::print("found app to resume: {0:}, AppId: {1:#x}", title_id, id); + fmt::print("found app to resume: {0:}, AppId: {1:#x}", all_apps[g_idx].info.id, id); app_status = RESUMABLE; // set the app status to resumable } else @@ -2246,18 +2238,18 @@ void fw_action_to_cf(int button) #else app_status = NO_APP_OPENED; // set the app status to no app opened #endif - pthread_mutex_unlock(&disc_lock); - // set the view to the select game - v2 = set_view(ITEM_PAGE); + // set the view_main to the select game + v2 = set_view_main(ITEM_PAGE); + // v_curr = ITEM_PAGE; break; // in_out } case TRI: { // REFRESH APP - // Update View + // Update view_main ani[0].is_running = true; confirm_close = false; refresh_apps_for_cf(get->sort_by, get->sort_cat); - ani_notify(NOTIFI_SUCCESS, getLangSTR(APPS_RELOADED), getLangSTR(APPS_RELOADED_2)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::APPS_RELOADED), getLangSTR(LANG_STR::APPS_RELOADED_2)); break; } case SQU:{ @@ -2272,11 +2264,12 @@ void fw_action_to_cf(int button) // workaround for [cannot increment/decrement expression of enum type] get->sort_cat = (Sort_Category)cat; refresh_apps_for_cf(get->sort_by, get->sort_cat); - ani_notify(NOTIFI_SUCCESS, getLangSTR(UPDATED_CATEGORY), category_get_str(get->sort_cat)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::UPDATED_CATEGORY), category_get_str(get->sort_cat)); break; } - /* case UP: gx -= 5.; log_info("gx:%.3f", gx); break; - case DOW: gx += 5.; log_info("gx:%.3f", gx); break; */ + /*case UP: gx -= 5.; log_info("gx:%.3f", gx); break; + case DOW: gx += 5.; log_info("gx:%.3f", gx); break; */ + case CIR: { @@ -2291,14 +2284,12 @@ void fw_action_to_cf(int button) } else { - ani_notify(NOTIFI_INFO, getLangSTR(BUTTON_CONFIRM_CLOSE), ""); + ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::BUTTON_CONFIRM_CLOSE), ""); confirm_close = true; } - pthread_mutex_lock(&disc_lock); drop_some_coverbox(); - pthread_mutex_unlock(&disc_lock); - v2 = set_view(ITEMzFLOW); + v2 = set_view_main(ITEMzFLOW); } return; } @@ -2308,60 +2299,60 @@ void fw_action_to_cf(int button) switch (button) { // movement case LEF: - layout_update_sele(active_p, -1); + layout_update_sele(ls_p, -1); break; // l_or_r case RIG: - layout_update_sele(active_p, +1); + layout_update_sele(ls_p, +1); break; case UP: - layout_update_sele(active_p, -active_p->fieldsize.x); + layout_update_sele(ls_p, -ls_p.fieldsize.x); break; case DOW: - layout_update_sele(active_p, +active_p->fieldsize.x); + layout_update_sele(ls_p, +ls_p.fieldsize.x); break; case CRO: - if(is_vapp(title_id)){ - if(title_id == APP_HOME_HOST_TID){ - hostapp_vapp_X_dispatch(0, active_p); + if(is_vapp(all_apps[g_idx].info.id)){ + if(all_apps[g_idx].info.id == APP_HOME_HOST_TID){ + hostapp_vapp_X_dispatch(0, ls_p); } } else // reg ps4 app - X_action_dispatch(0, active_p); + X_action_dispatch(0, ls_p); break; // in_out case SQU: - item_page_Square_action(active_p); + item_page_Square_action(ls_p); break; case CIR: - item_page_O_action(active_p); + item_page_O_action(ls_p); break; case OPT: //eject disc if(inserted_disc.load() > 0){ #if __ORBIS__ - if(Confirmation_Msg(getLangSTR(EJECT_DISK)) == NO){ - ani_notify(NOTIFI_WARNING, getLangSTR(OP_CANNED), ""); + if(Confirmation_Msg(getLangSTR(LANG_STR::EJECT_DISK)) == NO){ + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OP_CANNED), ""); break; } if((ret = sceShellCoreUtilRequestEjectDevice("/dev/cd0")) < 0) - ani_notify(NOTIFI_ERROR, getLangSTR(EJECT_DISK_ERROR), fmt::format("{0:#x}", ret)); + ani_notify(NOTIFI::ERROR, getLangSTR(LANG_STR::EJECT_DISK_ERROR), fmt::format("{0:#x}", ret)); else - ani_notify(NOTIFI_SUCCESS, getLangSTR(EJECT_DISK_SUCCESS), ""); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::EJECT_DISK_SUCCESS), ""); #endif inserted_disc = -1; break; } case TRI: { - item_page_Triangle_action(active_p); + item_page_Triangle_action(ls_p); /* app_status = OTHER_APP_OPEN; // set the app status to no app opened - ani_notify(NOTIFI_WARNING, getLangSTR(OPT_NOT_PERMITTED), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OPT_NOT_PERMITTED), ""); */ break; // goto back_to_Store; } @@ -2369,7 +2360,7 @@ void fw_action_to_cf(int button) case 0x1337: { log_info("called 0x1337"); - v2 = set_view(ITEMzFLOW); + v2 = set_view_main(ITEMzFLOW); break; } } @@ -2379,47 +2370,42 @@ void fw_action_to_cf(int button) switch (button) { // movement case LEF: - layout_update_sele(active_p, -1); + layout_update_sele(setting_p, -1); break; // l_or_r case RIG: - layout_update_sele(active_p, +1); + layout_update_sele(setting_p, +1); break; case UP: - layout_update_sele(active_p, -active_p->fieldsize.x); + layout_update_sele(setting_p, -setting_p.fieldsize.x); break; case DOW: - layout_update_sele(active_p, +active_p->fieldsize.x); + layout_update_sele(setting_p, +setting_p.fieldsize.x); break; // case OPT: case CRO: - X_action_settings(0, active_p); + X_action_settings(0, setting_p); break; // in_out case R1: { if (numb_of_settings == NUMBER_OF_SETTINGS) { numb_of_settings += 6; // add 6 to the number of settings to show the extra settings - ITEMZ_SETTING_TEXT[11] = fmt::format("{0:.30}", getLangSTR(EXTRA_SETTING_6)); - ITEMZ_SETTING_TEXT[17] = fmt::format("{0:.30}", getLangSTR(SETTINGS_12)); + ITEMZ_SETTING_TEXT[11] = fmt::format("{0:.30}", getLangSTR(LANG_STR::EXTRA_SETTING_6)); + ITEMZ_SETTING_TEXT[17] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_12)); } else { numb_of_settings = NUMBER_OF_SETTINGS; // remove 6 to the number of settings to hide the extra settings - ITEMZ_SETTING_TEXT[11] = fmt::format("{0:.30}", getLangSTR(SETTINGS_12)); + ITEMZ_SETTING_TEXT[11] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_12)); } - /* DELETE SETTINGS VIEW */ - if(active_p->vbo) - vertex_buffer_delete(active_p->vbo), active_p->vbo = NULL; + /* DELETE SETTINGS view_main */ + setting_p.vbo.clear(); + setting_p.f_rect.clear(); + title_vbo.clear(); - if (active_p->f_rect) - free(active_p->f_rect), active_p->f_rect = NULL; - - if(title_vbo) - vertex_buffer_delete(title_vbo), title_vbo = NULL; - - active_p->is_active = false; - active_p->item_d.clear(); + setting_p.is_active = false; + setting_p.item_d.clear(); break; } @@ -2427,15 +2413,15 @@ void fw_action_to_cf(int button) //eject disc if(inserted_disc.load() > 0){ #if __ORBIS__ - if(Confirmation_Msg(getLangSTR(EJECT_DISK)) == NO){ - ani_notify(NOTIFI_WARNING, getLangSTR(OP_CANNED), ""); + if(Confirmation_Msg(getLangSTR(LANG_STR::EJECT_DISK)) == NO){ + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OP_CANNED), ""); break; } if((ret = sceShellCoreUtilRequestEjectDevice("/dev/cd0")) < 0) - ani_notify(NOTIFI_ERROR, getLangSTR(EJECT_DISK_ERROR), fmt::format("{0:#x}", ret)); + ani_notify(NOTIFI::ERROR, getLangSTR(LANG_STR::EJECT_DISK_ERROR), fmt::format("{0:#x}", ret)); else - ani_notify(NOTIFI_SUCCESS, getLangSTR(EJECT_DISK_SUCCESS), ""); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::EJECT_DISK_SUCCESS), ""); #endif inserted_disc = -1; } @@ -2446,25 +2432,26 @@ void fw_action_to_cf(int button) case CIR: { back_05905: - vertex_buffer_delete(setting_p.vbo), setting_p.vbo = NULL; + setting_p.vbo.clear(); setting_p.is_active = false; - O_action_settings(active_p); + O_action_settings(setting_p); break; } case SQU: { - if(active_p->curr_item == BACKGROUND_MP3_OPTION){ + if(setting_p.curr_item == BACKGROUND_MP3_OPTION){ log_info("Disabling Background MP3 ..."); #ifdef __ORBIS__ Stop_Music(); #endif - if(setting_p.vbo) vertex_buffer_delete(setting_p.vbo), setting_p.vbo = NULL; + setting_p.vbo.clear(); } else if(numb_of_settings > NUMBER_OF_SETTINGS) { log_debug("Killing daemon ..."); #ifdef __ORBIS__ if(is_connected_app){ - IPCSendCommand(CMD_SHUTDOWN_DAEMON, (uint8_t*)&ipc_msg[0]); + + ipc.IPCSendCommand(IPC_Commands::CMD_SHUTDOWN_DAEMON, ipc_msg); is_connected_app = false; } #endif @@ -2474,17 +2461,17 @@ back_05905: case TRI: { - ani_notify(NOTIFI_WARNING, getLangSTR(OPT_NOT_PERMITTED), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OPT_NOT_PERMITTED), ""); break; // goto back_to_Store; } } } } -void load_button(const char* file_name, GLuint *tex){ +void load_button(const char* file_name, GLuint &tex){ std::string file_path; - if (*tex > GL_NULL) + if (tex > GL_NULL) return; if (get->setting_bools[using_theme]) @@ -2492,29 +2479,33 @@ void load_button(const char* file_name, GLuint *tex){ else file_path = fmt::format("{0:}{1:}", asset_path("buttons/"), file_name); - if (*tex == GL_NULL){ - log_debug("tex: %i", *tex); - *tex = load_png_asset_into_texture(file_path.c_str()); - log_debug("load_button: %s | tex: %i", file_path.c_str(), *tex); - } + log_debug("tex: %i", tex); + tex = load_png_asset_into_texture(file_path.c_str()); + log_debug("load_button: %s | tex: %i", file_path.c_str(), tex); + } extern bool file_options; - +//bool fav_list[8] = {false, false, false, false, false, false, false, false}; +int center_cover = 0; void draw_additions(view_t vt) { - load_button("btn_x.png", &btn_X); - load_button("btn_o.png", &btn_o); - load_button("btn_tri.png", &btn_tri); - load_button("btn_options.png", &btn_options); - load_button("btn_down.png", &btn_down); - load_button("btn_up.png", &btn_up); - load_button("btn_left.png", &btn_left); - load_button("btn_right.png", &btn_right); - load_button("btn_sq.png", &btn_sq); - load_button("btn_l1.png", &btn_l1); - load_button("btn_r1.png", &btn_r1); - load_button("btn_l2.png", &btn_l2); + load_button("btn_x.png", btn_X); + load_button("btn_o.png", btn_o); + load_button("../heart.png", heart_icon); + load_button("btn_tri.png", btn_tri); + load_button("btn_options.png", btn_options); + load_button("btn_down.png", btn_down); + load_button("btn_up.png", btn_up); + load_button("btn_left.png", btn_left); + load_button("btn_right.png", btn_right); + load_button("btn_sq.png", btn_sq); + load_button("btn_l1.png", btn_l1); + load_button("btn_r1.png", btn_r1); + load_button("btn_l2.png", btn_l2); + load_button("../debug_item_icon.png", debug_icon); + bool is_file_manager = (v_curr == FILE_BROWSER_LEFT || v_curr == FILE_BROWSER_RIGHT); + if (get->setting_bools[Show_Buttons]) { @@ -2522,6 +2513,41 @@ void draw_additions(view_t vt) { if (btn_X && btn_options && btn_tri && btn_o) { +#if 0 +int o = 0; +int max_count = std::max(g_idx + 3, 7); +std::array fav_list{false}; +if(max_count == 7) + max_count = 8; +for (int i = (max_count-7 == 0) ? 1 : max_count - 7; i <= max_count; ++i) { + if (i >= all_apps.size()) { + for (int j = 1; j < 3; ++j) { + log_info("o %i j: %i as: %i", o, j, all_apps.size()); + fav_list[o] = all_apps[j].flags.is_favorite; + log_debug("fav_list[%i] = %i %i (%i)", o, fav_list[o], all_apps[j].flags.is_favorite, j); + if(o == 7) { + break; + } + o++; + } + break; + } + log_info("i %i all_apps size: %i", i, all_apps.size()); + fav_list[o] = all_apps[i].flags.is_favorite; + if (i == g_idx) center_cover = o; + log_debug("fav_list[%i] = %i %i (%i) %s", o, fav_list[o], all_apps[i].flags.is_favorite, i, (i == g_idx) ? "< = CENTER" : ""); + if(o == 7) break; + o++; +} + +if (heart_icon && !ani[0].is_running) { + for (const auto& pos : heart_icon_positions) { + if (center_cover + pos.offset <= fav_list.size() && fav_list[center_cover + pos.offset]) { + render_button(heart_icon, 64, 64, pos.x, pos.y, 1.1); + } + } +} +#endif vec4 r; vec2 s = (vec2)(68), @@ -2531,10 +2557,10 @@ void draw_additions(view_t vt) // convert to normalized coordinates r.xy = px_pos_to_normalized(&p); r.zw = px_pos_to_normalized(&s); - // char *p = strstr( l->item_d[i].picpath.c_str(), "storedata" ); + // char *p = strstr( l.item_d[i].info.picpath.c_str(), "storedata" ); // draw texture, use passed frect - if(all_apps[0].token_c > 0) // if no apps dont draw x - on_GLES2_Render_icon(USE_COLOR, btn_X, 2, &r, NULL); + if(all_apps[0].count.token_c > 0) // if no apps dont draw x + on_GLES2_Render_icon(USE_COLOR, btn_X, 2, r, NULL); render_button(btn_options, 84., 145., 1380, 40., 1.5); render_button(btn_tri, 67., 68., 1380, 90., 1.1); @@ -2561,20 +2587,18 @@ void draw_additions(view_t vt) if (vt == ITEM_SETTINGS){ render_button(btn_r1, 30., 68., 500., 20., 1); - if(as_text) - vertex_buffer_delete(as_text), as_text = NULL; - if(remove_music) - vertex_buffer_delete(remove_music), remove_music = NULL; + as_text.clear(); + remove_music.clear(); - as_text = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); + as_text = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); if (setting_p.curr_item == BACKGROUND_MP3_OPTION && !get->setting_strings[MP3_PATH].empty()) { - remove_music = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); + remove_music = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); render_button(btn_sq, 67., 68., 500., 55., 1.1); - itemzcore_add_text(remove_music, 560., 80., getLangSTR(DISABLE_MUSIC)); + itemzcore_add_text(remove_music, 560., 80., getLangSTR(LANG_STR::DISABLE_MUSIC)); } #ifdef __ORBIS__ @@ -2582,63 +2606,67 @@ void draw_additions(view_t vt) #else else if (numb_of_settings != NUMBER_OF_SETTINGS){ #endif - remove_music = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); + remove_music = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); render_button(btn_sq, 67., 68., 500., 55., 1.1); - itemzcore_add_text(remove_music, 560., 80., getLangSTR(SHUTDOWN_DAEMON)); + itemzcore_add_text(remove_music, 560., 80., getLangSTR(LANG_STR::SHUTDOWN_DAEMON)); + remove_music.render_vbo(NULL); } - itemzcore_add_text(as_text, 580., 25., (numb_of_settings == NUMBER_OF_SETTINGS) ? getLangSTR(ADVANCED_SETTINGS) : getLangSTR(HIDE_ADVANCED_SETTINGS)); + itemzcore_add_text(as_text, 580., 25., (numb_of_settings == NUMBER_OF_SETTINGS) ? getLangSTR(LANG_STR::ADVANCED_SETTINGS) : getLangSTR(LANG_STR::HIDE_ADVANCED_SETTINGS)); - if (as_text) - ftgl_render_vbo(as_text, NULL); - if (remove_music) - ftgl_render_vbo(remove_music, NULL); + as_text.render_vbo(NULL); } if (vt == ITEM_PAGE) { // && is_trainer_page == true + render_button(btn_sq, 67., 68., 525., 0., 1.1); + if(ls_p.curr_item != cf_ops::REG_OPTS::TRAINERS){ - if (ls_p.curr_item == Trainers_opt) - { - - render_button(btn_sq, 67., 68., 525., 0., 1.1); - render_button(btn_tri, 67., 68., 525., 45., 1.1); - if(!trainer_btns){ - trainer_btns = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); - itemzcore_add_text(trainer_btns,585., 65., getLangSTR(BUTTON_PATCH_TRI_INFO)); - itemzcore_add_text(trainer_btns,585., 20., getLangSTR(BUTTON_PATCH_SQUARE_INFO)); + if(!favs){ + favs = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); + itemzcore_add_text(favs,585., 20., all_apps[g_idx].flags.is_favorite ? getLangSTR(LANG_STR::FAVORITES_REMOVE) : getLangSTR(LANG_STR::FAVORITES_DESC));// } - if(trainer_btns) - ftgl_render_vbo(trainer_btns, NULL); + favs.render_vbo(NULL); + } - else if (ls_p.curr_item == Launch_Game_opt) + else if (ls_p.curr_item == cf_ops::REG_OPTS::LAUNCH_GAME) { //started_epoch if(started_epoch.load() > 0){ - if (launch_text) vertex_buffer_delete(launch_text), launch_text = NULL; - launch_text = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); + launch_text.clear(); + launch_text = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); if(launch_text){ char buff[21]; const time_t t = started_epoch.load(); strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&t)); //log_info("started_epoch: %ld", (long)t); - itemzcore_add_text(launch_text, 660., 45., fmt::format("{} - {:.19}", getLangSTR(PLAYING_SINCE), buff)); - ftgl_render_vbo(launch_text, NULL); + itemzcore_add_text(launch_text, 660., 45., fmt::format("{} - {:.19}", getLangSTR(LANG_STR::PLAYING_SINCE), buff)); + launch_text.render_vbo(NULL); } } } + else + { + render_button(btn_tri, 67., 68., 525., 45., 1.1); + if(!trainer_btns){ + trainer_btns = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); + itemzcore_add_text(trainer_btns,585., 65., getLangSTR(LANG_STR::BUTTON_PATCH_TRI_INFO)); + itemzcore_add_text(trainer_btns,585., 20., getLangSTR(LANG_STR::BUTTON_PATCH_SQUARE_INFO)); + } + // ftgl_render_vbo(trainer_btns, NULL); + trainer_btns.render_vbo(NULL); + } } if (inserted_disc.load() > 0) render_button(btn_options, 84., 145., 25., 90., 1.5); } } - else if (vt == FILE_BROWSER) + else if (is_file_manager) { - if (title_vbo) - vertex_buffer_delete(title_vbo), title_vbo = NULL; + title_vbo.clear(); - title_vbo = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); + title_vbo = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); if (btn_X && btn_up && btn_down && btn_left && btn_right && btn_o && btn_tri) @@ -2663,38 +2691,53 @@ void draw_additions(view_t vt) // 6=New Directory // 7=Copy/Paste - itemzcore_add_text(title_vbo,80., 110., getLangSTR(FS_CONTROL_BUTTON_1)); - itemzcore_add_text(title_vbo,80., 70., getLangSTR(FS_CONTROL_BUTTON_2)); - itemzcore_add_text(title_vbo,80., 155., getLangSTR(FS_CONTROL_BUTTON_3)); + itemzcore_add_text(title_vbo,80., 110., getLangSTR(LANG_STR::FS_CONTROL_BUTTON_1)); + itemzcore_add_text(title_vbo,80., 70., getLangSTR(LANG_STR::FS_CONTROL_BUTTON_2)); + itemzcore_add_text(title_vbo,80., 155., getLangSTR(LANG_STR::FS_CONTROL_BUTTON_3)); if (fs_ret.filter != FS_NONE){ - itemzcore_add_text(title_vbo, 365., 125., (fs_ret.filter == FS_FOLDER) ? getLangSTR(FS_CONTROL_BUTTON_4_1) : getLangSTR(FS_CONTROL_BUTTON_4)); - itemzcore_add_text(title_vbo, 1550., 50., (fs_ret.filter == FS_FOLDER) ? getLangSTR(SELECT_A_FOLDER) : getLangSTR(SELECT_A_FILE)); - itemzcore_add_text(title_vbo, 620., 120., getLangSTR(FS_CONTROL_BUTTON_6)); + itemzcore_add_text(title_vbo, 365., 125., (fs_ret.filter == FS_FOLDER) ? getLangSTR(LANG_STR::FS_CONTROL_BUTTON_4_1) : getLangSTR(LANG_STR::FS_CONTROL_BUTTON_4)); + itemzcore_add_text(title_vbo, 1550., 50., (fs_ret.filter == FS_FOLDER) ? getLangSTR(LANG_STR::SELECT_A_FOLDER) : getLangSTR(LANG_STR::SELECT_A_FILE)); + itemzcore_add_text(title_vbo, 620., 120., getLangSTR(LANG_STR::FS_CONTROL_BUTTON_6)); } else if (fs_ret.filter == FS_NONE && !file_options){ itemzcore_add_text(title_vbo, 365., 125., "File Options"); - itemzcore_add_text(title_vbo, 620., 120., getLangSTR(FS_CONTROL_BUTTON_6)); + itemzcore_add_text(title_vbo, 620., 120., getLangSTR(LANG_STR::FS_CONTROL_BUTTON_6)); } else if (fs_ret.filter == FS_NONE && file_options){ itemzcore_add_text(title_vbo, 365., 125., "Select cunt"); - //itemzcore_add_text(title_vbo, 1550., 50., (fs_ret.filter == FS_FOLDER) ? getLangSTR(SELECT_A_FOLDER) : getLangSTR(SELECT_A_FILE)); + //itemzcore_add_text(title_vbo, 1550., 50., (fs_ret.filter == FS_FOLDER) ? getLangSTR(LANG_STR::SELECT_A_FOLDER) : getLangSTR(LANG_STR::SELECT_A_FILE)); itemzcore_add_text(title_vbo, 620., 120., "Select cunt2"); } - itemzcore_add_text(title_vbo,370., 65., getLangSTR(BUTTON_UP_DOWN_INFO)); - itemzcore_add_text(title_vbo,620., 65., getLangSTR(FS_CONTROL_BUTTON_5)); - //itemzcore_add_text(title_vbo,620., 120., getLangSTR(FS_CONTROL_BUTTON_6)); + itemzcore_add_text(title_vbo,370., 65., getLangSTR(LANG_STR::BUTTON_UP_DOWN_INFO)); + itemzcore_add_text(title_vbo,620., 65., getLangSTR(LANG_STR::FS_CONTROL_BUTTON_5)); + //itemzcore_add_text(title_vbo,620., 120., getLangSTR(LANG_STR::FS_CONTROL_BUTTON_6)); - ftgl_render_vbo(title_vbo, NULL); + title_vbo.render_vbo(NULL); } } } +#if 0 +static void LoadIconsAsync(int i){ + uint64_t fmem = 0; + + check_tex_for_reload(i); + #if defined(__ORBIS__) + sceKernelAvailableFlexibleMemorySize(&fmem); + // calc the progress % + //fill up the available VRAM with PNGs (to reduce loading times) but keep enough to do other things + if(fmem < MB(5)) + return; + #endif + + check_n_load_textures(i); +} +#endif + void DrawScene_4(void) { - - layout_t *l; /* animation alpha fading */ vec4 colo = (1.), ani_c; /* animation vectors */ @@ -2705,14 +2748,13 @@ void DrawScene_4(void) if(mins_played > 0 || mins_played == MIN_STATUS_NA || mins_played == MIN_STATUS_VAPP){ - if(title_vbo) - vertex_buffer_delete(title_vbo), title_vbo = NULL; + title_vbo.clear(); } /* animation l/r */ const vec3 offset = (vec3){1.05, 0., 0.}; - /* delta view movement vector */ - vec3 view_v = v2 - v1; + /* delta view_main movement vector */ + vec3 view_main_v = v2 - v1; if (ani[0].is_running) { // update time @@ -2725,22 +2767,22 @@ void DrawScene_4(void) ani_ratio = 0; /* switch on (ani type) but with pointers: l/R op_on g_idx - CRO op_on view_v */ + CRO op_on view_main_v */ if (ani[0].handle == &g_idx) { // ani ended, move global index by sign! g_idx += (xoff > 0) ? 1 : -1; // L:-1, R:+1 // bounds checking on item count - pthread_mutex_lock(&disc_lock); + { + std::lock_guard lock(disc_lock); + if (g_idx < 1) + g_idx = all_apps[0].count.token_c; + if (g_idx > all_apps[0].count.token_c) + g_idx = 1; + } - if (g_idx < 1) - g_idx = all_apps[0].token_c; - if (g_idx > all_apps[0].token_c) - g_idx = 1; - - pthread_mutex_unlock(&disc_lock); } if (ani[0].handle == &v_curr) - { // ani ended: now finally switch the view + { // ani ended: now finally switch the view_main v1 = v2; v_curr = up_next; @@ -2750,12 +2792,11 @@ void DrawScene_4(void) ani[0].is_running = false; xoff = 0.; // ani_flag, pm_flag - // refresh title - if (title_vbo) - vertex_buffer_delete(title_vbo), title_vbo = NULL; + // refresh all_apps[g_idx].info.name + title_vbo.clear(); } // the (delta!) fraction - view_v *= ani_ratio; + view_main_v *= ani_ratio; /* consider ani vector, get a fraction from offset and get a fraction of degree, for a single step */ @@ -2767,7 +2808,7 @@ void DrawScene_4(void) #if 0 if (ani) log_info("ani_now:%f (%3.1f%%), %.3f (%.3f, %.3f, %.3f)", ani[0].now, ani_ratio * 100., ani_ratio * xoff, - view_v.x, view_v.y, view_v.z); + view_main_v.x, view_main_v.y, view_main_v.z); #endif } else @@ -2775,8 +2816,8 @@ void DrawScene_4(void) // update time g_Time = u_t; - // add start point to view vector - view_v += v1; + // add start point to view_main vector + view_main_v += v1; switch (v_curr) // { @@ -2797,18 +2838,18 @@ void DrawScene_4(void) /* background image, or pixelshader */ if (bg_tex == GL_NULL && !get->setting_bools[has_image]) - pixelshader_render(2, NULL, NULL); // use PS_symbols shader + pixelshader_render(); // use PS_symbols shader else { /* bg image: fullscreen frect (normalized coordinates) */ vec4 r = (vec4){-1., -1., 1., 1.}; // see if its even loaded if (bg_tex > GL_NULL) - on_GLES2_Render_icon(USE_COLOR, bg_tex, 2, &r, NULL); + on_GLES2_Render_icon(USE_COLOR, bg_tex, 2, r, NULL); } int i, j, c = 0; /* loop outer to inner, care overlap */ - for (i = (all_apps[0].token_c > 1) ? pm_r : 0; i > 0; i--) // ...3, 2, 1 + for (i = (all_apps[0].count.token_c > 1) ? pm_r : 0; i > 0; i--) // ...3, 2, 1 { j = i; @@ -2861,30 +2902,30 @@ void DrawScene_4(void) glDisable(GL_CULL_FACE); { // order matters! - mat4_set_identity(&model); - mat4_translate(&model, 0, 0, 0); + mat4_set_identity(&model_main); + mat4_translate(&model_main, 0, 0, 0); // move to "floor" plane - mat4_translate(&model, 0, 1.25 / 2., 0); + mat4_translate(&model_main, 0, 1.25 / 2., 0); // rotations - mat4_rotate(&model, rot.x, 1, 0, 0); - mat4_rotate(&model, rot.y, 0, 1, 0); - mat4_rotate(&model, rot.z, 0, 0, 1); + mat4_rotate(&model_main, rot.x, 1, 0, 0); + mat4_rotate(&model_main, rot.y, 0, 1, 0); + mat4_rotate(&model_main, rot.z, 0, 0, 1); // set -DEFAULT_Z_CAMERA - mat4_translate(&model, pos.x, pos.y, pos.z); - if (1) // ani_view + mat4_translate(&model_main, pos.x, pos.y, pos.z); + if (1) // ani_view_main { - mat4_set_identity(&view); - mat4_translate(&view, view_v.x, view_v.y, view_v.z); + mat4_set_identity(&view_main); + mat4_translate(&view_main, view_main_v.x, view_main_v.y, view_main_v.z); } /* don't USE */ // glEnable( GL_DEPTH_TEST ); { - glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "model"), 1, 0, model.data); - glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "view"), 1, 0, view.data); - glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "projection"), 1, 0, projection.data); + glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "model"), 1, 0, model_main.data); + glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "view"), 1, 0, view_main.data); + glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "projection"), 1, 0, projection_main.data); glUniform4f(glGetUniformLocation(sh_prog1, "Color"), colo.r, colo.g, colo.b, colo.a); } - vertex_buffer_render(cover_o, GL_TRIANGLES); + cover_o.render(GL_TRIANGLES); } glUseProgram(0); // @@ -2892,25 +2933,23 @@ void DrawScene_4(void) int tex_idx = g_idx + j; //log_info("tex_idx: %d j: %d", tex_idx, j); - pthread_mutex_lock(&disc_lock); // check bounds: skip first if (tex_idx < 1) - tex_idx += all_apps[0].token_c; - else if (tex_idx > all_apps[0].token_c) - tex_idx -= all_apps[0].token_c; + tex_idx += all_apps[0].count.token_c; + else if (tex_idx > all_apps[0].count.token_c) + tex_idx -= all_apps[0].count.token_c; if (Download_icons) { #if defined(__ORBIS__) - if (0)//get->setting_bools[cover_message]) - { - if (Confirmation_Msg(getLangSTR(DOWNLOAD_COVERS)) == YES) + + if (get->setting_bools[cover_message] && Confirmation_Msg(getLangSTR(LANG_STR::DOWNLOAD_COVERS)) == YES) { - loadmsg(getLangSTR(DOWNLOADING_COVERS)); + loadmsg(getLangSTR(LANG_STR::DOWNLOADING_COVERS)); log_info("Downloading covers..."); - for (int i = 1; i < all_apps[0].token_c + 1; i++) + for (int i = 1; i < all_apps[0].count.token_c + 1; i++) download_texture(i); sceMsgDialogTerminate(); @@ -2918,120 +2957,132 @@ void DrawScene_4(void) } else log_info("Download covers canceled"); - } -#else - if (0) - { - } + #endif - else - { + #if 0 + Timer timer; + std::vector> m_future; + int i = 0; + #if defined(__ORBIS__) + progstart("Pre-loading Covers..."); + #endif + for (item_t &item : all_apps) + { + // m_future.push_back(std::async(std::launch::async, LoadIconsAsync, i)); + LoadIconsAsync(i); + check_n_draw_textures(tex_idx, +1, colo); - for (int i = 1; i < all_apps[0].token_c + 1; i++) - check_tex_for_reload(i); - } + if(item.info.id.empty()) + log_error("wtf"); + #if defined(__ORBIS__) + ProgressUpdate(((i * 100) / all_apps.size()), "Pre-loading Covers..."); + #endif + i++; + } + sceMsgDialogTerminate(); + #endif - log_info("all_apps[0].token_c: %i", all_apps[0].token_c); + log_info("all_apps[0].count.token_c: %i", all_apps[0].count.token_c); Download_icons = false; } - + + sceMsgDialogTerminate(); // rdisc_i - mat4 rdisc_model; + mat4 rdisc_model_main; // load textures (once) check_n_load_textures(tex_idx); // draw coverbox, if not avail craft coverbox from icon check_n_draw_textures(tex_idx, +1, colo); - pthread_mutex_unlock(&disc_lock); + // log_info("000000000000"); + //log_info("tex_idx: %d, inserted_disc.load() %i", tex_idx, inserted_disc.load()); if (tex_idx == inserted_disc.load()) // single diff item { rdisc_rz += 1.; // rotation on Z // save for restore later - mat4 model_b = model; - mat4_set_identity(&model); - mat4_translate(&model, 0, 0, 0); + mat4 model_main_b = model_main; + mat4_set_identity(&model_main); + mat4_translate(&model_main, 0, 0, 0); // circle shadow does not rotates(?) - mat4_rotate(&model, rdisc_rz, 0, 0, 1); + mat4_rotate(&model_main, rdisc_rz, 0, 0, 1); // move to "floor" plane - mat4_translate(&model, 0, 1. /2., 0); + mat4_translate(&model_main, 0, 1. /2., 0); // move to final pos - mat4_translate(&model, pos.x, pos.y, pos.z /* a little out from cover */ + .025f); + mat4_translate(&model_main, pos.x, pos.y, pos.z /* a little out from cover */ + .025f); // draw a fake shadow vec4 sh_col = vec4 {0., 0., 0., .75}; - // we need a new fragment shader / review the fragment shaders sources to clean the mess + // we need a new fragment shader / review_main the fragment shaders sources to clean the mess // actually texture is drawn plain, color does not change nothing - render_tex(rdisc_tex, /* rdisc_texture */ -2, rdisc_i, sh_col); + rdisc_i.render_tex(rdisc_tex, /* rdisc_texture */ -2, sh_col); // re-position - mat4_set_identity(&model); - mat4_translate(&model, 0, 0, 0); - mat4_rotate(&model, rdisc_rz, 0, 0, 1); + mat4_set_identity(&model_main); + mat4_translate(&model_main, 0, 0, 0); + mat4_rotate(&model_main, rdisc_rz, 0, 0, 1); // move to "floor" plane - mat4_translate(&model, 0, 1. /2., 0); + mat4_translate(&model_main, 0, 1. /2., 0); // move to final pos - mat4_translate(&model, pos.x, pos.y, pos.z /* a little out from cover */ + .050f); + mat4_translate(&model_main, pos.x, pos.y, pos.z /* a little out from cover */ + .050f); // draw - render_tex(rdisc_tex, /* rdisc_texture */ +1, rdisc_i, colo); + rdisc_i.render_tex(rdisc_tex, /* rdisc_texture */ +1, colo); if (1) { /* reflect on the Y, actual position matters! */ - mat4_scale(&model, 1, -1, 1); + mat4_scale(&model_main, 1, -1, 1); // fill blue sh_col = vec4 {0., 0., 1., .9 * colo.a}; // rdisc_i - rdisc_model = model; + rdisc_model_main = model_main; // render_tex(rdisc_tex, /* rdisc_texture */ -2, rdisc_i, sh_col); } // restore from saved - model = model_b; - // glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "model"), 1, 0, model.data); + model_main = model_main_b; + // glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "model_main"), 1, 0, model_main.data); } + // log_info("000000000000"); // reflection option if (use_reflection) { /* reflect on the Y, actual position matters! */ - mat4_scale(&model, 1, -1, 1); - - pthread_mutex_lock(&disc_lock); + mat4_scale(&model_main, 1, -1, 1); // draw objects glUseProgram(sh_prog1); - glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "model"), 1, 0, model.data); + glUniformMatrix4fv(glGetUniformLocation(sh_prog1, "model"), 1, 0, model_main.data); glUniform4f(glGetUniformLocation(sh_prog1, "Color"), .3, .3, .3, .1 * colo.a); - vertex_buffer_render(cover_o, GL_TRIANGLES); + cover_o.render(GL_TRIANGLES); + glUseProgram(0); // draw coverbox reflection, if not avail craft coverbox from icon check_n_draw_textures(tex_idx, -1, colo); - - pthread_mutex_unlock(&disc_lock); - if (tex_idx == inserted_disc.load()) { // fill blue, (shader 1 don't use) vec4 sh_col = vec4 {1., 1., .8, .7 * colo.a}; // rdisc_i - model = rdisc_model; - render_tex(rdisc_tex, /* rdisc_texture */ -3, rdisc_i, sh_col); + model_main = rdisc_model_main; + rdisc_i.render_tex(rdisc_tex, /* rdisc_texture */ -3, sh_col); } } - /* set title */ + /* set all_apps[g_idx].info.name */ + // log_info("000000000000"); if (!j // last drawn one, the selected in the center && !title_vbo) // we cleaned VBO { std::string tmpstr; - // vec2 pen = (vec2){ (float)l->bound_box.x - 20., resolution.y - 140. }; + // vec2 pen = (vec2){ (float)l.bound_box.x - 20., resolution.y - 140. }; // - item_t *li = &all_apps[tex_idx]; + item_t &li = all_apps[tex_idx]; // log_info("tex_idx: %i", tex_idx); // - title_vbo = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); + title_vbo = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); // NAME, is size checked - tmpstr = fmt::format("{0:.25}{1:} {2:}", li->name, (li->name.length() > 25) ? "..." : "", li->is_ext_hdd ? "(Ext. HDD)" : ""); + tmpstr = fmt::format("{0:.25}{1:} {2:}", li.info.name, (li.info.name.length() > 25) ? "..." : "", li.flags.is_ext_hdd ? "(Ext. HDD)" : ""); if (v_curr == ITEM_SETTINGS) - tmpstr = getLangSTR(ITEMZ_SETTINGS); + tmpstr = getLangSTR(LANG_STR::ITEMZ_SETTINGS); // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs(titl_font, tmpstr.c_str()); @@ -3052,19 +3103,19 @@ void DrawScene_4(void) if (v_curr == ITEM_PAGE){ pen_game_options.y += 25.; - add_text(title_vbo, titl_font, tmpstr, &col, &pen_game_options); + title_vbo.add_text(titl_font, tmpstr, col, pen_game_options); } else if (v_curr == ITEMzFLOW) // fill the vbo - add_text(title_vbo, titl_font, tmpstr, &col, &pen); + title_vbo.add_text(titl_font, tmpstr, col, pen); else if (v_curr == ITEM_SETTINGS) - add_text(title_vbo, titl_font, tmpstr, &col, &setting_pen); + title_vbo.add_text(titl_font, tmpstr, col, setting_pen); // log_info("%i",__LINE__); - +// log_info("000000000000"); // // ID // we need to know Text_Length_in_px in advance, so we call this: - texture_font_load_glyphs(sub_font, li->id.c_str()); + texture_font_load_glyphs(sub_font, li.info.id.c_str()); // align text centered // log_info("%i",__LINE__); @@ -3079,49 +3130,49 @@ void DrawScene_4(void) { if (get->setting_bools[Show_Buttons]) { - //itemzcore_add_text(title_vbo,80., 115., getLangSTR(BUTTON_X_INFO)); - itemzcore_add_text(title_vbo,80., 20., getLangSTR(BUTTON_X_INFO)); - itemzcore_add_text(title_vbo,80., 65., getLangSTR(BUTTON_O_INFO)); + //itemzcore_add_text(title_vbo,80., 115., getLangSTR(LANG_STR::BUTTON_X_INFO)); + itemzcore_add_text(title_vbo,80., 20., getLangSTR(LANG_STR::BUTTON_X_INFO)); + itemzcore_add_text(title_vbo,80., 65., getLangSTR(LANG_STR::BUTTON_O_INFO)); // if (is_trainer_page) - itemzcore_add_text(title_vbo,390., 100., getLangSTR(BUTTON_LEFT_RIGHT_INFO)); - itemzcore_add_text(title_vbo,370., 42., getLangSTR(BUTTON_UP_DOWN_INFO)); + itemzcore_add_text(title_vbo,390., 100., getLangSTR(LANG_STR::BUTTON_LEFT_RIGHT_INFO)); + itemzcore_add_text(title_vbo,370., 42., getLangSTR(LANG_STR::BUTTON_UP_DOWN_INFO)); - itemzcore_add_text(title_vbo,inserted_disc.load() > 0 ? 115 : 40., 110., inserted_disc.load() > 0 ? getLangSTR(EJECT_DISK) : getLangSTR(NO_DISC_INSERTED) ); + itemzcore_add_text(title_vbo,inserted_disc.load() > 0 ? 115 : 40., 110., inserted_disc.load() > 0 ? getLangSTR(LANG_STR::EJECT_DISK) : getLangSTR(LANG_STR::NO_DISC_INSERTED) ); } pen_game_options.x += 10.; - if(li->is_vapp) - tmpstr = fmt::format("{0:} ({1:}) (VAPP)", li->id, li->version); + if(li.flags.is_vapp) + tmpstr = fmt::format("{0:} ({1:}) (VAPP)", li.info.id, li.info.version); else - tmpstr = fmt::format("{0:} {2:} ({1:})", li->id, li->is_fpkg ? "FPKG" : getLangSTR(RETAIL), li->version); + tmpstr = fmt::format("{0:} {2:} ({1:})", li.info.id, li.flags.is_fpkg ? "FPKG" : getLangSTR(LANG_STR::RETAIL), li.info.version); // fill the vbo - add_text(title_vbo, sub_font, tmpstr, &c, &pen_game_options); + title_vbo.add_text(sub_font, tmpstr, c, pen_game_options); fmt::print("mins_played: {}", mins_played.load()); - tmpstr = getLangSTR(FETCH_STATS); + tmpstr = getLangSTR(LANG_STR::FETCH_STATS); if(mins_played.load() > 0){ if(mins_played.load() < 59){ - tmpstr = fmt::format("{0:d} {1:}", mins_played.load(), getLangSTR(STATS_MINS)); + tmpstr = fmt::format("{0:d} {1:}", mins_played.load(), getLangSTR(LANG_STR::STATS_MINS)); } else if(mins_played < 1440){ - tmpstr = fmt::format("{0:d} {1:} {2:d} {3:}", mins_played.load()/60, getLangSTR(STATS_HOURS), mins_played.load()%60, getLangSTR(STATS_MINS)); + tmpstr = fmt::format("{0:d} {1:} {2:d} {3:}", mins_played.load()/60, getLangSTR(LANG_STR::STATS_HOURS), mins_played.load()%60, getLangSTR(LANG_STR::STATS_MINS)); } else if(mins_played >= 1440){ - tmpstr = fmt::format("{0:d} {1:} {2:d} {3:} {4:d} {5:}", mins_played.load()/1440, getLangSTR(STATS_DAYS), mins_played.load()%1440/60, getLangSTR(STATS_HOURS), mins_played.load()%1440%60, getLangSTR(STATS_MINS)); + tmpstr = fmt::format("{0:d} {1:} {2:d} {3:} {4:d} {5:}", mins_played.load()/1440, getLangSTR(LANG_STR::STATS_DAYS), mins_played.load()%1440/60, getLangSTR(LANG_STR::STATS_HOURS), mins_played.load()%1440%60, getLangSTR(LANG_STR::STATS_MINS)); } //fmt::print("cpp msg: {}", tmpstr); mins_played = MIN_STATUS_SEEN; }// else if(mins_played == MIN_STATUS_NA){ - tmpstr = getLangSTR(NO_STATS_AVAIL); + tmpstr = getLangSTR(LANG_STR::NO_STATS_AVAIL); mins_played = MIN_STATUS_SEEN; } else if(mins_played == MIN_STATUS_VAPP){ - tmpstr = getLangSTR(VAPP_MENU); + tmpstr = getLangSTR(LANG_STR::VAPP_MENU); mins_played = MIN_STATUS_SEEN; } itemzcore_add_text(title_vbo,100., 920., tmpstr); @@ -3129,45 +3180,46 @@ void DrawScene_4(void) else if (v_curr == ITEMzFLOW) { // fill the vbo{ // 1692 y 61 - add_text(title_vbo, sub_font, li->id, & c, &pen); + //add_text(title_vbo, sub_font, li.info.id, & c, &pen); + title_vbo.add_text(sub_font, li.info.id, c, pen); if (get->setting_bools[Show_Buttons]) { - itemzcore_add_text(title_vbo,1475., 61., getLangSTR(SETTINGS)); - itemzcore_add_text(title_vbo,1435., 110., getLangSTR(BUTTON_TRIANGLE)); + itemzcore_add_text(title_vbo,1475., 61., getLangSTR(LANG_STR::SETTINGS)); + itemzcore_add_text(title_vbo,1435., 110., getLangSTR(LANG_STR::BUTTON_TRIANGLE)); if (!confirm_close) - itemzcore_add_text(title_vbo,1660., 61., getLangSTR(BUTTON_CLOSE)); + itemzcore_add_text(title_vbo,1660., 61., getLangSTR(LANG_STR::BUTTON_CLOSE)); - itemzcore_add_text(title_vbo, 1660., 110., getLangSTR(CHANGE_CATEGORY)); + itemzcore_add_text(title_vbo, 1660., 110., getLangSTR(LANG_STR::CHANGE_CATEGORY)); } if (confirm_close) - itemzcore_add_text(title_vbo,1660., 61., getLangSTR(BUTTON_CONFIRM_CLOSE)); + itemzcore_add_text(title_vbo,1660., 61., getLangSTR(LANG_STR::BUTTON_CONFIRM_CLOSE)); if(get->sort_cat != NO_CATEGORY_FILTER){ //pen.y -= 32; - itemzcore_add_text(title_vbo, 1440., 175., getLangSTR(RESET_CAT)); - itemzcore_add_text(title_vbo, 780, 50, fmt::format("{0:} - {1:}", getLangSTR(SHOWING_CATEGORY), category_get_str(get->sort_cat))); + itemzcore_add_text(title_vbo, 1440., 175., getLangSTR(LANG_STR::RESET_CAT)); + itemzcore_add_text(title_vbo, 780, 50, fmt::format("{0:} - {1:}", getLangSTR(LANG_STR::SHOWING_CATEGORY), category_get_str(get->sort_cat))); } } else if (v_curr == ITEM_SETTINGS) { // fill the vbo{ std::string ud_str; - // THIS DOESNT NOT NEED TRANSLATED BECAUSE ITS NEVER SUPPOSED TO BE USED BY USERS + // THIS DOESNT NOT NEED TRANSLATED BECAUSE ITS NEVER SUPPOSED TO BE USED BY USERS // ONLY THE DEVS OF THIS PROJECT USE THIS - get->setting_bools[INTERNAL_UPDATE] ? ud_str = "Internal Update Mode Enabled" : ud_str = getLangSTR(SETTINGS); - log_info( "ud_str: %s", (numb_of_settings == NUMBER_OF_SETTINGS) ? ud_str.c_str() : getLangSTR(ADVANCED_SETTINGS).c_str()); + get->setting_bools[INTERNAL_UPDATE] ? ud_str = "Internal Update Mode Enabled" : ud_str = getLangSTR(LANG_STR::SETTINGS); + //log_info( "ud_str: %s", (numb_of_settings == NUMBER_OF_SETTINGS) ? ud_str.c_str() : getLangSTR(LANG_STR::ADVANCED_SETTINGS).c_str()); setting_pen.x += 10.; - texture_font_load_glyphs(sub_font, (numb_of_settings == NUMBER_OF_SETTINGS) ? ud_str.c_str() : getLangSTR(ADVANCED_SETTINGS).c_str()); - add_text(title_vbo, sub_font, (numb_of_settings == NUMBER_OF_SETTINGS) ? ud_str : getLangSTR(ADVANCED_SETTINGS), &c, &setting_pen); + texture_font_load_glyphs(sub_font, (numb_of_settings == NUMBER_OF_SETTINGS) ? ud_str.c_str() : getLangSTR(LANG_STR::ADVANCED_SETTINGS).c_str()); + title_vbo.add_text(sub_font, (numb_of_settings == NUMBER_OF_SETTINGS) ? ud_str : getLangSTR(LANG_STR::ADVANCED_SETTINGS), c, setting_pen); if (get->setting_bools[Show_Buttons]) { - // itemzcore_add_text(title_vbo,80., 115., getLangSTR(BUTTON_X_INFO)); - itemzcore_add_text(title_vbo, 80., 20., getLangSTR(BUTTON_X_INFO)); - itemzcore_add_text(title_vbo,80., 65., getLangSTR(BUTTON_O_INFO)); - itemzcore_add_text(title_vbo,390., 100., getLangSTR(BUTTON_LEFT_RIGHT_INFO)); - itemzcore_add_text(title_vbo,370., 42., getLangSTR(BUTTON_UP_DOWN_INFO)); - itemzcore_add_text(title_vbo,inserted_disc.load() > 0 ? 115 : 40., 110., inserted_disc.load() > 0 ? getLangSTR(EJECT_DISK) : getLangSTR(NO_DISC_INSERTED)); + // itemzcore_add_text(title_vbo,80., 115., getLangSTR(LANG_STR::BUTTON_X_INFO)); + itemzcore_add_text(title_vbo, 80., 20., getLangSTR(LANG_STR::BUTTON_X_INFO)); + itemzcore_add_text(title_vbo,80., 65., getLangSTR(LANG_STR::BUTTON_O_INFO)); + itemzcore_add_text(title_vbo,390., 100., getLangSTR(LANG_STR::BUTTON_LEFT_RIGHT_INFO)); + itemzcore_add_text(title_vbo,370., 42., getLangSTR(LANG_STR::BUTTON_UP_DOWN_INFO)); + itemzcore_add_text(title_vbo,inserted_disc.load() > 0 ? 115 : 40., 110., inserted_disc.load() > 0 ? getLangSTR(LANG_STR::EJECT_DISK) : getLangSTR(LANG_STR::NO_DISC_INSERTED)); } } @@ -3194,55 +3246,58 @@ void DrawScene_4(void) } // last one is 0: the selected item //log_info("********** end"); + // log_info("000000000000"); if (!i) { j = 0; - if (all_apps[0].token_c >= 1) + if (all_apps[0].count.token_c >= 1) { - // log_info("********** numb %i", all_apps[0].token_c); + // log_info("********** numb %i", all_apps[0].count.token_c); + // log_info("000000000000"); goto draw_item; } // else // log_info("no apps found"); } - - if (all_apps[0].token_c < 1 && !title_vbo) +// log_info("000000000000"); + if (all_apps[0].count.token_c < 1 && !title_vbo) { - title_vbo = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); + title_vbo = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); if(v_curr == ITEMzFLOW){ - itemzcore_add_text(title_vbo, 830, 150, getLangSTR(NO_APPS_FOUND)); + itemzcore_add_text(title_vbo, 830, 150, getLangSTR(LANG_STR::NO_APPS_FOUND)); if (get->setting_bools[Show_Buttons]) { - itemzcore_add_text(title_vbo, 1475., 61., getLangSTR(SETTINGS)); - itemzcore_add_text(title_vbo, 1435., 110., getLangSTR(BUTTON_TRIANGLE)); + itemzcore_add_text(title_vbo, 1475., 61., getLangSTR(LANG_STR::SETTINGS)); + itemzcore_add_text(title_vbo, 1435., 110., getLangSTR(LANG_STR::BUTTON_TRIANGLE)); if (!confirm_close) - itemzcore_add_text(title_vbo, 1660., 61., getLangSTR(BUTTON_CLOSE)); + itemzcore_add_text(title_vbo, 1660., 61., getLangSTR(LANG_STR::BUTTON_CLOSE)); - itemzcore_add_text(title_vbo, 1660., 110., getLangSTR(CHANGE_CATEGORY)); + itemzcore_add_text(title_vbo, 1660., 110., getLangSTR(LANG_STR::CHANGE_CATEGORY)); } if (confirm_close) - itemzcore_add_text(title_vbo, 1660., 61., getLangSTR(BUTTON_CONFIRM_CLOSE)); + itemzcore_add_text(title_vbo, 1660., 61., getLangSTR(LANG_STR::BUTTON_CONFIRM_CLOSE)); - itemzcore_add_text(title_vbo, 1440., 175., getLangSTR(RESET_CAT)); - itemzcore_add_text(title_vbo, 780, 50, fmt::format("{0:} - {1:}", getLangSTR(SHOWING_CATEGORY), category_get_str(get->sort_cat))); + itemzcore_add_text(title_vbo, 1440., 175., getLangSTR(LANG_STR::RESET_CAT)); + itemzcore_add_text(title_vbo, 780, 50, fmt::format("{0:} - {1:}", getLangSTR(LANG_STR::SHOWING_CATEGORY), category_get_str(get->sort_cat))); } else if (v_curr == ITEM_SETTINGS) { - itemzcore_add_text(title_vbo, (float)(resolution.y / 2.1), (float)(resolution.y - 190.), getLangSTR(SETTINGS)); + itemzcore_add_text(title_vbo, (float)(resolution.y / 2.1), (float)(resolution.y - 190.), getLangSTR(LANG_STR::SETTINGS)); if (get->setting_bools[Show_Buttons]) { - itemzcore_add_text(title_vbo, 80., 20., getLangSTR(BUTTON_X_INFO)); - itemzcore_add_text(title_vbo, 80., 65., getLangSTR(BUTTON_O_INFO)); - itemzcore_add_text(title_vbo, 390., 100., getLangSTR(BUTTON_LEFT_RIGHT_INFO)); - itemzcore_add_text(title_vbo, 370., 42., getLangSTR(BUTTON_UP_DOWN_INFO)); - itemzcore_add_text(title_vbo, 580., 25., getLangSTR(ADVANCED_SETTINGS)); + itemzcore_add_text(title_vbo, 80., 20., getLangSTR(LANG_STR::BUTTON_X_INFO)); + itemzcore_add_text(title_vbo, 80., 65., getLangSTR(LANG_STR::BUTTON_O_INFO)); + itemzcore_add_text(title_vbo, 390., 100., getLangSTR(LANG_STR::BUTTON_LEFT_RIGHT_INFO)); + itemzcore_add_text(title_vbo, 370., 42., getLangSTR(LANG_STR::BUTTON_UP_DOWN_INFO)); + itemzcore_add_text(title_vbo, 580., 25., getLangSTR(LANG_STR::ADVANCED_SETTINGS)); } } } all_drawn: +// log_info("000000000000"); glDisable(GL_BLEND); glUseProgram(0); draw_additions((view_t)v_curr); @@ -3250,17 +3305,19 @@ all_drawn: if (old_curr != v_curr) GLES2_refresh_sysinfo(); - if (v_curr != ITEMzFLOW) + if (v_curr != ITEMzFLOW){ GLES2_render_list((view_t)v_curr); // v2 - - switch (v_curr) // draw more things, per view + // log_info("v_curr: %i", v_curr); + } +// log_info("000000000000"); + switch (v_curr) // draw more things, per view_main { case ITEMzFLOW: if (pr_dbg) { log_info("%d,\t%s\t%s", g_idx, - all_apps[g_idx].name.c_str(), - all_apps[g_idx].id.c_str()); + all_apps[g_idx].info.name.c_str(), + all_apps[g_idx].info.id.c_str()); } break; // add menu @@ -3268,8 +3325,7 @@ all_drawn: { if (old_curr != v_curr) { - l = &ls_p; - l->vbo_s = ASK_REFRESH; + ls_p.vbo_s = ASK_REFRESH; } break; } @@ -3278,16 +3334,12 @@ all_drawn: // log_info("%i", __LINE__); break; } - case FILE_BROWSER: - { - break; - } } // texts out of VBO - ftgl_render_vbo(title_vbo, NULL); - + title_vbo.render_vbo(NULL); old_curr = v_curr; pr_dbg = 0; + // log_info("000000000000"); } /* @@ -3297,17 +3349,20 @@ all_drawn: */extern std::atomic_bool is_idle; void Render_ItemzCore(void) { + bool is_file_manager = (v_curr == FILE_BROWSER_LEFT || v_curr == FILE_BROWSER_RIGHT); // update the // v_curr = FILE_BROWSER; on_GLES2_Update(u_t); GLES2_ani_update(u_t); if(!is_idle.load()){ - if ((view_t)v_curr != FILE_BROWSER) + if (!is_file_manager){ DrawScene_4(); + // log_info("000000000000"); + } else { draw_additions((view_t)v_curr); - GLES2_render_list(FILE_BROWSER); + GLES2_render_list((view_t)v_curr); } } diff --git a/itemzflow/source/GLES2_menu.cpp b/itemzflow/source/GLES2_menu.cpp index b891040..1f25d96 100644 --- a/itemzflow/source/GLES2_menu.cpp +++ b/itemzflow/source/GLES2_menu.cpp @@ -5,14 +5,13 @@ #include "defines.h" #include "GLES2_common.h" #include +#define FS_START_1 "/" #if defined(__ORBIS__) #include "shaders.h" -#define FS_START_1 "/user" #define FS_START_2 "/data" #else #include "pc_shaders.h" -#define FS_START_1 "/" -#define FS_START_2 "/home/lm/" +#define FS_START_2 "/home/lm" #endif #include #include @@ -20,12 +19,13 @@ #include #include #include +#include "feature_classes.hpp" #if defined(__ORBIS__) #include "patcher.h" #endif GLuint ic = GL_NULL; -extern vertex_buffer_t* title_vbo; +extern VertexBuffer title_vbo, test_v; extern bool skipped_first_X; // the Settings extern ItemzSettings set, @@ -45,19 +45,14 @@ extern std::vector gm_p_text; char selected_text[64]; extern int DL_CO; extern bool is_dumpable; - -// ------------------------------------------------------- typedef & struct --- -typedef struct { - float x, y, z; // position (3f) - float r, g, b, a; // color (4f) -} vertex_t; +extern int curr_file_index; // ------------------------------------------------------- global variables --- -static vertex_buffer_t* rects_buffer; +static VertexBuffer rects_buffer; static GLuint shader = GL_NULL, mz_shader= GL_NULL, curr_Program; // the current one -static mat4 model, view, projection; +static mat4 model_menu, view_menu, projection_menu; //static GLuint g_TimeSlot = 0; static vec2 resolution2; @@ -68,7 +63,8 @@ extern vec2 p1_pos; // unused yet extern struct _trainer_struct trs; #endif -void draw_rect(vec4 *color, float x, float y, float w, float h){ +using namespace multi_select_options; +void draw_rect(vec4 &color, float x, float y, float w, float h){ vec4 r; vec2 p = (vec2){x, y}; vec2 s = (vec2){w, h}; @@ -76,16 +72,35 @@ void draw_rect(vec4 *color, float x, float y, float w, float h){ r.xy = px_pos_to_normalized(&p), r.zw = px_pos_to_normalized(&s); // gles render the frect - ORBIS_RenderFillRects(USE_COLOR, color, &r, 1); + std::vector v; + v.push_back(r); + ORBIS_RenderFillRects(USE_COLOR, color, v, 1); +} + +VertexBuffer vbo_from_rect(vec4& rect) +{ + VertexBuffer vbo = VertexBuffer("vertex:3f,color:4f"); + vec4 color = { 1, 0, 0, 1 }; + float r = color.r, g = color.g, b = color.b, a = color.a; + /* VBO is setup as: "vertex:3f, color:4f */ + vertex_t vtx[4] = { { rect.x, rect.y, 0, r,g,b,a }, + { rect.x, rect.w, 0, r,g,b,a }, + { rect.z, rect.w, 0, r,g,b,a }, + { rect.z, rect.y, 0, r,g,b,a } }; + // two triangles: 2 * 3 vertex + GLuint idx[6] = { 0, 1, 2, 0, 2, 3 }; + vbo.push_back(vtx, 4, idx, 6); + + return vbo; } // io loads borders, then save UVs -vec4 get_rect_from_index(const int idx, const layout_t* l, vec4* io) +vec4 get_rect_from_index(const int idx, const layout_t& l, vec4* io) { // output vector, in px vec4 r = (0); // item position and size, fieldsize, borders, in px - vec2 p = (0), // l->bound_box.xy, - s = l->bound_box.zw, - fs = (vec2){(float)l->fieldsize.x, (float)l->fieldsize.y}, + vec2 p = (0), // l.bound_box.xy, + s = l.bound_box.zw, + fs = (vec2){(float)l.fieldsize.x, (float)l.fieldsize.y}, borders = (0); // override, load vector if (io) borders = io->xy; @@ -111,48 +126,44 @@ vec4 get_rect_from_index(const int idx, const layout_t* l, vec4* io) return r; // in px size! } -void GLES2_layout_init(int req_item_count, layout_t* l) +void GLES2_layout_init(int req_item_count, layout_t& l) { - l->page_sel.x = 0; + l.page_sel.x = 0; // dynalloc for max requested number of items - l->item_c = req_item_count; - l->is_active = true; -} -vec4 frect_add_border(vec4* in, int border) -{ /* increase the size of rectangle by applying some - border pixels offset to each of two rect points */ - vec4 r = *in; - vec2 o = (vec2)(border) / resolution * 2; - r.xy -= o, - r.zw += o; - // normalized rectangle (.xyzw) as (p1.xy, p2.xy) - return r; + l.item_d.clear(); + l.item_c = req_item_count; + l.item_d.resize(l.item_c); + l.is_active = true; } /* also, different panel have a different selector */ -static void GLES2_render_selector(layout_t* l, vec4* rect) +static void GLES2_render_selector(view_t v, vec4 &rect) { - vec4 r = *rect; - if (l == &ls_p || l == &setting_p) + std::vector r; + r.push_back(rect); + if (v == ITEMzFLOW || v == ITEM_PAGE || v == ITEM_SETTINGS) { // gles render the selected c - ORBIS_RenderFillRects(USE_COLOR, &grey, &r, 1); + ORBIS_RenderFillRects(USE_COLOR, grey, r, 1); // gles render the glowing blue box - ORBIS_RenderDrawBox(USE_UTIME, &sele, &r); + ORBIS_RenderDrawBox(USE_UTIME, sele, rect); } } +// multi-select options +using namespace multi_select_options; + std::string Sort_get_str(Sort_Multi_Sel num) { sprintf(&text_str_tmp[0], "OUT_OF_INDEX_ERROR (%i)", num); switch (num) { - case NA_SORT: - return getLangSTR(NA_SORT1); - case TID_ALPHA_SORT: - return getLangSTR(TID_ALPHA_SORT1); - case TITLE_ALPHA_SORT: - return getLangSTR(TITLE_ALPHA_SORT1); + case multi_select_options::NA_SORT: + return getLangSTR(LANG_STR::NA_SORT1); + case multi_select_options::TID_ALPHA_SORT: + return getLangSTR(LANG_STR::TID_ALPHA_SORT1); + case multi_select_options::TITLE_ALPHA_SORT: + return getLangSTR(LANG_STR::TITLE_ALPHA_SORT1); default: return &text_str_tmp[0]; } @@ -161,9 +172,9 @@ std::string orbis_menu_get_str(ShellUI_Multi_Sel num) { switch (num) { case SHELLUI_SETTINGS_MENU: - return getLangSTR(SETTINGS); + return getLangSTR(LANG_STR::SETTINGS); case SHELLUI_POWER_SAVE: - return getLangSTR(PSV_MENU); + return getLangSTR(LANG_STR::PSV_MENU); default: return "OUT_OF_INDEX_ERROR"; } @@ -172,11 +183,11 @@ std::string power_menu_get_str(Power_control_sel num) { switch (num) { case POWER_OFF_OPT: - return getLangSTR(SHUTDOWN_STR); + return getLangSTR(LANG_STR::SHUTDOWN_STR); case RESTART_OPT: - return getLangSTR(RESTART_STR); + return getLangSTR(LANG_STR::RESTART_STR); case SUSPEND_OPT: - return getLangSTR(SUSPEND_STR); + return getLangSTR(LANG_STR::SUSPEND_STR); default: return "OUT_OF_INDEX_ERROR"; } @@ -186,11 +197,11 @@ std::string uninstall_get_str(Uninstall_Multi_Sel num) { switch (num) { case UNINSTALL_GAME: - return getLangSTR(UNINSTALL_GAME1); + return getLangSTR(LANG_STR::UNINSTALL_GAME1); case UNINSTALL_PATCH: - return getLangSTR(UNINSTALL_PATCH1); + return getLangSTR(LANG_STR::UNINSTALL_PATCH1); case UNINSTALL_DLC: - return getLangSTR(UNINSTALL_DLC1); + return getLangSTR(LANG_STR::UNINSTALL_DLC1); default: return "OUT_OF_INDEX_ERROR"; } @@ -205,11 +216,11 @@ std::string save_game_get_str(SAVE_Multi_Sel num) { switch (num) { case BACKUP_GAME_SAVE: - return getLangSTR(BACKUP_SAVE); + return getLangSTR(LANG_STR::BACKUP_SAVE); case RESTORE_GAME_SAVE: - return getLangSTR(IMPORT_SAVE); + return getLangSTR(LANG_STR::IMPORT_SAVE); case DELETE_GAME_SAVE: - return getLangSTR(SAVE_DELETE); + return getLangSTR(LANG_STR::SAVE_DELETE); default: return "OUT_OF_INDEX_ERROR"; } @@ -219,17 +230,19 @@ std::string dumper_opt_get_str(Dump_Multi_Sels num) { //0 is DUMP ALL switch (num) { case SEL_DUMP_ALL: - return getLangSTR(SEL_DUMP_ALL1); + return getLangSTR(LANG_STR::SEL_DUMP_ALL1); + case SEL_DUMP_VALID: + return getLangSTR(LANG_STR::SEL_DUMP_VALID1); case SEL_BASE_GAME: - return getLangSTR(SEL_BASE_GAME1); + return getLangSTR(LANG_STR::SEL_BASE_GAME1); case SEL_GAME_PATCH: - return getLangSTR(SEL_GAME_PATCH1); + return getLangSTR(LANG_STR::SEL_GAME_PATCH1); case SEL_REMASTER: - return getLangSTR(SEL_REMASTER1); + return getLangSTR(LANG_STR::SEL_REMASTER1); case SEL_DLC: - return getLangSTR(SEL_DLC1); + return getLangSTR(LANG_STR::SEL_DLC1); case SEL_FPKG: - return getLangSTR(BACKUP_FPKG); + return getLangSTR(LANG_STR::BACKUP_FPKG); default: return "OUT_OF_INDEX_ERROR"; } @@ -241,11 +254,11 @@ std::string restore_app_get_str(RESTORE_OPTIONS num) switch (num) { case RESTORE_APP: - return getLangSTR(RESTORE_APP_OPT_1); + return getLangSTR(LANG_STR::RESTORE_APP_OPT_1); case RESTORE_DLC: - return getLangSTR(RESTORE_APP_OPT_2); + return getLangSTR(LANG_STR::RESTORE_APP_OPT_2); case RESTORE_PATCH: - return getLangSTR(RESTORE_APP_OPT_3); + return getLangSTR(LANG_STR::RESTORE_APP_OPT_3); default: return "OUT_OF_INDEX_ERROR"; } @@ -256,11 +269,11 @@ std::string move_app_get_str(MOVE_OPTIONS num) switch (num) { case MOVE_APP: - return getLangSTR(MOVE_APP_OPT_1); + return getLangSTR(LANG_STR::MOVE_APP_OPT_1); case MOVE_DLC: - return getLangSTR(MOVE_APP_OPT_2); + return getLangSTR(LANG_STR::MOVE_APP_OPT_2); case MOVE_PATCH: - return getLangSTR(MOVE_APP_OPT_3); + return getLangSTR(LANG_STR::MOVE_APP_OPT_3); default: return "OUT_OF_INDEX_ERROR"; } @@ -271,11 +284,11 @@ std::string control_app_get_str(GameStatus num) switch (num) { case OTHER_APP_OPEN: - return getLangSTR(CLOSE_GAME); + return getLangSTR(LANG_STR::CLOSE_GAME); case RESUMABLE: - return getLangSTR(GAME_RESUME); + return getLangSTR(LANG_STR::GAME_RESUME); case NO_APP_OPENED: - return getLangSTR(LAUNCH_GAME); + return getLangSTR(LANG_STR::LAUNCH_GAME); default: return "OUT_OF_INDEX_ERROR"; } @@ -285,16 +298,18 @@ std::string category_get_str(Sort_Category num) { switch (num) { + case FILTER_FAVORITES: + return getLangSTR(LANG_STR::FAVORITES); case FILTER_RETAIL_GAMES: - return getLangSTR(RETAIL_GAMES); + return getLangSTR(LANG_STR::RETAIL_GAMES); case FILTER_FPKG: - return getLangSTR(FPKG_STR); + return getLangSTR(LANG_STR::FPKG_STR); case FILTER_GAMES: - return getLangSTR(GAMES); + return getLangSTR(LANG_STR::GAMES); case FILTER_HOMEBREW: - return getLangSTR(HOMEBREW); + return getLangSTR(LANG_STR::HOMEBREW); default: - return getLangSTR(NONE); + return getLangSTR(LANG_STR::NONE); } } // REBUILD_DB_OPT_1=Rebuild Internal Database @@ -304,11 +319,11 @@ std::string rebuild_db_get_str(Rebuild_db_Multi_Sel num) switch (num) { case REBUILD_ALL: - return getLangSTR(REBUILD_DB_OPT_1); + return getLangSTR(LANG_STR::REBUILD_DB_OPT_1); case REBUILD_DLC: - return getLangSTR(REBUILD_DB_OPT_2); + return getLangSTR(LANG_STR::REBUILD_DB_OPT_2); case REBUILD_REACT: - return getLangSTR(REBUILD_DB_OPT_3); + return getLangSTR(LANG_STR::REBUILD_DB_OPT_3); default: return "OUT_OF_INDEX_ERROR"; } @@ -334,10 +349,10 @@ std::string trainer_get_str(int num) #endif } -static void print_option_info(layout_t* l, vec2* pen, std::string line_1, std::string line_2, bool is_settings) { +static void print_option_info(layout_t& l, vec2 &pen, std::string line_1, std::string line_2, bool is_settings) { vec4 c = col * .75f; - vec2 info_text = *pen; + vec2 info_text = pen; if (is_settings) { info_text.x = 520.; @@ -349,7 +364,7 @@ static void print_option_info(layout_t* l, vec2* pen, std::string line_1, std::s } if (!line_1.empty()) - add_text(l->vbo, sub_font, fmt::format("{0:.80}", line_1), &c, &info_text); + l.vbo.add_text( sub_font, fmt::format("{0:.80}", line_1), c, info_text); if (!line_2.empty()) { @@ -361,119 +376,121 @@ static void print_option_info(layout_t* l, vec2* pen, std::string line_1, std::s info_text.x = 90.; info_text.y = 170.; } - add_text(l->vbo, sub_font, fmt::format("{0:.80}", line_2), &c, &info_text); + l.vbo.add_text(sub_font, fmt::format("{0:.80}", line_2), c, info_text); } } /* default way to deal with text, for layout_t */ -static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) +static void layout_compose_text(view_t vw, int idx, vec2 &pen, bool save_text) { #if 0 log_info("%s %p, %d", __FUNCTION__, l, save_text); #endif + layout_t &l = getLayoutbyView(vw); + GameStatus app_state = app_status.load(); std::string tmp = ""; vec4 c = col * .75f; // default indexing, item_t* AOS - item_t* li = &l->item_d[idx]; + item_t &li = l.item_d[idx]; - if (l == &ls_p)//|| l == setting_p) + if (vw == ITEM_PAGE)//|| l == setting_p) { - vec2 bk = *pen; + vec2 bk = pen; bk.x += 10, bk.y += 50; vec2 sub_ops = { bk.x, bk.y }; - add_text(l->vbo, sub_font, li->name, &col, &bk); + l.vbo.add_text( sub_font, li.info.name, col, bk); /* special option */ - vec2 tp = *pen; + vec2 tp = pen; //vec4 rr = {300., 300., 100., 100.}; switch (idx) { - case Launch_Game_opt: - if(app_status != RESUMABLE) break; - if (!li->multi_sel.is_active){ - li->multi_sel.pos = (ivec4){0, 0, 0, 2}; - li->multi_sel.is_active = true; + case cf_ops::REG_OPTS::LAUNCH_GAME: + if(app_state != RESUMABLE) break; + if (!li.multi_sel.is_active){ + li.multi_sel.pos = (ivec4){0, 0, 0, 2}; + li.multi_sel.is_active = true; log_info("$$$$$ multi_sel is active"); } - case Restore_apps_opt: // 3 - case Move_apps_opt: // 3 - case Game_Save_opt: // 3 - if (!li->multi_sel.is_active) + case cf_ops::REG_OPTS::RESTORE_APPS: // 3 + case cf_ops::REG_OPTS::MOVE_APPS: // 3 + case cf_ops::REG_OPTS::GAME_SAVE: // 3 + if (!li.multi_sel.is_active) { - li->multi_sel.pos = (ivec4){0, 0, 0, 3}; - li->multi_sel.is_active = true; + li.multi_sel.pos = (ivec4){0, 0, 0, 3}; + li.multi_sel.is_active = true; } - case Trainers_opt: + case cf_ops::REG_OPTS::TRAINERS: #if defined(__ORBIS__) - if (!li->multi_sel.is_active) + if (!li.multi_sel.is_active) { - li->multi_sel.pos = (ivec4){0, 0, 0, trs.controls_text.size()}; - li->multi_sel.is_active = true; + li.multi_sel.pos = (ivec4){0, 0, 0, trs.controls_text.size()}; + li.multi_sel.is_active = true; } #else - if (!li->multi_sel.is_active) + if (!li.multi_sel.is_active) { - li->multi_sel.pos = (ivec4){0, 0, 0, 0}; - li->multi_sel.is_active = true; + li.multi_sel.pos = (ivec4){0, 0, 0, 0}; + li.multi_sel.is_active = true; } #endif - case Uninstall_opt: - if (!li->multi_sel.is_active) + case cf_ops::REG_OPTS::UNINSTALL: + if (!li.multi_sel.is_active) { - li->multi_sel.pos = (ivec4){0, 0, 0, 2}; - li->multi_sel.is_active = true; + li.multi_sel.pos = (ivec4){0, 0, 0, 2}; + li.multi_sel.is_active = true; } - case Dump_Game_opt: { - if (!li->multi_sel.is_active) + case cf_ops::REG_OPTS::DUMP_GAME: { + if (!li.multi_sel.is_active) { - li->multi_sel.pos = (ivec4){0, 0, 0, 6}; - li->multi_sel.is_active = true; + li.multi_sel.pos = (ivec4){0, 0, 0, 6}; + li.multi_sel.is_active = true; } //set active for all above too - //li->multi_sel.is_active = true; - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + //li.multi_sel.is_active = true; + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { - if (idx == Dump_Game_opt){ - if (current_app_is_fpkg){ - if(!is_dumpable){ - li->multi_sel.pos.w = 1; + if (idx == cf_ops::REG_OPTS::DUMP_GAME){ + if (all_apps[g_idx].flags.is_fpkg){ + if(!all_apps[g_idx].flags.is_dumpable){ + li.multi_sel.pos.w = 1; get->Dump_opt = SEL_FPKG; tmp = dumper_opt_get_str(SEL_FPKG); break; } else - li->multi_sel.pos.w = 6; + li.multi_sel.pos.w = 7; } else { - if(is_dumpable){ - li->multi_sel.pos.w = 5; - if (li->multi_sel.pos.y == SEL_FPKG) - li->multi_sel.pos.y--; + if(!all_apps[g_idx].flags.is_fpkg){ + li.multi_sel.pos.w = 6; + if (li.multi_sel.pos.y == SEL_FPKG) + li.multi_sel.pos.y--; } else{ - li->multi_sel.pos.y = SEL_RESET; - tmp = getLangSTR(OPT_NOT_PERMITTED); + li.multi_sel.pos.y = SEL_RESET; + tmp = getLangSTR(LANG_STR::OPT_NOT_PERMITTED); break; } } } // show '< X >' around Option switch (idx){ - case Dump_Game_opt:{ - tmp = fmt::format("< {0:.20} >", dumper_opt_get_str((Dump_Multi_Sels)li->multi_sel.pos.y)); - get->Dump_opt = (Dump_Multi_Sels)li->multi_sel.pos.y; + case cf_ops::REG_OPTS::DUMP_GAME:{ + tmp = fmt::format("< {0:.20} >", dumper_opt_get_str((multi_select_options::Dump_Multi_Sels)li.multi_sel.pos.y)); + get->Dump_opt = (Dump_Multi_Sels)li.multi_sel.pos.y; break; } - case Uninstall_opt:{ - tmp = fmt::format("< {0:.20} >", uninstall_get_str((Uninstall_Multi_Sel)li->multi_sel.pos.y)); - get->un_opt = (Uninstall_Multi_Sel)li->multi_sel.pos.y; + case cf_ops::REG_OPTS::UNINSTALL:{ + tmp = fmt::format("< {0:.20} >", uninstall_get_str((multi_select_options::Uninstall_Multi_Sel)li.multi_sel.pos.y)); + get->un_opt = (multi_select_options::Uninstall_Multi_Sel)li.multi_sel.pos.y; break; } - case Game_Save_opt:{ + case cf_ops::REG_OPTS::GAME_SAVE:{ #if defined(__ORBIS__) if(!gm_save.is_loaded || gm_save.ui_requested_save != gm_save.ui_current_save){ if(!(gm_save.is_loaded = GameSave_Info(&gm_save, (gm_save.ui_requested_save == -1) ? 0 : gm_save.ui_requested_save))){ @@ -501,74 +518,74 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) vec4 color = (vec4){.8164, .8164, .8125, 1.}; vec2 pen; pen.x = 800., pen.y = 750.; - tmp = fmt::format("{0:} {1:}", gm_save.numb_of_saves, (gm_save.numb_of_saves > 1) ? getLangSTR(FOUND_SAVE_MESSAGE_OVER_1) : getLangSTR(FOUND_SAVE_MESSAGE) ); - add_text(l->vbo, sub_font, tmp, &color, &pen); + tmp = fmt::format("{0:} {1:}", gm_save.numb_of_saves, (gm_save.numb_of_saves > 1) ? getLangSTR(LANG_STR::FOUND_SAVE_MESSAGE_OVER_1) : getLangSTR(LANG_STR::FOUND_SAVE_MESSAGE) ); + l.vbo.add_text( sub_font, tmp, color, pen); pen.y = 710., pen.x = 1040.; - add_text(l->vbo, main_font, title, &color, &pen); + l.vbo.add_text( main_font, all_apps[g_idx].info.name, color, pen); // ONLY CONTINUE IF SAVE IS LOADED if(!gm_save.is_loaded){ pen.y = 680., pen.x = 1040.; - tmp = fmt::format("{0:}: {1:#x}", getLangSTR(USER_LANG), gm_save.userid); - add_text(l->vbo, main_font, tmp, &color, &pen); - tmp = fmt::format(" < {} >", getLangSTR(IMPORT_SAVE)); - li->multi_sel.pos.y = RESTORE_GAME_SAVE; + tmp = fmt::format("{0:}: {1:#x}", getLangSTR(LANG_STR::USER_LANG), gm_save.userid); + l.vbo.add_text( main_font, tmp, color, pen); + tmp = fmt::format(" < {} >", getLangSTR(LANG_STR::IMPORT_SAVE)); + li.multi_sel.pos.y = RESTORE_GAME_SAVE; break; } pen.y = 680., pen.x = 1040.; - add_text(l->vbo, main_font, gm_save.mtime, &color, &pen); + l.vbo.add_text( main_font, gm_save.mtime, color, pen); pen.y = 650., pen.x = 1040.; - tmp = fmt::format("{0:}: {1:#x}", getLangSTR(USER_LANG), gm_save.userid); - add_text(l->vbo, main_font, tmp, &color, &pen); + tmp = fmt::format("{0:}: {1:#x}", getLangSTR(LANG_STR::USER_LANG), gm_save.userid); + l.vbo.add_text( main_font, tmp, color, pen); pen.y = 620., pen.x = 1040.; //KB to Bytes to String tmp = fmt::format("{} | {} Blocks", calculateSize(gm_save.size * 1000), gm_save.blocks); - add_text(l->vbo, main_font, tmp, &color, &pen); + l.vbo.add_text( main_font, tmp, color, pen); pen.y = 570., pen.x = 790.; - add_text(l->vbo, main_font, fmt::format("{0:.45}", gm_save.detail), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{0:.45}", gm_save.detail), color, pen); pen.y = 595., pen.x = 1040.; - add_text(l->vbo, main_font, fmt::format("{0:.45}", gm_save.main_title), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{0:.45}", gm_save.main_title), color, pen); pen.y = 500., pen.x = 1265.; - add_text(l->vbo, main_font, fmt::format("{}/{}", gm_save.ui_requested_save, gm_save.numb_of_saves), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{}/{}", gm_save.ui_requested_save, gm_save.numb_of_saves), color, pen); - tmp = fmt::format("< {0:.20} >", save_game_get_str((SAVE_Multi_Sel)li->multi_sel.pos.y)); + tmp = fmt::format("< {0:.20} >", save_game_get_str((multi_select_options::SAVE_Multi_Sel)li.multi_sel.pos.y)); break; - }//move_app_get_str((MOVE_OPTIONS)li->multi_sel.pos.y) - case Restore_apps_opt:{ - tmp = fmt::format("< {0:.20} >", restore_app_get_str((RESTORE_OPTIONS)li->multi_sel.pos.y)); + }//move_app_get_str((MOVE_OPTIONS)li.multi_sel.pos.y) + case cf_ops::REG_OPTS::RESTORE_APPS:{ + tmp = fmt::format("< {0:.20} >", restore_app_get_str((multi_select_options::RESTORE_OPTIONS)li.multi_sel.pos.y)); break; } - case Move_apps_opt: { - tmp = fmt::format("< {0:.20} >", move_app_get_str((MOVE_OPTIONS)li->multi_sel.pos.y)); + case cf_ops::REG_OPTS::MOVE_APPS: { + tmp = fmt::format("< {0:.20} >", move_app_get_str((multi_select_options::MOVE_OPTIONS)li.multi_sel.pos.y)); break; } - case Launch_Game_opt:{ - tmp = fmt::format("< {0:.20} >", control_app_get_str((GameStatus)(li->multi_sel.pos.y+1))); + case cf_ops::REG_OPTS::LAUNCH_GAME:{ + tmp = fmt::format("< {0:.20} >", control_app_get_str((GameStatus)(li.multi_sel.pos.y+1))); break; } - case Trainers_opt: + case cf_ops::REG_OPTS::TRAINERS: { #if defined(__ORBIS__) - patch_current_index = li->multi_sel.pos.y; - li->multi_sel.pos.w = trs.controls_text.size(); + patch_current_index = li.multi_sel.pos.y; + li.multi_sel.pos.w = trs.controls_text.size(); vec4 color = (vec4){.8164, .8164, .8125, 1.}; vec2 pen; pen.x = 800., pen.y = 750.; - add_text(l->vbo, sub_font, fmt::format("{0}", trs.patcher_title.at(patch_current_index)), &color, &pen); + l.vbo.add_text( sub_font, fmt::format("{0}", trs.patcher_title.at(patch_current_index)), color, pen); pen.y = 710., pen.x = 790.; - add_text(l->vbo, main_font, fmt::format("{1} {0}", trs.patcher_name.at(patch_current_index), getLangSTR(PATCH_NAME)), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{1} {0}", trs.patcher_name.at(patch_current_index), getLangSTR(LANG_STR::PATCH_NAME)), color, pen); pen.y = 680., pen.x = 790.; - add_text(l->vbo, main_font, fmt::format("{1} {0}", trs.patcher_app_ver.at(patch_current_index), getLangSTR(PATCH_APP_VER)), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{1} {0}", trs.patcher_app_ver.at(patch_current_index), getLangSTR(LANG_STR::PATCH_APP_VER)), color, pen); pen.y = 650., pen.x = 790.; - add_text(l->vbo, main_font, fmt::format("{1} {0}", trs.patcher_author.at(patch_current_index), getLangSTR(PATCH_AUTHOR)), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{1} {0}", trs.patcher_author.at(patch_current_index), getLangSTR(LANG_STR::PATCH_AUTHOR)), color, pen); pen.y = 620., pen.x = 790.; - add_text(l->vbo, main_font, fmt::format("{1} {0:.40}", trs.patcher_note.at(patch_current_index), getLangSTR(PATCH_NOTE)), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{1} {0:.40}", trs.patcher_note.at(patch_current_index), getLangSTR(LANG_STR::PATCH_NOTE)), color, pen); pen.y = 590., pen.x = 790.; - add_text(l->vbo, main_font, fmt::format("{1} {0}", trs.patcher_patch_ver.at(patch_current_index), getLangSTR(PATCH_VER)), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{1} {0}", trs.patcher_patch_ver.at(patch_current_index), getLangSTR(LANG_STR::PATCH_VER)), color, pen); pen.y = 560., pen.x = 790.; - add_text(l->vbo, main_font, fmt::format("{1} {0}", trs.patcher_app_elf.at(patch_current_index), getLangSTR(PATCH_APP_ELF)), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{1} {0}", trs.patcher_app_elf.at(patch_current_index), getLangSTR(LANG_STR::PATCH_APP_ELF)), color, pen); pen.y = 530., pen.x = 790.; - add_text(l->vbo, main_font, fmt::format("{1} {0}", trs.patcher_enablement.at(patch_current_index), getLangSTR(PATCH_ENABLED)), &color, &pen); + l.vbo.add_text( main_font, fmt::format("{1} {0}", trs.patcher_enablement.at(patch_current_index), getLangSTR(LANG_STR::PATCH_ENABLED)), color, pen); log_debug("index: %i size: %i state %i ", patch_current_index, trs.controls_text.size(), trs.patcher_enablement.at(patch_current_index)); tmp = fmt::format("< {0:.20} / {1} >", trainer_get_str(patch_current_index), trs.controls_text.size()); break; @@ -578,96 +595,96 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) // compute rectangle position and size in px vec4 r = get_rect_from_index(idx, l, NULL); // start at panel position, move to center - tp = *pen; - tp.x = (l->bound_box.x + r.z / 2.) + 300.; + tp = pen; + tp.x = (l.bound_box.x + r.z / 2.) + 300.; // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs(main_font, tmp.c_str()); tp.x -= tl / 2.; - *pen = tp; + pen = tp; } else - tmp = getLangSTR(SELECT_INTERACT); + tmp = getLangSTR(LANG_STR::SELECT_INTERACT); break; } - case Change_icon_opt: { tmp = fmt::format("{0:.20}{1:}", icon_path, (icon_path.size() > 20) ? "..." : ""); break; } + case cf_ops::REG_OPTS::CHANGE_ICON: { tmp = fmt::format("{0:.20}{1:}", all_apps[g_idx].info.picpath, (all_apps[g_idx].info.picpath.size() > 20) ? "..." : ""); break; } #if defined(__ORBIS__) - case Hide_App_opt: { tmp = (AppVis ? getLangSTR(HIDE_APP) : getLangSTR(SHOW_APP)); break; } + case cf_ops::REG_OPTS::HIDE_APP: { tmp = (AppVis ? getLangSTR(LANG_STR::HIDE_APP) : getLangSTR(LANG_STR::SHOW_APP)); break; } #endif - case Change_name_opt: { tmp = fmt::format("{0:.20}{1:}", title, (title.size() > 20) ? "..." : ""); break; } + case cf_ops::REG_OPTS::CHANGE_NAME: { tmp = fmt::format("{0:.20}{1:}", all_apps[g_idx].info.name, (all_apps[g_idx].info.name.size() > 20) ? "..." : ""); break; } } if (tmp.size() > 3) { sub_ops.y -= 40; - add_text(l->vbo, main_font, tmp, &c, &sub_ops); + l.vbo.add_text( main_font, tmp, c, sub_ops); } - if (idx == 0 || (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE)) + if (idx == 0 || (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE)) { // print options info - switch (l->curr_item) + switch (l.curr_item) {//RESTORE_NOTICE - case Change_name_opt: - print_option_info(l, pen, getLangSTR(NAME_INFO), getLangSTR(IF_NOT_EFFECTED), false); + case cf_ops::REG_OPTS::CHANGE_NAME: + print_option_info(l, pen, getLangSTR(LANG_STR::NAME_INFO), getLangSTR(LANG_STR::IF_NOT_EFFECTED), false); break; - case Move_apps_opt: - print_option_info(l, pen, getLangSTR(MOVE_INFO_TEXT), getLangSTR(IF_NOT_EFFECTED), false); + case cf_ops::REG_OPTS::MOVE_APPS: + print_option_info(l, pen, getLangSTR(LANG_STR::MOVE_INFO_TEXT), getLangSTR(LANG_STR::IF_NOT_EFFECTED), false); break; - case Restore_apps_opt: - print_option_info(l, pen, getLangSTR(RESTORE_INFO_TEXT), getLangSTR(RESTORE_NOTICE), false); + case cf_ops::REG_OPTS::RESTORE_APPS: + print_option_info(l, pen, getLangSTR(LANG_STR::RESTORE_INFO_TEXT), getLangSTR(LANG_STR::RESTORE_NOTICE), false); break; - case Change_icon_opt: - print_option_info(l, pen, getLangSTR(ICON_INFO), getLangSTR(PNG_NOTE), false); + case cf_ops::REG_OPTS::CHANGE_ICON: + print_option_info(l, pen, getLangSTR(LANG_STR::ICON_INFO), getLangSTR(LANG_STR::PNG_NOTE), false); break; - case Launch_Game_opt: - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + case cf_ops::REG_OPTS::LAUNCH_GAME: + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option - if (app_status == RESUMABLE) - print_option_info(l, pen, getLangSTR(GAME_CONTROL_INFO), getLangSTR(SUSPEND_IF), false); + if (app_state == RESUMABLE) + print_option_info(l, pen, getLangSTR(LANG_STR::GAME_CONTROL_INFO), getLangSTR(LANG_STR::SUSPEND_IF), false); else - print_option_info(l, pen, getLangSTR(CLOSE_OPEN_APP_INFO), "", false); + print_option_info(l, pen, getLangSTR(LANG_STR::CLOSE_OPEN_APP_INFO), "", false); } - else if (app_status == NO_APP_OPENED) - print_option_info(l, pen, getLangSTR(LAUNCH_INFO), getLangSTR(SUSPEND_IF), false); - else if (app_status == OTHER_APP_OPEN) - print_option_info(l, pen, getLangSTR(CLOSE_OPEN_APP_INFO), "", false); - else if (app_status == RESUMABLE) - print_option_info(l, pen, getLangSTR(GAME_CONTROL_INFO), getLangSTR(SUSPEND_IF), false); + else if (app_state == NO_APP_OPENED) + print_option_info(l, pen, getLangSTR(LANG_STR::LAUNCH_INFO), getLangSTR(LANG_STR::SUSPEND_IF), false); + else if (app_state == OTHER_APP_OPEN) + print_option_info(l, pen, getLangSTR(LANG_STR::CLOSE_OPEN_APP_INFO), "", false); + else if (app_state == RESUMABLE) + print_option_info(l, pen, getLangSTR(LANG_STR::GAME_CONTROL_INFO), getLangSTR(LANG_STR::SUSPEND_IF), false); break; - case Trainers_opt: - print_option_info(l, pen, getLangSTR(TRAINER_INFO), "", false); + case cf_ops::REG_OPTS::TRAINERS: + print_option_info(l, pen, getLangSTR(LANG_STR::TRAINER_INFO), "", false); break; - case Hide_App_opt: - print_option_info(l, pen, getLangSTR(HIDE_OPT_INFO), getLangSTR(HIDE_INFO), false); + case cf_ops::REG_OPTS::HIDE_APP: + print_option_info(l, pen, getLangSTR(LANG_STR::HIDE_OPT_INFO), getLangSTR(LANG_STR::HIDE_INFO), false); break; - case Dump_Game_opt: + case cf_ops::REG_OPTS::DUMP_GAME: { - print_option_info(l, pen, getLangSTR(DUMP_OPT_INFO), "", false); - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + print_option_info(l, pen, getLangSTR(LANG_STR::DUMP_OPT_INFO), "", false); + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option - switch (li->multi_sel.pos.y) + switch (li.multi_sel.pos.y) { case SEL_DUMP_ALL: - print_option_info(l, pen, "", getLangSTR(SEL_DUMP_ALL_INFO), false); + print_option_info(l, pen, "", getLangSTR(LANG_STR::SEL_DUMP_ALL_INFO), false); break; case SEL_BASE_GAME: - print_option_info(l, pen, "", getLangSTR(SEL_BASE_GAME_INFO), false); + print_option_info(l, pen, "", getLangSTR(LANG_STR::SEL_BASE_GAME_INFO), false); break; case SEL_GAME_PATCH: - print_option_info(l, pen, "", getLangSTR(SEL_GAME_PATCH_INFO), false); + print_option_info(l, pen, "", getLangSTR(LANG_STR::SEL_GAME_PATCH_INFO), false); break; case SEL_REMASTER: - print_option_info(l, pen, "", getLangSTR(SEL_REMASTER_INFO), false); + print_option_info(l, pen, "", getLangSTR(LANG_STR::SEL_REMASTER_INFO), false); break; case SEL_DLC: - print_option_info(l, pen, "", getLangSTR(SEL_DLC_INFO), false); + print_option_info(l, pen, "", getLangSTR(LANG_STR::SEL_DLC_INFO), false); break; case SEL_FPKG: - print_option_info(l, pen, "", getLangSTR(BACKUP_FPKG_INFO), false); + print_option_info(l, pen, "", getLangSTR(LANG_STR::BACKUP_FPKG_INFO), false); break; default: break; @@ -677,19 +694,19 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) break; } - case Uninstall_opt: + case cf_ops::REG_OPTS::UNINSTALL: { - print_option_info(l, pen, getLangSTR(UNINSTALL_NOTE), "", false); - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + print_option_info(l, pen, getLangSTR(LANG_STR::UNINSTALL_NOTE), "", false); + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option - switch (li->multi_sel.pos.y) + switch (li.multi_sel.pos.y) { case UNINSTALL_GAME: - print_option_info(l, pen, "", getLangSTR(UNINSTALL_GAME_INFO), false); + print_option_info(l, pen, "", getLangSTR(LANG_STR::UNINSTALL_GAME_INFO), false); break; case UNINSTALL_PATCH: - print_option_info(l, pen, "", getLangSTR(UNINSTALL_PATCH_INFO), false); + print_option_info(l, pen, "", getLangSTR(LANG_STR::UNINSTALL_PATCH_INFO), false); break; default: break; @@ -697,21 +714,21 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) } break; } - case Game_Save_opt:{ - print_option_info(l, pen, getLangSTR(GAME_SAVE_OPTS), "", false); - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + case cf_ops::REG_OPTS::GAME_SAVE:{ + print_option_info(l, pen, getLangSTR(LANG_STR::GAME_SAVE), "", false); + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option - switch (li->multi_sel.pos.y) + switch (li.multi_sel.pos.y) { - case BACKUP_GAME_SAVE: - print_option_info(l, pen, "", getLangSTR(BACKUP_GAME_INFO), false); + case multi_select_options::BACKUP_GAME_SAVE: + print_option_info(l, pen, "", getLangSTR(LANG_STR::BACKUP_GAME_INFO), false); break; - case RESTORE_GAME_SAVE: - print_option_info(l, pen, "", getLangSTR(RESTORE_GAME_INFO), false); + case multi_select_options::RESTORE_GAME_SAVE: + print_option_info(l, pen, "", getLangSTR(LANG_STR::RESTORE_GAME_INFO), false); break; - case DELETE_GAME_SAVE: - print_option_info(l, pen, "", getLangSTR(DELETE_GAME_INFO), false); + case multi_select_options::DELETE_GAME_SAVE: + print_option_info(l, pen, "", getLangSTR(LANG_STR::DELETE_GAME_INFO), false); break; default: break; @@ -728,11 +745,11 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) } return; } - else if (l == &setting_p) { + else if (vw == ITEM_SETTINGS) { // append to vbo - vec2 tp = *pen; + vec2 tp = pen; tp.y += 40.; - add_text(l->vbo, sub_font, li->name, &col, &tp); + l.vbo.add_text( sub_font, li.info.name, col, tp); /* DUMPER_PATH_OPTION, @@ -744,40 +761,40 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) BACKGROUND_MP3_OPTION, SAVE_ITEMZFLOW_SETTINGS */ - //snprintf(&tmp[0], 63, format, get->HomeMenu_Redirection ? getLangSTR(ON2) : getLangSTR(OFF2)); break; + //snprintf(&tmp[0], 63, format, get->HomeMenu_Redirection ? getLangSTR(LANG_STR::ON2) : getLangSTR(LANG_STR::OFF2)); break; switch (idx) { case OPEN_SHELLUI_MENU: - if (!li->multi_sel.is_active) + if (!li.multi_sel.is_active) { - li->multi_sel.pos = (ivec4){0, 0, 0, 2}; - li->multi_sel.is_active = true; + li.multi_sel.pos = (ivec4){0, 0, 0, 2}; + li.multi_sel.is_active = true; } case REBUILD_DB_OPTION: case POWER_CONTROL_OPTION: case SORT_BY_OPTION: { /* special option */ - if (!li->multi_sel.is_active) + if (!li.multi_sel.is_active) { - li->multi_sel.pos = (ivec4){0, 0, 0, 3}; - li->multi_sel.is_active = true; + li.multi_sel.pos = (ivec4){0, 0, 0, 3}; + li.multi_sel.is_active = true; } - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option and set sort by opt switch (idx) { case POWER_CONTROL_OPTION: - tmp = fmt::format("< {0:.35} >", power_menu_get_str((Power_control_sel)li->multi_sel.pos.y)); + tmp = fmt::format("< {0:.35} >", power_menu_get_str((Power_control_sel)li.multi_sel.pos.y)); break; - case SORT_BY_OPTION: tmp = fmt::format("< {0:.35} >",Sort_get_str((Sort_Multi_Sel)li->multi_sel.pos.y)); break; + case SORT_BY_OPTION: tmp = fmt::format("< {0:.35} >",Sort_get_str((Sort_Multi_Sel)li.multi_sel.pos.y)); break; case OPEN_SHELLUI_MENU: - tmp = fmt::format("< {0:.35} >", orbis_menu_get_str((ShellUI_Multi_Sel)li->multi_sel.pos.y)); + tmp = fmt::format("< {0:.35} >", orbis_menu_get_str((ShellUI_Multi_Sel)li.multi_sel.pos.y)); break; case REBUILD_DB_OPTION: - tmp = fmt::format("< {0:.35} >", rebuild_db_get_str((Rebuild_db_Multi_Sel)li->multi_sel.pos.y)); + tmp = fmt::format("< {0:.35} >", rebuild_db_get_str((Rebuild_db_Multi_Sel)li.multi_sel.pos.y)); break; default: break; @@ -786,25 +803,25 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs(main_font, tmp.c_str()); // compute rectangle position and size in px - tp = *pen; + tp = pen; vec4 r = get_rect_from_index(idx, l, NULL); log_info("idx %i, r: %f %f %f %f", idx, r.x, r.y, r.z, r.w); if (numb_of_settings <= NUMBER_OF_SETTINGS) { // start at panel position, move to center - tp.x = l->bound_box.x + r.z / 2.; + tp.x = l.bound_box.x + r.z / 2.; tp.x -= tl / 2.; } else {// workaround for advanced settings if(idx % 3 == 0) // last column - tp.x = (l->bound_box.x + l->bound_box.x + r.z - 20.); + tp.x = (l.bound_box.x + l.bound_box.x + r.z - 20.); else if(idx % 3 == 1){ - tp.x = l->bound_box.x + r.z + 20.; + tp.x = l.bound_box.x + r.z + 20.; //tp.x -= tl; } } - *pen = tp; + pen = tp; } else{ switch (idx) @@ -813,7 +830,7 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) case REBUILD_DB_OPTION: case OPEN_SHELLUI_MENU: default: - tmp = getLangSTR(SELECT_INTERACT); + tmp = getLangSTR(LANG_STR::SELECT_INTERACT); break; } } @@ -831,105 +848,105 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) tmp = get->setting_strings[FUSE_PC_NFS_IP]; break; case DOWNLOAD_COVERS_OPTION: - tmp = getLangSTR(X_DL_COVER); + tmp = getLangSTR(LANG_STR::X_DL_COVER); break; case RESET_THEME_OPTION: case CHANGE_BACKGROUND_OPTION: case THEME_OPTION: case CHECK_FOR_UPDATE_OPTION: case IF_PKG_INSTALLER_OPTION: - tmp = getLangSTR(SELECT_INTERACT); + tmp = getLangSTR(LANG_STR::SELECT_INTERACT); break; - case HOME_MENU_OPTION: tmp = (get->setting_bools[HomeMenu_Redirection] ? getLangSTR(ON2) : getLangSTR(OFF2)); break; + case HOME_MENU_OPTION: tmp = (get->setting_bools[HomeMenu_Redirection] ? getLangSTR(LANG_STR::ON2) : getLangSTR(LANG_STR::OFF2)); break; case FONT_OPTION: if(numb_of_settings > NUMBER_OF_SETTINGS) tmp = fmt::format("{0:.34}{1:}", get->setting_strings[FNT_PATH], (get->setting_strings[FNT_PATH].size() > 34) ? "..." : ""); break; - case SHOW_BUTTON_OPTION: tmp = (get->setting_bools[Show_Buttons] ? getLangSTR(ON2) : getLangSTR(OFF2)); + case SHOW_BUTTON_OPTION: tmp = (get->setting_bools[Show_Buttons] ? getLangSTR(LANG_STR::ON2) : getLangSTR(LANG_STR::OFF2)); break; case BACKGROUND_MP3_OPTION: tmp = fmt::format("{0:.34}{1:}", get->setting_strings[MP3_PATH], (get->setting_strings[MP3_PATH].size() > 34) ? "..." : ""); break; default: break; } // append to vbo - add_text(l->vbo, main_font, tmp, &c, pen); - if (idx == 0 || (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE)) + l.vbo.add_text( main_font, tmp, c, pen); + if (idx == 0 || (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE)) { // print options info - switch (l->curr_item) + switch (l.curr_item) { case REBUILD_DB_OPTION: - print_option_info(l, pen, getLangSTR(REBUILD_DB_OPTION_DESC), "", true); - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + print_option_info(l, pen, getLangSTR(LANG_STR::REBUILD_DB_OPTION_DESC), "", true); + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option - switch (li->multi_sel.pos.y) + switch (li.multi_sel.pos.y) { case REBUILD_DLC: - print_option_info(l, pen, "", getLangSTR(REBUILD_DB_DESC_2), true); + print_option_info(l, pen, "", getLangSTR(LANG_STR::REBUILD_DB_DESC_2), true); break; case REBUILD_REACT: - print_option_info(l, pen, "", getLangSTR(REBUILD_DB_DESC_3), true); + print_option_info(l, pen, "", getLangSTR(LANG_STR::REBUILD_DB_DESC_3), true); break; case REBUILD_ALL: default: - print_option_info(l, pen, "", getLangSTR(REBUILD_DB_DESC_1), true); + print_option_info(l, pen, "", getLangSTR(LANG_STR::REBUILD_DB_DESC_1), true); break; } } break; case FONT_OPTION: if (numb_of_settings > NUMBER_OF_SETTINGS) - print_option_info(l, pen, getLangSTR(TTF_OPTION_DESC), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::TTF_OPTION_DESC), "", true); break; case HOME_MENU_OPTION: - print_option_info(l, pen, getLangSTR(HOME_MENU_OPTION_DESC), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::HOME_MENU_OPTION_DESC), "", true); break; case DUMPER_PATH_OPTION: - print_option_info(l, pen, getLangSTR(DUMPER_PATH_DESC), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::DUMPER_PATH_DESC), "", true); break; case CHECK_FOR_UPDATE_OPTION: - print_option_info(l, pen, getLangSTR(CHECK_UPDATE_INFO), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::CHECK_UPDATE_INFO), "", true); break; case SORT_BY_OPTION: - print_option_info(l, pen, getLangSTR(SORT_BY_OPTION_DESC), "", true); - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + print_option_info(l, pen, getLangSTR(LANG_STR::SORT_BY_OPTION_DESC), "", true); + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option - switch (li->multi_sel.pos.y) + switch (li.multi_sel.pos.y) { case SORT_TID: - print_option_info(l, pen, "", getLangSTR(SORT_ALPHA_TID_INFO), true); + print_option_info(l, pen, "", getLangSTR(LANG_STR::SORT_ALPHA_TID_INFO), true); break; case SORT_ALPHABET: - print_option_info(l, pen, "", getLangSTR(SORT_ALPHA_NAME_INFO), true); + print_option_info(l, pen, "", getLangSTR(LANG_STR::SORT_ALPHA_NAME_INFO), true); break; default: - print_option_info(l, pen, "", getLangSTR(SORT_RANDOM_INFO), true); + print_option_info(l, pen, "", getLangSTR(LANG_STR::SORT_RANDOM_INFO), true); break; } } break; case THEME_OPTION: - print_option_info(l, pen, getLangSTR(THEME_INSTALLER_INFO), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::THEME_INSTALLER_INFO), "", true); break; case SHOW_BUTTON_OPTION: - print_option_info(l, pen, getLangSTR(SHOW_BUTTON_OPTION_DESC), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::SHOW_BUTTON_OPTION_DESC), "", true); break; case OPEN_SHELLUI_MENU: - print_option_info(l, pen, getLangSTR(OPEN_DEBUG_MENU_DESC), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::OPEN_DEBUG_MENU_DESC), "", true); break; case BACKGROUND_MP3_OPTION: - print_option_info(l, pen, getLangSTR(BACKGROUND_MP3_OPTION_DESC), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::BACKGROUND_MP3_OPTION_DESC), "", true); break; case IF_PKG_INSTALLER_OPTION: - print_option_info(l, pen, getLangSTR(IF_PKG_INSTALLER_INFO), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::IF_PKG_INSTALLER_INFO), "", true); break; case POWER_CONTROL_OPTION: - print_option_info(l, pen, getLangSTR(POWER_CONTROL_INFO), "", true); + print_option_info(l, pen, getLangSTR(LANG_STR::POWER_CONTROL_INFO), "", true); break; case FUSE_IP_OPTION: - print_option_info(l, pen, getLangSTR(FUSE_OPTION_DESC), getLangSTR(FUSE_OPTION_DESC_1), true); + print_option_info(l, pen, getLangSTR(LANG_STR::FUSE_OPTION_DESC), getLangSTR(LANG_STR::FUSE_OPTION_DESC_1), true); break; default: break; @@ -937,65 +954,66 @@ static void layout_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) } } else - add_text(l->vbo, sub_font, tmp, &col, pen); + l.vbo.add_text( sub_font, tmp, col, pen); } /* default way to deal with text, for layout_t */ -static void hostapp_vapp_compose_text(layout_t* l, int idx, vec2* pen, bool save_text) +static void hostapp_vapp_compose_text(view_t vw, int idx, vec2& pen, bool save_text) { #if 0 log_info("%s %p, %d", __FUNCTION__, l, save_text); #endif - + layout_t &l = getLayoutbyView(vw); std::string tmp = ""; + GameStatus app_state = app_status.load(); vec4 c = col * .75f; // default indexing, item_t* AOS - item_t* li = &l->item_d[idx]; + item_t &li = l.item_d[idx]; - if (l != &ls_p) return; + if (vw != ITEM_PAGE) return; - vec2 bk = *pen; + vec2 bk = pen; bk.x += 10, bk.y += 50; vec2 sub_ops = { bk.x, bk.y }; - add_text(l->vbo, sub_font, li->name, &col, &bk); + l.vbo.add_text( sub_font, li.info.name, col, bk); /* special option */ - vec2 tp = *pen; + vec2 tp = pen; switch (idx) { - case LAUNCH_HA: - if(app_status != RESUMABLE) break; - if (!li->multi_sel.is_active){ - li->multi_sel.pos = (ivec4){0, 0, 0, 2}; - li->multi_sel.is_active = true; + case cf_ops::HOSTAPP_OPTS::LAUNCH_HA: + if(app_state != RESUMABLE) break; + if (!li.multi_sel.is_active){ + li.multi_sel.pos = (ivec4){0, 0, 0, 2}; + li.multi_sel.is_active = true; log_info("$$$$$ multi_sel is active"); } //set active for all above too - //li->multi_sel.is_active = true; - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + //li.multi_sel.is_active = true; + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option switch (idx){ - case LAUNCH_HA:{ - tmp = fmt::format("< {0:.20} >", control_app_get_str((GameStatus)(li->multi_sel.pos.y+1))); + case cf_ops::HOSTAPP_OPTS::LAUNCH_HA:{ + tmp = fmt::format("< {0:.20} >", control_app_get_str((GameStatus)(li.multi_sel.pos.y+1))); break; } } // compute rectangle position and size in px vec4 r = get_rect_from_index(idx, l, NULL); // start at panel position, move to center - tp = *pen; - tp.x = (l->bound_box.x + r.z / 2.) + 300.; + tp = pen; + tp.x = (l.bound_box.x + r.z / 2.) + 300.; // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs(main_font, tmp.c_str()); tp.x -= tl / 2.; - *pen = tp; + pen = tp; } else - tmp = getLangSTR(SELECT_INTERACT); + tmp = getLangSTR(LANG_STR::SELECT_INTERACT); break; } @@ -1003,32 +1021,32 @@ static void hostapp_vapp_compose_text(layout_t* l, int idx, vec2* pen, bool save if (tmp.size() > 3) { sub_ops.y -= 40; - add_text(l->vbo, main_font, tmp, &c, &sub_ops); + l.vbo.add_text( main_font, tmp, c, sub_ops); } - if (idx == 0 || (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE)) + if (idx == 0 || (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE)) { // print options info - switch (l->curr_item) + switch (l.curr_item) {//RESTORE_NOTICE - case REFRESH_HA: - print_option_info(l, pen, getLangSTR(REFRESH_HOSTAPP_DESC), "", false); + case cf_ops::HOSTAPP_OPTS::REFRESH_HA: + print_option_info(l, pen, getLangSTR(LANG_STR::REFRESH_HOSTAPP_DESC), "", false); break; - case LAUNCH_HA: - if (li->multi_sel.is_active && li->multi_sel.pos.x == FIRST_MULTI_LINE) + case cf_ops::HOSTAPP_OPTS::LAUNCH_HA: + if (li.multi_sel.is_active && li.multi_sel.pos.x == FIRST_MULTI_LINE) { // show '< X >' around Option - if (app_status == RESUMABLE) - print_option_info(l, pen, getLangSTR(GAME_CONTROL_INFO), getLangSTR(SUSPEND_IF), false); + if (app_state == RESUMABLE) + print_option_info(l, pen, getLangSTR(LANG_STR::GAME_CONTROL_INFO), getLangSTR(LANG_STR::SUSPEND_IF), false); else - print_option_info(l, pen, getLangSTR(CLOSE_OPEN_APP_INFO), "", false); + print_option_info(l, pen, getLangSTR(LANG_STR::CLOSE_OPEN_APP_INFO), "", false); } - else if (app_status == NO_APP_OPENED) - print_option_info(l, pen, getLangSTR(LAUNCH_INFO), getLangSTR(SUSPEND_IF), false); - else if (app_status == OTHER_APP_OPEN) - print_option_info(l, pen, getLangSTR(CLOSE_OPEN_APP_INFO), "", false); - else if (app_status == RESUMABLE) - print_option_info(l, pen, getLangSTR(GAME_CONTROL_INFO), getLangSTR(SUSPEND_IF), false); + else if (app_state == NO_APP_OPENED) + print_option_info(l, pen, getLangSTR(LANG_STR::LAUNCH_INFO), getLangSTR(LANG_STR::SUSPEND_IF), false); + else if (app_state == OTHER_APP_OPEN) + print_option_info(l, pen, getLangSTR(LANG_STR::CLOSE_OPEN_APP_INFO), "", false); + else if (app_state == RESUMABLE) + print_option_info(l, pen, getLangSTR(LANG_STR::GAME_CONTROL_INFO), getLangSTR(LANG_STR::SUSPEND_IF), false); break; default: @@ -1040,7 +1058,7 @@ static void hostapp_vapp_compose_text(layout_t* l, int idx, vec2* pen, bool save } /* - last review: + last review_menu: - correct positioning, in px - cache normalized rectangles - uses cached texture from atlas + UVs @@ -1050,91 +1068,93 @@ static void hostapp_vapp_compose_text(layout_t* l, int idx, vec2* pen, bool save left, icon, cf_game_option, download panels */ std::string path; -void GLES2_render_layout_v2(layout_t* l, int unused) +void GLES2_render_layout_v2(view_t vw) { - if (!l || !l->is_shown || !l->is_active) return; - // log_info("%s %p %d", __FUNCTION__, l->vbo, l->vbo_s); + layout_t &l = getLayoutbyView(vw); + if ( !l.is_shown || !l.is_active) return; + bool is_file_browser = (vw == FILE_BROWSER_LEFT || vw == FILE_BROWSER_RIGHT); + // log_info("%s %p %d", __FUNCTION__, l.vbo, l.vbo_s); int field_s, f_sele, rem, i_paged; - item_t *li = NULL; // layout item iterator vec2 p, pen, s, // item position and size, in px tp, // used for text position, in px b1 = { 30, 30 }, // border between rectangles, in px b2 = { 10, 10 }; // border between text and rectangle, in px vec4 r, // normalized (float) rectangle selection_box, - tv = 0.; // temporary vector //tv.xy = b1 * (l->fieldsize - 1.); + tv = 0.; // temporary vector //tv.xy = b1 * (l.fieldsize - 1.); // normalized custom color, like Settings vec4 c = (vec4){ 41., 41., 41., 256. } / 256.; - if (l == &ls_p || l == &setting_p) /* draw a bg rectangle from bound_box + borders */ + if (vw == ITEMzFLOW || vw == ITEM_PAGE || vw == ITEM_SETTINGS) /* draw a bg rectangle from bound_box + borders */ { - p = (vec2){ l->bound_box.x - b1.x, l->bound_box.y + b1.y }, - s = (vec2){ l->bound_box.z + b1.x * 2, -l->bound_box.w - b1.y * 2 }; + p = (vec2){ l.bound_box.x - b1.x, l.bound_box.y + b1.y }, + s = (vec2){ l.bound_box.z + b1.x * 2, -l.bound_box.w - b1.y * 2 }; s += p; r.xy = px_pos_to_normalized(&p), r.zw = px_pos_to_normalized(&s); // temporary vector: use for custom color, normalize 0.f-1.f tv = (vec4){ 41., 41., 100., 128. } / 256.; // gles render the single rectangle - ORBIS_RenderFillRects(USE_COLOR, &tv, &r, 1); + std::vector v; + v.push_back(r); + ORBIS_RenderFillRects(USE_COLOR, tv, v, 1); } - if (l == &ls_p && ls_p.curr_item == Game_Save_opt && skipped_first_X) + if (vw == ITEM_PAGE && ls_p.curr_item == cf_ops::REG_OPTS::GAME_SAVE && skipped_first_X) { vec4 cc = (vec4){41., 41., 41., 256.} / 256.; - draw_rect(&cc, 770., 800., 600., -320.); + draw_rect(cc, 770., 800., 600., -320.); if(ic > GL_NULL){ render_button(ic, 128., 228., 800., 600., 1.); } } - else if (l == &ls_p && ls_p.curr_item == Trainers_opt && skipped_first_X) + else if (vw == ITEM_PAGE && ls_p.curr_item == cf_ops::REG_OPTS::TRAINERS && skipped_first_X) { vec4 cc = (vec4){41., 41., 41., 256.} / 256.; - draw_rect(&cc, 770., 800., 600., -320.); + draw_rect(cc, 770., 800., 600., -320.); } - if (l != &fm_lp && l != &fm_rp) + if (!is_file_browser) { /* destroy VBO */ - if (l->vbo_s == ASK_REFRESH) + if (l.vbo_s == ASK_REFRESH) { - if (l->vbo) - vertex_buffer_delete(l->vbo), l->vbo = NULL; + l.vbo.clear(); } -} + } -if (!l->vbo) // we cleaned vbo ? +if (!l.vbo) // we cleaned vbo ? { - l->vbo = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); - l->vbo_s = EMPTY; + l.vbo = VertexBuffer("vertex:3f,tex_coord:2f,color:4f"); + l.vbo_s = EMPTY; } /* compute normalized rectangle we will draw, once! */ -if (l->vbo_s < CLOSED && (l != &fm_lp && l != &fm_rp)) +if (l.vbo_s < CLOSED && !is_file_browser) { - int curr_page = l->page_sel.x; + int curr_page = l.page_sel.x; // get global item index: first one for current page - int g_idx = curr_page * (l->fieldsize.x * l->fieldsize.y); + int g_idx = curr_page * (l.fieldsize.x * l.fieldsize.y); - if (!l->f_rect) - l->f_rect = (vec4*)calloc(l->fieldsize.x * l->fieldsize.y, sizeof(vec4)); + if (l.f_rect.empty()) + l.f_rect.resize(l.fieldsize.x * l.fieldsize.y); // draw the field, according to passed layout_t - for (int i = 0; i < l->f_size; i++) + for (int i = 0; i < l.f_size; i++) { int loop_idx = g_idx + i; - if (loop_idx > l->item_c - 1) break; + if (loop_idx > l.item_c - 1) break; // temporary vector: use to load borders and get UVs tv.xy = b1; // compute position and size in px, then fill normalized UVs r = get_rect_from_index(i, l, &tv); p = r.xy, s = r.zw; // swap r w/ p, s and reuse r below - p += l->bound_box.xy; // add origin: it's bound box position - p.y -= l->bound_box.w; // move back down by *minus* size.h(!) + p += l.bound_box.xy; // add origin: it's bound box position + p.y -= l.bound_box.w; // move back down by *minus* size.h(!) tp = p + b2; // update pen for text positioning tp.y += s.y; // relate b2 to BotLef of current rectangle s += p; // turn size into a second point @@ -1142,52 +1162,52 @@ if (l->vbo_s < CLOSED && (l != &fm_lp && l != &fm_rp)) r.xy = px_pos_to_normalized(&p), r.zw = px_pos_to_normalized(&s); r.yw = r.wy; // flip on Y axis - l->f_rect[i] = r; // save the normalized rectangle + l.f_rect[i] = r; // save the normalized rectangle // to refresh main header/title screen bool save_text = false; // if item is the selected one save frect for optional work - if (i == l->f_sele) + if (i == l.f_sele) { selection_box = r, save_text = true; } // texts: check for its vbo - if (l->vbo && l->vbo_s < CLOSED) + if (l.vbo && l.vbo_s < CLOSED) { /* draw over item: text pos, in px */ - if(l == &ls_p && !is_vapp(title_id)) - layout_compose_text(l, loop_idx, &tp, save_text); - else if (l == &ls_p && is_vapp(title_id)){ - if(title_id == APP_HOME_HOST_TID){ - hostapp_vapp_compose_text(l, loop_idx, &tp, save_text); + if(vw == ITEM_PAGE && !is_vapp(title_id.get())) + layout_compose_text(vw, loop_idx, tp, save_text); + else if (vw == ITEM_PAGE && is_vapp(title_id.get())){ + if(title_id.get() == APP_HOME_HOST_TID){ + hostapp_vapp_compose_text(vw, loop_idx, tp, save_text); } } else - layout_compose_text(l, loop_idx, &tp, save_text); + layout_compose_text(vw, loop_idx, tp, save_text); } } /// EOFor each item } /* use cached normalized f_rects */ -if (l->f_rect && (l != &fm_lp && l != &fm_rp)) +if (!l.f_rect.empty() && !is_file_browser) { // read from cached normalized rectangle for more work - selection_box = l->f_rect[l->f_sele]; + selection_box = l.f_rect[l.f_sele]; // basic - if (l == &ls_p || l == &setting_p) + if (vw == ITEMzFLOW || vw == ITEM_PAGE || vw == ITEM_SETTINGS) // draws the option boxes behind the text { // draw all the rectangle array by count, at once - ORBIS_RenderFillRects(USE_COLOR, &c, &l->f_rect[0], l->f_size); + ORBIS_RenderFillRects(USE_COLOR, c, l.f_rect, l.f_size); } } - if (l == &fm_lp || l == &fm_rp) + if (is_file_browser) { - field_s = l->fieldsize.x * l->fieldsize.y, - f_sele = l->item_sel.y * l->fieldsize.x + l->item_sel.x, - i_paged = l->page_sel.x * field_s; + field_s = l.fieldsize.x * l.fieldsize.y, + f_sele = l.item_sel.y * l.fieldsize.x + l.item_sel.x, + i_paged = l.page_sel.x * field_s; /* if last page can't fill the field... */ - rem = l->item_c - i_paged + /* first title */ 1; + rem = l.item_c - i_paged + /* first title */ 1; // decrease the field size! if (rem < field_s) field_s = rem; @@ -1199,14 +1219,14 @@ if (l->f_rect && (l != &fm_lp && l != &fm_rp)) // iterate items we are about to draw for (i = 0; i < field_s; i++) { - li = &l->item_d[i_paged + i]; + item_t &li = l.item_d[i_paged + i]; /* compute origin position for this item */ - s = (vec2){l->bound_box.z / l->fieldsize.x, - l->bound_box.w / l->fieldsize.y}; + s = (vec2){l.bound_box.z / l.fieldsize.x, + l.bound_box.w / l.fieldsize.y}; s -= b1; // consider borders! - p = (vec2){(float)(l->bound_box.x + (i % (int)l->fieldsize.x) * (s.x + b1.x /* px border */)), - (float)(l->bound_box.y - (i + 1) / l->fieldsize.x * (s.y + b1.y /* px border */))}; + p = (vec2){(float)(l.bound_box.x + (i % (int)l.fieldsize.x) * (s.x + b1.x /* px border */)), + (float)(l.bound_box.y - (i + 1) / l.fieldsize.x * (s.y + b1.y /* px border */))}; // .xy point additions: p += p_off.xy; // add offset: global displacing s += p; // turn size into a second point @@ -1216,11 +1236,13 @@ if (l->f_rect && (l != &fm_lp && l != &fm_rp)) vec4 test_r = (vec4){p.x, p.y, s.x, s.y}; // panel is active, highlight with filled rectangle - if (l->is_active) + if (l.is_active) { // custom color, like Settings vec4 c = (vec4){41., 41., 41., 256.} / 256.; // gles render the single iterated rectangle - ORBIS_RenderFillRects(USE_COLOR, &c, &r, 1); + std::vector rr; + rr.push_back(c); + ORBIS_RenderFillRects(USE_COLOR, c, rr, 1); // is item the selected one ? if (i == f_sele) @@ -1230,20 +1252,20 @@ if (l->f_rect && (l != &fm_lp && l != &fm_rp)) p1_pos = r.xy; // a VBO for pixelshader if (!test_v) - test_v = vbo_from_rect(&test_r); + test_v = vbo_from_rect(test_r); } } // draw outline rectangle for this item[i] - ORBIS_RenderDrawBox(USE_COLOR, &grey, &r); + ORBIS_RenderDrawBox(USE_COLOR, grey, r); // texts: check for its vbo - if (l->vbo && l->vbo_s < CLOSED) + if (l.vbo && l.vbo_s < CLOSED) { // build filepath std::string out; /* draw over item */ - out = fmt::format("{0:.20}{1:}", li->id, li->id.size() > 20 ? "..." : ""); + out = fmt::format("{0:.30}{1:}", li.info.id, li.info.id.size() > 30 ? "..." : ""); // update pen @@ -1253,49 +1275,57 @@ if (l->f_rect && (l != &fm_lp && l != &fm_rp)) // the very first reserved_index is the title and retative path if (!(i_paged + i)) { // append to VBO - add_text(l->vbo, titl_font, out, &col, &pen); + l.vbo.add_text( titl_font, out, col, pen); vec4 c = col * .75f; - add_text(l->vbo, sub_font, fmt::format(", {} items", li->token_c), &c, &pen); + l.vbo.add_text( sub_font, fmt::format(", {} items", li.count.token_c), c, pen); } else { // append to VBO - add_text(l->vbo, sub_font, out, &col, &pen); + + li.flags.is_all_pkg_enabled ? pen.x += 64, pen.y += 9 : pen.x; + l.vbo.add_text( sub_font, out, col, pen); + //log_info("%s %s", li.info.id.c_str(), li.is_all_pkg_enabled ? "enabled" : "disabled"); + path = (l.item_d[0].info.id.size() > 1 ? l.item_d[0].info.id : "") + "/" + li.info.id; + if(!li.flags.is_all_pkg_enabled) + get_stat_from_file(out, path); + else{ + out = getLangSTR(LANG_STR::INSTALL_ALL_PKGS_DEC); + pen.y -= 9; + } - //log_info("%s %s", buff.str().c_str(), l->item_d[0].id.c_str()); - path = (l->item_d[0].id.size() > 1 ? l->item_d[0].id : "") + "/" + li->id; - get_stat_from_file(out, path); // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs(main_font, out.c_str()); // we know 'tl' now, right align - pen.x = p.x + l->bound_box.z - tl - b2.x - 22; + pen.x = p.x + l.bound_box.z - tl - b2.x - 22; // append to VBO vec4 c = col * .75f; - add_text(l->vbo, main_font, out, &c, &pen); + l.vbo.add_text( main_font, out, c, pen); } } } r = selection_box; // panel is active, show selector - if (l->is_active){ + if (l.is_active){ // render new selector - ORBIS_RenderFillRects(USE_COLOR, &grey, &r, 1); + std::vector sl; + sl.push_back(r); + ORBIS_RenderFillRects(USE_COLOR, grey, sl, 1); // gles render the glowing blue box - ORBIS_RenderDrawBox(USE_UTIME, &sele, &r); + ORBIS_RenderDrawBox(USE_UTIME, sele, r); } } // close text vbo - if (l->vbo_s < CLOSED){ l->vbo_s = CLOSED;refresh_atlas(); } + if (l.vbo_s < CLOSED){ l.vbo_s = CLOSED;refresh_atlas(); } /* elaborate more on the selected item: */ r = selection_box; // panel is active, show selector - if (l == active_p && (l != &fm_lp && l != &fm_rp)) + if ( !is_file_browser ) { /* also, different panel have different selector */ - GLES2_render_selector(l, &r); + GLES2_render_selector(vw, r); } - if(l->vbo) - ftgl_render_vbo(l->vbo, NULL); + l.vbo.render_vbo(NULL); // log_info("%s %p", __FUNCTION__, l); } @@ -1308,125 +1338,123 @@ static void bound_check(int* idx, int Min, int Max) /* update field_size for layout: eventually reduce field_size to match remaining item count */ -void layout_update_fsize(layout_t* l) +void layout_update_fsize(layout_t& l) { - l->f_size = l->fieldsize.x * l->fieldsize.y; - int count = l->item_c; + l.f_size = l.fieldsize.x * l.fieldsize.y; + int count = l.item_c; // reduce field_size - if (count < l->f_size) + if (count < l.f_size) { - l->f_size = count; - l->vbo_s = ASK_REFRESH; + l.f_size = count; + l.vbo_s = ASK_REFRESH; } } int setting_on = -1; extern bool is_sub_enabled; /* each keypress updates linear and vector indexes */ -void layout_update_sele(layout_t* l, int movement) +void layout_update_sele(layout_t& l, int movement) { // unconditionally ask to refresh! - l->vbo_s = ASK_REFRESH; + l.vbo_s = ASK_REFRESH; // check current item: is Multi-Sel? - if (l->item_d[l->curr_item].multi_sel.is_active) + if (l.item_d[l.curr_item].multi_sel.is_active) { - //log_info("Multi-Sel status: %d", l->item_d[l->curr_item].multi_sel.pos.x); - if (l->item_d[l->curr_item].multi_sel.pos.x == FIRST_MULTI_LINE) // EDITABLE + //log_info("Multi-Sel status: %d", l.item_d[l.curr_item].multi_sel.pos.x); + if (l.item_d[l.curr_item].multi_sel.pos.x == FIRST_MULTI_LINE) // EDITABLE { // pass movement to Multi-Sel - int res = l->item_d[l->curr_item].multi_sel.pos.y += movement; + int res = l.item_d[l.curr_item].multi_sel.pos.y += movement; - bound_check(&res, l->item_d[l->curr_item].multi_sel.pos.z, - l->item_d[l->curr_item].multi_sel.pos.w - 1); + bound_check(&res, l.item_d[l.curr_item].multi_sel.pos.z, + l.item_d[l.curr_item].multi_sel.pos.w - 1); // copy back result into destination! - l->item_d[l->curr_item].multi_sel.pos.y = res; - //printf("%d\n", l->item_d[ l->curr_item ].multi_sel.pos.y); + l.item_d[l.curr_item].multi_sel.pos.y = res; + //printf("%d\n", l.item_d[ l.curr_item ].multi_sel.pos.y); return; } } // update all related to this one! - int idx = l->curr_item + movement; + int idx = l.curr_item + movement; - // if (idx < 0) idx = l->item_c - 1, l->vbo_s = ASK_REFRESH; - // if (idx > l->item_c - 1) idx = 0, l->vbo_s = ASK_REFRESH; - bound_check(&idx, 0, l->item_c - 1); + // if (idx < 0) idx = l.item_c - 1, l.vbo_s = ASK_REFRESH; + // if (idx > l.item_c - 1) idx = 0, l.vbo_s = ASK_REFRESH; + bound_check(&idx, 0, l.item_c - 1); // update vector and linear index - l->item_sel = (ivec2){ idx % l->fieldsize.x, - idx / l->fieldsize.x }; - l->curr_item = idx; + l.item_sel = (ivec2){ idx % l.fieldsize.x, + idx / l.fieldsize.x }; + l.curr_item = idx; // log_info("l %i\n", idx); - l->f_size = l->fieldsize.x * l->fieldsize.y; + l.f_size = l.fieldsize.x * l.fieldsize.y; - if (l->f_size) + if (l.f_size) { - l->f_sele = idx % l->f_size; + l.f_sele = idx % l.f_size; } else // guard the div by zero! { - l->f_sele = 0, - l->page_sel = (ivec2)(0); + l.f_sele = 0, + l.page_sel = (ivec2)(0); } layout_update_fsize(l); } int numb_of_settings = NUMBER_OF_SETTINGS; -void GLES2_render_list(enum view_t v) +void GLES2_render_list(view_t v) { - layout_t* l = NULL; -// + layout_t &l = getLayoutbyView(v); + bool is_file_manager = (v == FILE_BROWSER_LEFT || v == FILE_BROWSER_RIGHT); + if (v == ITEM_PAGE) { - l = &ls_p; - if (!l->is_active || old_status != app_status) + if (!l.is_active || old_status != app_status.load()) { - log_info("init layout for %s", title_id.c_str()); - if(!is_vapp((title_id))){ - GLES2_layout_init(10, l); - l->bound_box = (vec4){ 120, resolution.y - 200, 620, 600 }; - l->fieldsize = (ivec2){ 2, 5 }; + log_info("init layout for %s", title_id.get().c_str()); + if(!is_vapp((title_id.get()))){ + GLES2_layout_init(10, ls_p); + l.bound_box = (vec4){ 120, resolution.y - 200, 620, 600 }; + l.fieldsize = (ivec2){ 2, 5 }; } - else if(title_id == APP_HOME_HOST_TID){ + else if(title_id.get() == APP_HOME_HOST_TID){ GLES2_layout_init(2, l); - //l->bound_box = (vec4){ 120, resolution.y - 200, 620, 100 }; - //l->fieldsize = (ivec2){ 2, 1 }; - l->bound_box = (vec4){ 120, resolution.y - 200, 620, 600 }; - l->fieldsize = (ivec2){ 2, 5 }; + //l.bound_box = (vec4){ 120, resolution.y - 200, 620, 100 }; + //l.fieldsize = (ivec2){ 2, 1 }; + l.bound_box = (vec4){ 120, resolution.y - 200, 620, 600 }; + l.fieldsize = (ivec2){ 2, 5 }; } - switch (app_status) + switch (app_status.load()) { case RESUMABLE: - gm_p_text.at(0) = getLangSTR(GAME_CONTROLS); + gm_p_text.at(0) = getLangSTR(LANG_STR::GAME_CONTROLS); break; case APP_NA_STATUS: case NO_APP_OPENED: - gm_p_text.at(0) = getLangSTR(LAUNCH_GAME); + gm_p_text.at(0) = getLangSTR(LANG_STR::LAUNCH_GAME); break; case OTHER_APP_OPEN: - gm_p_text.at(0) = getLangSTR(CLOSE_GAME); + gm_p_text.at(0) = getLangSTR(LANG_STR::CLOSE_GAME); break; } layout_fill_item_from_list(l, gm_p_text); - active_p = l; - log_info("app_status: %i | old_status: %i", app_status, old_status); - old_status = app_status; // update status + log_info("app_status: %i | old_status: %i", app_status.load(), old_status); + old_status = app_status.load(); // update status } } else if (v == ITEM_SETTINGS) { - l = &setting_p; - if (!l->is_active) { + if (!setting_p.is_active) { GLES2_layout_init(numb_of_settings, l); //X Y | W , H - l->bound_box = (vec4){resolution.y / 2, resolution.y - 230, (numb_of_settings > NUMBER_OF_SETTINGS) ? 1200 : 800, 600}; + l.bound_box = (vec4){resolution.y / 2, resolution.y - 230, (numb_of_settings > NUMBER_OF_SETTINGS) ? 1200 : 800, 600}; // First: amount in 1 column, 2nd: Amount allowed in 1 column? - l->fieldsize = (ivec2){(numb_of_settings > NUMBER_OF_SETTINGS) ? 3 : 2, 6 }; + l.fieldsize = (ivec2){(numb_of_settings > NUMBER_OF_SETTINGS) ? 3 : 2, 6 }; layout_fill_item_from_list(l, ITEMZ_SETTING_TEXT); } } - else if (v == FILE_BROWSER){ + else if (is_file_manager){ vec2 size = {(resolution.x - 50 * 3) / 2, // we draw two coloumns: L/R, so 3 (*50px) border (resolution.y - 50 * 2)}; // we center whole rectangle, so 2 (*50px) border @@ -1441,12 +1469,12 @@ void GLES2_render_list(enum view_t v) } - vertex_buffer_delete(fm_lp.vbo), fm_lp.vbo = NULL; - vertex_buffer_delete(fm_rp.vbo), fm_rp.vbo = NULL; + fm_lp.vbo.clear(); + fm_rp.vbo.clear(); fm_lp.item_d.clear(), fm_rp.item_d.clear(); fm_rp.fs_is_active = fm_lp.fs_is_active = fm_lp.is_shown = fm_rp.is_shown = fm_lp.is_active = fm_rp.is_active = false; - active_p = fs_ret.last_layout; - if (title_vbo) vertex_buffer_delete(title_vbo), title_vbo = NULL; + //active_p = fs_ret.last_layout; + title_vbo.clear(); fs_ret.status = FS_NOT_STARTED; return; } @@ -1467,57 +1495,52 @@ void GLES2_render_list(enum view_t v) else index_items_from_dir_v2(FS_START_2, fm_rp.item_d); - fm_lp.item_c = fm_lp.item_d[0].token_c, fm_rp.item_c = fm_rp.item_d[0].token_c; + fm_lp.item_c = fm_lp.item_d[0].count.token_c, fm_rp.item_c = fm_rp.item_d[0].count.token_c; fm_rp.fieldsize = fm_lp.fieldsize = (ivec2){1, FM_ENTRIES}; - fm_rp.is_shown = fm_lp.is_shown = fm_lp.is_active = true, fm_rp.is_active = true; - fm_lp.fs_is_active = true, fm_rp.fs_is_active = false; - active_p = &fm_lp; + fm_rp.is_shown = fm_rp.is_active = true, + fm_lp.is_shown = fm_lp.is_active = true; + fm_lp.fs_is_active = true, fm_rp.fs_is_active = true; + //active_p = fm_lp; } - GLES2_render_layout_v2(&fm_lp, 0); - GLES2_render_layout_v2(&fm_rp, 0); + GLES2_render_layout_v2(FILE_BROWSER_LEFT); + GLES2_render_layout_v2(FILE_BROWSER_RIGHT); } + // v_curr = v; - if (l && v != FILE_BROWSER){ - l->is_shown = true; - GLES2_render_layout_v2(l, 0); + if (!is_file_manager){ + l.is_shown = true; + GLES2_render_layout_v2(v); } // we can also free data once vbo is done - // destroy_item_t(&l->item_d); + // destroy_item_t(&l.item_d); } +/*-------------------- global variables ---*/ + // ---------------------------------------------------------------- display --- -void pixelshader_render(GLuint program_i, vertex_buffer_t* vbo, vec2* req_size) +void pixelshader_render() { - curr_Program = mz_shader; - - if (program_i) curr_Program = shader; // just one for now - + curr_Program = shader; glUseProgram(curr_Program); { // enable alpha channel glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // notify shader about screen size - if (req_size) - glUniform2f(glGetUniformLocation(curr_Program, "resolution"), req_size->x, req_size->y); - else - glUniform2f(glGetUniformLocation(curr_Program, "resolution"), resolution2.x, resolution2.y); + + glUniform2f(glGetUniformLocation(curr_Program, "resolution"), resolution2.x, resolution2.y); // mouse position and actual cumulative time glUniform2f(glGetUniformLocation(curr_Program, "mouse"), p1_pos.x, p1_pos.y); glUniform1f(glGetUniformLocation(curr_Program, "time"), u_t); // notify shader about elapsed time // ft-gl style: MVP - glUniformMatrix4fv(glGetUniformLocation(curr_Program, "model"), 1, 0, model.data); - glUniformMatrix4fv(glGetUniformLocation(curr_Program, "view"), 1, 0, view.data); - glUniformMatrix4fv(glGetUniformLocation(curr_Program, "projection"), 1, 0, projection.data); - // draw whole VBO items array: fullscreen rectangle + glUniformMatrix4fv(glGetUniformLocation(curr_Program, "model"), 1, 0, model_menu.data); + glUniformMatrix4fv(glGetUniformLocation(curr_Program, "view"), 1, 0, view_menu.data); + glUniformMatrix4fv(glGetUniformLocation(curr_Program, "projection"), 1, 0, projection_menu.data); - if (vbo) - vertex_buffer_render(vbo, GL_TRIANGLES); - else - vertex_buffer_render(rects_buffer, GL_TRIANGLES); + //vertex_buffer_render2(rects_buffer, GL_TRIANGLES); + rects_buffer.render(GL_TRIANGLES); glDisable(GL_BLEND); } @@ -1528,7 +1551,7 @@ void pixelshader_render(GLuint program_i, vertex_buffer_t* vbo, vec2* req_size) static void reshape(int width, int height) { glViewport(0, 0, width, height); - mat4_set_orthographic(&projection, 0, width, 0, height, -1, 1); + mat4_set_orthographic(&projection_menu, 0, width, 0, height, -1, 1); } // ------------------------------------------------------ from source shaders --- @@ -1536,6 +1559,7 @@ static void reshape(int width, int height) static GLuint CreateProgramFE(int program_num, std::string path = "") { GLuint programID; + const char* data = NULL; switch (program_num) { @@ -1557,10 +1581,10 @@ static GLuint CreateProgramFE(int program_num, std::string path = "") case 5: log_debug("Next (LOADING)"); programID = -1; - const char* data = (const char*)orbisFileGetFileContent(path.c_str()); + data = (const char*)orbisFileGetFileContent(path.c_str()); if(data == NULL){ log_debug("Error: can't load shader.bin"); - ani_notify(NOTIFI_WARNING, "Unable to load Background Shader", "No One should see this"); + ani_notify(NOTIFI::WARNING, "Unable to load Background Shader", "No One should see this"); break; } else{ @@ -1568,73 +1592,43 @@ static GLuint CreateProgramFE(int program_num, std::string path = "") free((void*)data); } break; + default: + programID = BuildProgram(p_vs1, p_fs2, p_vs1_length, p_fs2_length); + break; } - if (!programID) { log_error("program creation failed!, switch: %i", program_num); sleep(2); } return programID; } -/* =========================================================================== */ -// get a vbo from rectangle, in px coordinates -vertex_buffer_t* vbo_from_rect(vec4* rect) -{ - vertex_buffer_t* vbo = vertex_buffer_new("vertex:3f,color:4f"); - vec4 color = { 1, 0, 0, 1 }; - float r = color.r, g = color.g, b = color.b, a = color.a; - /* VBO is setup as: "vertex:3f, color:4f */ - vertex_t vtx[4] = { { rect->x, rect->y, 0, r,g,b,a }, - { rect->x, rect->w, 0, r,g,b,a }, - { rect->z, rect->w, 0, r,g,b,a }, - { rect->z, rect->y, 0, r,g,b,a } }; - // two triangles: 2 * 3 vertex - GLuint idx[6] = { 0, 1, 2, 0, 2, 3 }; - vertex_buffer_push_back(vbo, vtx, 4, idx, 6); - - return vbo; -} - // ------------------------------------------------------------------- init --- + +typedef struct { + float x, y, z; // position (3f) + float r, g, b, a; // color (4f) +} pixel_vertex_t; + void pixelshader_init(int width, int height) { - resolution2 = (vec2){(float)width, (float)height}; - rects_buffer = vertex_buffer_new("vertex:3f,color:4f"); + resolution2 = (vec2){ width, height }; + rects_buffer = VertexBuffer("vertex:3f,color:4f"); vec4 color = { 1, 0, 0, 1 }; float r = color.r, g = color.g, b = color.b, a = color.a; - -#define TEST_1 (0) - -#if TEST_1 - #warning "splitted rects pixelshader" - vec2 pen = { 100, 100 }; - for (int i = 0; i < 10; ++i) - { - int x0 = (int)(pen.x + i * 2); - int y0 = (int)(pen.y + 20); - int x1 = (int)(x0 + 64); - int y1 = (int)(y0 - 64); -#else - // #warning "one fullscreen pixelshader" - int x0 = (int)(0); + int x0 = (int)(0); int y0 = (int)(0); int x1 = (int)(width); int y1 = (int)(height); -#endif /* VBO is setup as: "vertex:3f, color:4f */ - vertex_t vtx[4] = {{(float)x0, (float)y0, 0, r, g, b, a}, - {(float)x0, (float)y1, 0, r, g, b, a}, - {(float)x1, (float)y1, 0, r, g, b, a}, - {(float)x1, (float)y0, 0, r, g, b, a}}; - // two triangles: 2 * 3 vertex - GLuint idx[6] = {0, 1, 2, 0, 2, 3}; - vertex_buffer_push_back(rects_buffer, vtx, 4, idx, 6); + pixel_vertex_t vtx[4] = { { x0,y0,0, r,g,b,a }, + { x0,y1,0, r,g,b,a }, + { x1,y1,0, r,g,b,a }, + { x1,y0,0, r,g,b,a } }; + // two triangles: 2 * 3 vertex + GLuint idx[6] = { 0, 1, 2, 0, 2, 3 }; + //vertex_buffer_push_back(rects_buffer, vtx, 4, idx, 6); + rects_buffer.push_back(vtx, 4, idx, 6); -#if TEST_1 - pen += (vec2) { 72., -32. }; - color.g -= i * 0.1; // to show some difference: less green - } -#endif /* compile, link and use shader set->using_sb */ if(mz_shader == GL_NULL) @@ -1653,16 +1647,16 @@ else if ((!get->setting_bools[has_image] && !get->setting_bools[using_sb] && sha log_info("[%s] program_id=%d (0x%08x)", __FUNCTION__, mz_shader, mz_shader); log_info("[%s] program_id=%d (0x%08x)", __FUNCTION__, shader, shader); -mat4_set_identity(&projection); -mat4_set_identity(&model); -mat4_set_identity(&view); +mat4_set_identity(&projection_menu); +mat4_set_identity(&model_menu); +mat4_set_identity(&view_menu); reshape(width, height); } void pixelshader_fini(void) { - vertex_buffer_delete(rects_buffer); rects_buffer = NULL; + rects_buffer.clear(); if (shader) glDeleteProgram(shader), shader = 0; if (mz_shader) glDeleteProgram(mz_shader), mz_shader = 0; diff --git a/itemzflow/source/GLES2_panel.cpp b/itemzflow/source/GLES2_panel.cpp index ecebc2b..3639db9 100644 --- a/itemzflow/source/GLES2_panel.cpp +++ b/itemzflow/source/GLES2_panel.cpp @@ -23,12 +23,15 @@ #include "GLES2_common.h" extern char selected_text[64]; +extern GLuint glsl_Program[4]; extern vec2 resolution; extern int v_curr; extern GLuint bg_tex; /* common text */ -static vertex_buffer_t *t_vbo = NULL; // for sysinfo +extern GLuint debug_icon; + +static VertexBuffer t_vbo; // for sysinfo vec3 clk = (vec3){ 0., 0, 15 }; // timer(now, prev, limit in seconds) @@ -37,7 +40,7 @@ double dfp_hdd = -1., dfp_ext = -1., dfp_fmem = -1.; -static std::atomic_bool refresh_clk = true; +static std::atomic_bool refresh_clk(true); /* upper right sytem_info, updates internally each 'clk.z' seconds */ #define SHOW_FMEM 0 @@ -68,22 +71,22 @@ void switchTextPositions(vec2 &pen, vec2 origin) { log_debug("Current corner: %d", currentCorner); // Add text to the current corner - tmp = fmt::format("{0:} {1:x}, {2:d}°C, {3:x}", getLangSTR(SYS_VER), ps4_fw_version(), numb, fmem); + tmp = fmt::format("{0:} {1:x}, {2:d}°C, {3:x}", getLangSTR(LANG_STR::SYS_VER), ps4_fw_version(), numb, fmem); // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs( main_font, tmp.c_str() ); // we know 'tl' now, right align pen.y -= 32; pen.x = corners[currentCorner].x; // fill the vbo - add_text( t_vbo, main_font, tmp, &c, &pen); - tmp = fmt::format("{0:}: {1:}", getLangSTR(ITEMZ_VER), completeVersion); + t_vbo.add_text( main_font, tmp, c, pen); + tmp = fmt::format("{0:}: {1:}", getLangSTR(LANG_STR::ITEMZ_VER), completeVersion); // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs( main_font, tmp.c_str() ); // we know 'tl' now, right align pen.y -= 32; pen.x = corners[currentCorner].x; // fill the vbo - add_text( t_vbo, main_font, tmp, &c, &pen); + t_vbo.add_text( main_font, tmp, c, pen); pen.y -= 32; pen.x = corners[currentCorner].x; texture_font_load_glyphs(sub_font, get->setting_strings[THEME_NAME].c_str()); @@ -92,33 +95,46 @@ void switchTextPositions(vec2 &pen, vec2 origin) { if(detail_size > 35) pen.x -= tl; } - add_text(t_vbo, sub_font, get->setting_strings[THEME_NAME], &c, &pen); + t_vbo.add_text( sub_font, get->setting_strings[THEME_NAME], c, pen); texture_font_load_glyphs(sub_font, get->setting_strings[THEME_AUTHOR].c_str()); pen.x += 12; - add_text(t_vbo, sub_font, get->setting_strings[THEME_AUTHOR], &c, &pen); + t_vbo.add_text( sub_font, get->setting_strings[THEME_AUTHOR], c, pen); texture_font_load_glyphs(sub_font, get->setting_strings[THEME_VERSION].c_str()); pen.x += 12; - add_text(t_vbo, sub_font, get->setting_strings[THEME_VERSION], &c, &pen); + t_vbo.add_text( sub_font, get->setting_strings[THEME_VERSION], c, pen); pen.y -= 40; pen.x = corners[currentCorner].x; } - +//float debug_x = 55.; void GLES2_Draw_sysinfo(bool is_idle) { uint32_t numb = 70; size_t fmem = 1000000; + bool is_file_manager = (v_curr == FILE_BROWSER_LEFT || v_curr == FILE_BROWSER_RIGHT); + + if (!is_idle && is_file_manager){ + if(right_page_pos < 10 && is_all_pkg_enabled(0)){ + render_button(debug_icon, 64., 64., 990., 860., 1); + //log_debug("rendering debug icon"); + } + if(left_page_pos < 10 && is_all_pkg_enabled(1)){ + render_button(debug_icon, 64., 64., 55., 860., 1); + //log_debug("rendering debug icon"); + } + // log_debug("left_page_pos = %d, right_page_pos = %d is_all_pkg_enabled %i", left_page_pos, right_page_pos, is_all_pkg_enabled(1)); + } std::string usb = fmt::format("/mnt/usb{}/", usbpath()); std::string tmp; if(is_idle){ /* background image, or pixelshader */ if (bg_tex == GL_NULL && !get->setting_bools[has_image]) - pixelshader_render(2, NULL, NULL); // use PS_symbols shader + pixelshader_render(); // use PS_symbols shader else{ /* bg image */ vec4 r = (vec4){-1., -1., 1., 1.}; // see if its even loaded if (bg_tex > GL_NULL) - on_GLES2_Render_icon(USE_COLOR, bg_tex, 2, &r, NULL); + on_GLES2_Render_icon(USE_COLOR, bg_tex, 2, r, NULL); } } // update time @@ -127,7 +143,7 @@ void GLES2_Draw_sysinfo(bool is_idle) // time limit passed! if(clk.x / clk.z > 1. || refresh_clk.load()) // by time or requested { // destroy VBO - if(t_vbo) vertex_buffer_delete(t_vbo), t_vbo = NULL; + t_vbo.clear(); // reset clock clk.x = 0., refresh_clk = false; } @@ -137,7 +153,7 @@ void GLES2_Draw_sysinfo(bool is_idle) vec2 pen, origin = resolution, border = (vec2)(50.); - t_vbo = vertex_buffer_new( "vertex:3f,tex_coord:2f,color:4f" ); + t_vbo = VertexBuffer( "vertex:3f,tex_coord:2f,color:4f" ); // add border, in px origin -= border; pen = origin; @@ -150,15 +166,15 @@ void GLES2_Draw_sysinfo(bool is_idle) std::string ip; if(!get_ip_address(ip)){ texture_font_load_glyphs( sub_font, "0.0.0.0"); - add_text(t_vbo, sub_font, "0.0.0.0", &col, &pen); + t_vbo.add_text( sub_font, "0.0.0.0", col, pen); } else{ texture_font_load_glyphs( sub_font, ip.c_str()); - add_text(t_vbo, sub_font, ip, &col, &pen); + t_vbo.add_text(sub_font, ip, col, pen); } #else texture_font_load_glyphs( sub_font, "0.0.0.0"); - add_text(t_vbo, sub_font, "0.0.0.0", &col, &pen); + t_vbo.add_text( sub_font, "0.0.0.0", col, pen); #endif refresh_atlas(); return; @@ -175,38 +191,37 @@ void GLES2_Draw_sysinfo(bool is_idle) // we know 'tl' now, right align pen.x -= tl; // fill the vbo - add_text( t_vbo, sub_font, tmp, &col, &pen); + t_vbo.add_text( sub_font, tmp, col, pen); //we dont want all the text on FS view - if (!is_idle && v_curr == FILE_BROWSER){ - if (t_vbo) - ftgl_render_vbo(t_vbo, NULL); + if (!is_idle && is_file_manager){ + t_vbo.render_vbo(NULL); return; } #if defined(__ORBIS__) sceKernelGetCpuTemperature(&numb); if (numb > 90) - ani_notify(NOTIFI_WARNING, getLangSTR(REALLY_HOT), getLangSTR(REALLY_HOT_2)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::REALLY_HOT), getLangSTR(LANG_STR::REALLY_HOT_2)); sceKernelAvailableFlexibleMemorySize(&fmem); #endif - tmp = fmt::format("{0:} {1:x}, {2:d}°C, {3:x}", getLangSTR(SYS_VER), ps4_fw_version(), numb, fmem); + tmp = fmt::format("{0:} {1:x}, {2:d}°C, {3:x}", getLangSTR(LANG_STR::SYS_VER), ps4_fw_version(), numb, fmem); // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs( main_font, tmp.c_str() ); // we know 'tl' now, right align pen.x = origin.x - tl, pen.y -= 32; // fill the vbo - add_text( t_vbo, main_font, tmp, &c, &pen); + t_vbo.add_text( main_font, tmp, c, pen); - tmp = fmt::format("{0:}: {1:}", getLangSTR(ITEMZ_VER), completeVersion); + tmp = fmt::format("{0:}: {1:}", getLangSTR(LANG_STR::ITEMZ_VER), completeVersion); // we need to know Text_Length_in_px in advance, so we call this: texture_font_load_glyphs( main_font, tmp.c_str() ); // we know 'tl' now, right align pen.x = origin.x - tl, pen.y -= 32; // fill the vbo - add_text( t_vbo, main_font, tmp, &c, &pen); + t_vbo.add_text( main_font, tmp, c, pen); // draw theme info below if (v_curr == ITEMzFLOW) { @@ -231,7 +246,7 @@ void GLES2_Draw_sysinfo(bool is_idle) pen.y += 32; #endif // fill the vbo - add_text(t_vbo, sub_font, getLangSTR(STORAGE), &col, &pen); + t_vbo.add_text( sub_font, getLangSTR(LANG_STR::STORAGE), col, pen); // new line pen.x = 26, pen.y -= 32; @@ -239,11 +254,11 @@ void GLES2_Draw_sysinfo(bool is_idle) #if defined(__ORBIS__) if (usbpath() != -1) #else -//log_info("%s", tmp.c_str()); +//log_vinfo("%s", tmp.c_str()); if (if_exists(homedir)) #endif { - add_text(t_vbo, main_font, tmp, &c, &pen); + t_vbo.add_text( main_font, tmp, c, pen); // a new line pen.x = 26, pen.y -= 32; @@ -251,7 +266,7 @@ void GLES2_Draw_sysinfo(bool is_idle) dfp_ext = df("/user", tmp); - add_text(t_vbo, main_font, tmp, &c, &pen); + t_vbo.add_text( main_font, tmp, c, pen); // update FMEM dfp_fmem = (1. - (double)fmem / (double)0x8000000) * 100.; @@ -261,7 +276,7 @@ void GLES2_Draw_sysinfo(bool is_idle) //draw music info on the right corner of a 1080p screen pen = (vec2){ 26, 20 }; // fill the vbo - add_text(t_vbo, main_font, fmt::format("{0:}{1:.35}{2:} - {3:.30}{4:} ({5:}/{6:})", (get->lang == 1 || get->lang == 18) ? "Playing: " : "", current_song.song_title, current_song.song_title.size() > 35 ? "..." : "" ,current_song.song_artist, current_song.song_artist.size() > 35 ? "..." : "", current_song.total_files > 1 ? current_song.current_file-1 : 1, current_song.total_files), &col, &pen); + t_vbo.add_text( main_font, fmt::format("{0:}{1:.35}{2:} - {3:.30}{4:} ({5:}/{6:})", (get->lang == 1 || get->lang == 18) ? "Playing: " : "", current_song.song_title, current_song.song_title.size() > 35 ? "..." : "" ,current_song.song_artist, current_song.song_artist.size() > 35 ? "..." : "", current_song.total_files > 1 ? current_song.current_file-1 : 1, current_song.total_files), col, pen); } #endif @@ -272,6 +287,9 @@ void GLES2_Draw_sysinfo(bool is_idle) } if(!is_idle && v_curr == ITEMzFLOW){ + +//DrawWhiteStar(); +// #if SHOW_FMEM==1 /* FMEM: draw filling color bar, by percentage */ vec4 r = (vec4) { -.975, -.950, -.505, -.955 }; @@ -279,15 +297,16 @@ if(!is_idle && v_curr == ITEMzFLOW){ ORBIS_RenderDrawBox(USE_COLOR, &grey, &r); GLES2_DrawFillingRect(&r, &white, &dfp_fmem); #else - vec4 r = (vec4){ -.975, -.900, -.505, -.905 }; + std::vector r; + r.push_back((vec4){-.975, -.900, -.505, -.905}); // gles render the frect - ORBIS_RenderFillRects(USE_COLOR, &grey, &r, 1); + ORBIS_RenderFillRects(USE_COLOR, grey, r, 1); /* draw filling color bar, by percentage */ - GLES2_DrawFillingRect(&r, &sele, &dfp_ext); + GLES2_DrawFillingRect(r, sele, dfp_ext); #endif } // texts out of VBO - if(t_vbo) ftgl_render_vbo(t_vbo, NULL); + t_vbo.render_vbo(NULL); } void GLES2_refresh_sysinfo(void) diff --git a/itemzflow/source/GLES2_rects.cpp b/itemzflow/source/GLES2_rects.cpp index 957e1a3..945dba5 100644 --- a/itemzflow/source/GLES2_rects.cpp +++ b/itemzflow/source/GLES2_rects.cpp @@ -6,16 +6,17 @@ 2020, masterzorag */ -#include -#if defined(__ORBIS__) +#include "defines.h" +#include "GLES2_common.h" +#include "utils.h" +#ifdef __ORBIS__ #include "shaders.h" #else #include "pc_shaders.h" #endif -#include /* glsl programs */ -static GLuint glsl_Program[3]; +static GLuint glsl_Program[NUM_OF_PROGRAMS]; static GLuint curr_Program; // the current one extern vec2 resolution; @@ -27,23 +28,36 @@ static GLint u_time_location; // from main.c extern double u_t; + /* translate px vector to normalized coord */ vec2 px_pos_to_normalized(vec2 *pos) { return 2. / resolution * (*pos) - 1.; } +void on_GLES2_Update1(double time) +{ + for(int i = 0; i < NUM_OF_PROGRAMS; ++i) + { + glUseProgram(glsl_Program[i]); + // write the value to the shaders + glUniform1f(glGetUniformLocation(glsl_Program[i], "u_time"), time); + } + +} + /*================= RECT SHADERS =================================*/ + void ORBIS_RenderFillRects_init(int width, int height) { - resolution = (vec2){(float)width, (float)height}; + resolution = (vec2){ width, height }; curr_Program = glsl_Program[0] = BuildProgram(rects_vs0, rects_fs0, rects_vs0_length, rects_fs0_length); curr_Program = glsl_Program[1] = BuildProgram(rects_vs1, rects_fs1, rects_vs1_length, rects_fs1_length); curr_Program = glsl_Program[2] = BuildProgram(rects_vs2, rects_fs2, rects_vs2_length, rects_fs2_length); - for (int i = 0; i < 3; i++) + for (int i = 0; i < NUM_OF_PROGRAMS; i++) { curr_Program = glsl_Program[i]; @@ -65,7 +79,7 @@ void ORBIS_RenderFillRects_init(int width, int height) void ORBIS_RenderFillRects_fini(void) { - for(int i = 0; i < 3; ++i) + for(int i = 0; i < NUM_OF_PROGRAMS; ++i) { if (glsl_Program[i]) glDeleteProgram(glsl_Program[i]), glsl_Program[i] = 0; } @@ -111,18 +125,19 @@ void ORBIS_RenderDrawLines(//SDL_Renderer *renderer, /* draw a white box around selected rect */ -void ORBIS_RenderDrawBox(enum SH_type SL_program, const vec4 *rgba, const vec4 *r) +void ORBIS_RenderDrawBox(enum SH_type SL_program, const vec4 &rgba, vec4 &r) { - if(rgba) color = *rgba; + + color = rgba; // backup current color vec4 curr_color = color; // a box is 4 segments joining 2 points vec2 b[4 * 2]; // 1 line for each 2 points - b[0] = r->xy, b[1] = r->xw; - b[2] = r->xw, b[3] = r->zw; - b[4] = r->zw, b[5] = r->zy; - b[6] = r->zy, b[7] = r->xy; + b[0] = r.xy, b[1] = r.xw; + b[2] = r.xw, b[3] = r.zw; + b[4] = r.zw, b[5] = r.zy; + b[6] = r.zy, b[7] = r.xy; // select shader to use curr_Program = glsl_Program[SL_program]; // gles render all lines @@ -130,11 +145,12 @@ void ORBIS_RenderDrawBox(enum SH_type SL_program, const vec4 *rgba, const vec4 * // restore current color color = curr_color; } - -/* main function to draw FillRects */ -void ORBIS_RenderFillRects(enum SH_type SL_program, const vec4 *rgba, const vec4 *rects, int count) +void ORBIS_RenderFillRects(enum SH_type SL_program, const vec4 &rgba, std::vector &rects, int count) { - if(rgba) color = *rgba; + + if(rects.size() < count) return; + + color = rgba; // (4 float pairs!) GLfloat vertices[8]; // select shader to use @@ -150,10 +166,10 @@ void ORBIS_RenderFillRects(enum SH_type SL_program, const vec4 *rgba, const vec4 /* emit a triangle strip for each rectangle */ for(int i = 0; i < count; ++i) { - const vec4 *rect = &rects[i]; + const vec4 rect = rects[i]; - GLfloat xMin = rect->x, xMax = rect->z, - yMin = rect->y, yMax = rect->w; + GLfloat xMin = rect.x, xMax = rect.z, + yMin = rect.y, yMax = rect.w; /* (x, y) for 4 points: 8 */ vertices[0] = xMin; vertices[1] = yMin; vertices[2] = xMax; vertices[3] = yMin; @@ -174,11 +190,40 @@ void ORBIS_RenderFillRects(enum SH_type SL_program, const vec4 *rgba, const vec4 } /* draw filling color bar */ -void GLES2_DrawFillingRect(vec4* r, // float/normalized rectangle - vec4* c, // normalized color - double* percentage) // how much filled from left +void GLES2_DrawFillingRect(std::vector &r, // float/normalized rectangle + vec4 &c, // normalized color + const double percentage) // how much filled from left { // shrink frect RtoL by_percentage - r->z = r->x + (r->z - r->x) * (*percentage / 100.f); - // log_info( "%.2f %.2f, %.2f, %.2f %f", r.x, r.y, r.z, r.w, dfp); + if(r.empty()) + return; + r[0].z = r[0].x + (r[0].z - r[0].x) * (percentage / 100.f); + //log_info("p %f", percentage); + // log_info( "%.2f %.2f, %.2f, %.2f %f", c.x, c.y, c.z, c.w, percentage); +// log_info( "%.2f %.2f, %.2f, %.2f %f", r.x, r.y, r.z, r.w, dfp); ORBIS_RenderFillRects(USE_COLOR, c, r, 1); } + +void render_button(GLuint btn, float h, float w, float x, float y, float multi) +{ + + vec4 r; + vec2 s = (vec2){w / multi, h / multi}, p = (resolution - s); + p.y = y; + p.x = x; + s += p; + // convert to normalized coordinates + r.xy = px_pos_to_normalized(&p); + r.zw = px_pos_to_normalized(&s); + on_GLES2_Render_icon(USE_COLOR, btn, 2, r, NULL); +} +/* +#include "buttons/btn_options.h" +GLuint options = GL_NULL, btn_x = GL_NULL; +void ORBIS_DrawControls(float x, float y) +{ + if(!options) + options = load_png_data_into_texture(btn_options_png.data, btn_options_png.size); + + render_button(options, 84., 145., x, y, 1.5); +} +*/ diff --git a/itemzflow/source/GLES2_textures.cpp b/itemzflow/source/GLES2_textures.cpp index 3ab6cfb..b1da802 100644 --- a/itemzflow/source/GLES2_textures.cpp +++ b/itemzflow/source/GLES2_textures.cpp @@ -9,13 +9,13 @@ #include #include -#include -#if defined(__ORBIS__) +#include "defines.h" +#ifdef __ORBIS__ #include "shaders.h" #else #include "pc_shaders.h" #endif - +#include "GLES2_common.h" /* we setup two SL programs: @@ -23,7 +23,7 @@ 1. use glowing effect on passed time */ -static GLuint glsl_Program[3]; +GLuint glsl_Program[MAX_SL_PROGRAMS]; static GLuint curr_Program; // the current one #define BUFFER_OFFSET(i) ((void*)(i)) @@ -40,31 +40,59 @@ static GLint u_texture_unit_location, extern vec2 resolution; // (shared and constant!) +//static vec4 color = { 1., 0., .5, 1. }; // current RGBA color + +#if 0 +// my_Frect position(.xy) and size (.zw) +static vec4 rects[NUM_OF_TEXTURES]; +// fill all our "rects" info with normalized coordinates +static void setup_texture_position(int i, vec2 pos, const float scale_f) +{ + vec2 p, s; + /* fill in some pos, and size */ + //for (int i = 0; i < NUM_OF_TEXTURES; ++i) + { // position in px + p = (vec2) { 100. + i * (100. + 2. /*border*/), + 100. + 4 * 20. }, + /* convert to normalized coordinates */ + rects[i].xy = px_pos_to_normalized(&p); + // size in px + s = (vec2) { 100., 100. }; + // turn size into a second point + s += p; + /* convert to normalized coordinates */ + rects[i].zw = px_pos_to_normalized(&s); + } +} +#endif + +/*================= ICON SHADERS =================================*/ + void on_GLES2_Init_icons(int view_w, int view_h) { - resolution = (vec2){(float)view_w, (float)view_h}; + resolution = (vec2){ view_w, view_h }; glsl_Program[0] = BuildProgram(icons_vs0, icons_fs0, icons_vs0_length, icons_fs0_length); glsl_Program[1] = BuildProgram(icons_vs1, icons_fs1, icons_vs1_length, icons_fs1_length); glsl_Program[2] = BuildProgram(icons_vs2, icons_fs2, icons_vs2_length, icons_fs2_length); - for (int i = 0; i < 3; i++) + for(int i = 0; i < MAX_SL_PROGRAMS; i++) { log_debug("%s: glsl_Program[%d]: %u", __FUNCTION__, i, glsl_Program[i]); - - if (!glsl_Program[i]) - log_error("error for program[%d]!", i); - + + if( ! glsl_Program[i] ) + log_error( " for program[%d]!", i); + // make program the current one curr_Program = glsl_Program[i]; glUseProgram(curr_Program); - a_position_location = glGetAttribLocation(curr_Program, "a_Position"); - a_texture_coordinates_location = glGetAttribLocation(curr_Program, "a_TextureCoordinates"); - u_texture_unit_location = glGetUniformLocation(curr_Program, "u_TextureUnit"); - u_time_location = glGetUniformLocation(curr_Program, "u_time"); + a_position_location = glGetAttribLocation (curr_Program, "a_Position"); + a_texture_coordinates_location = glGetAttribLocation (curr_Program, "a_TextureCoordinates"); + u_texture_unit_location = glGetUniformLocation(curr_Program, "u_TextureUnit"); + u_time_location = glGetUniformLocation(curr_Program, "u_time"); } // reset to default one curr_Program = glsl_Program[USE_COLOR]; @@ -75,16 +103,14 @@ void on_GLES2_Init_icons(int view_w, int view_h) void on_GLES2_Final(void) { - for(int i = 0; i < 3; i++) + for(int i = 0; i < MAX_SL_PROGRAMS; i++) { if(glsl_Program[i]) glDeleteProgram(glsl_Program[i]), glsl_Program[i] = 0; } - - } void on_GLES2_Update(double time) { - for(int i = 0; i < 3; i++) + for(int i = 0; i < MAX_SL_PROGRAMS; i++) { glUseProgram(glsl_Program[i]); // write the value to the shaders @@ -95,34 +121,37 @@ void on_GLES2_Update(double time) } - -void on_GLES2_Render_icon(enum SH_type SL_program, GLuint texture,int num,vec4* frect,vec4* uv) +void on_GLES2_Render_icon(enum SH_type SL_program, GLuint texture, + int num, + vec4 &frect, + vec4 *uv) { - if (texture == 0 || !frect) return; + if(texture == 0) return; // we already clean - curr_Program = glsl_Program[SL_program]; + curr_Program = glsl_Program[ SL_program ]; +// log_info("SL_program:%d, curr_Program:%i", SL_program, curr_Program); // change shader if item selected // if(num == selected_icon) curr_Program = glsl_Program[GLOWING]; - glUseProgram(curr_Program); + glUseProgram (curr_Program); - glDisable(GL_CULL_FACE); + glDisable (GL_CULL_FACE); // enable alpha for png textures - glEnable(GL_BLEND); + glEnable (GL_BLEND); glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // select requested texture glActiveTexture(GL_TEXTURE0 + num); - glBindTexture(GL_TEXTURE_2D, texture); - // glUniform1i (u_texture_unit_location, num); // tell to shader - glUniform1i(glGetUniformLocation(curr_Program, "u_TextureUnit"), num); + glBindTexture (GL_TEXTURE_2D, texture); +// glUniform1i (u_texture_unit_location, num); // tell to shader + glUniform1i (glGetUniformLocation(curr_Program, "u_TextureUnit"), num); // setup positions - const vec4* rect = frect; + const vec4 rect = frect; - GLfloat xMin = rect->x, xMax = rect->z, - yMin = rect->y, yMax = rect->w; + GLfloat xMin = rect.x, xMax = rect.z, + yMin = rect.y, yMax = rect.w; /* (x, y) for 4 points: 8 vertices */ const float v_pos[] = { xMin, yMin, // TPLF @@ -133,17 +162,17 @@ void on_GLES2_Render_icon(enum SH_type SL_program, GLuint texture,int num,vec4* const float v_tex[] = { 0.f, 1.f, // TPLF 0.f, 0.f, // BTLF 1.f, 1.f, // BTRG - 1.f, 0.f }; // TPRG + 1.f, 0.f }; // TPRG // override with custom UVs - if (uv) + if( uv ) { - xMin = uv->x, xMax = uv->z, - yMin = uv->y, yMax = uv->w; + xMin = uv->x, xMax = uv->z, + yMin = uv->y, yMax = uv->w; float v_uv[] = { xMin, yMax, // TPLF xMin, yMin, // BTLF xMax, yMax, // BTRG xMax, yMin }; // TPRG - memcpy((void*)&v_tex[0], &v_uv, sizeof(float) * 8); + memcpy((void *)&v_tex[0], &v_uv, sizeof(float) * 8); } // setup attr @@ -155,13 +184,14 @@ void on_GLES2_Render_icon(enum SH_type SL_program, GLuint texture,int num,vec4* glEnableVertexAttribArray(a_position_location); glEnableVertexAttribArray(a_texture_coordinates_location); // draw from arrays buffer - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); // revert state back - glDisable(GL_BLEND); + glDisable (GL_BLEND); // release VBO, texture and program //glActiveTexture(0); // error on piglet !! glBindTexture(GL_TEXTURE_2D, 0); - glUseProgram(0); + glUseProgram (0); // we already flip/swap } + diff --git a/itemzflow/source/demo-font.cpp b/itemzflow/source/demo-font.cpp index 5ab4b07..5bc3a39 100644 --- a/itemzflow/source/demo-font.cpp +++ b/itemzflow/source/demo-font.cpp @@ -37,49 +37,26 @@ */ #include #include - #include // links against libfreetype-gl - #include "defines.h" +#include "utils.h" #if defined (__ORBIS__) - -#include -#include #include "shaders.h" -#define fprintf debugNetPrintf -#define ERROR DEBUGNET_ERROR -#define DEBUG DEBUGNET_DEBUG -#define INFO DEBUGNET_INFO -#else + +#else // on pc #include "pc_shaders.h" #endif -// ------------------------------------------------------- typedef & struct --- -typedef struct { - float x, y, z; // position (3f) - float s, t; // texture (2f) - float r, g, b, a; // color (4f) -} vertex_t; - -// then we can print them splitted -typedef struct { - int offset; - int count; -} textline_t; /* basic indexing of lines/texts */ GLuint num_of_texts = 0; // text lines, or num of lines/how we split entire texts /* shared freetype-gl function! */ +void add_text( vertex_buffer_t * buffer, texture_font_t * font, + const char * text, vec4 * color, vec2 * pen ); -void add_text(vertex_buffer_t* buffer, texture_font_t* font, std::string text, vec4* color, vec2* pen); - -#define MAX_NUM_OF_TEXTS 16 -static textline_t texts[MAX_NUM_OF_TEXTS]; -static unsigned int framecount = 0; // to animate something -static unsigned int selected = 0; // ------------------------------------------------------- global variables --- GLuint shader; @@ -94,80 +71,27 @@ static void reshape(int width, int height) mat4_set_orthographic( &projection, 0, width, 0, height, -1, 1); } -// ---------------------------------------------------------------- display --- -void render_text( void ) -{ - // we already clean in main renderloop()! - glUseProgram( shader ); - // setup state - glActiveTexture( GL_TEXTURE0 ); - glBindTexture ( GL_TEXTURE_2D, atlas->id ); // rebind glyph atlas - glDisable ( GL_CULL_FACE ); - glEnable ( GL_BLEND ); - glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - { - glUniform1i ( glGetUniformLocation( shader, "texture" ), 0 ); - glUniformMatrix4fv( glGetUniformLocation( shader, "model" ), 1, 0, model.data); - glUniformMatrix4fv( glGetUniformLocation( shader, "view" ), 1, 0, view.data); - glUniformMatrix4fv( glGetUniformLocation( shader, "projection" ), 1, 0, projection.data); - - if(0) /* draw whole VBO (storing all added texts) */ - { - vertex_buffer_render( buffer, GL_TRIANGLES ); // all vbo - } - else /* draw a range/selection of indexes (= glyph) */ - { - vertex_buffer_render_setup( buffer, GL_TRIANGLES ); // start draw - - int j = selected; - if(framecount %120 == 0) // sometime... - { - selected++; - j = selected %num_of_texts; - // selected = framecount %num_of_texts; // ... flip text - selected = j; - } - //for(int j=0; jr, g = color->g, b = color->b, a = color->a; - for( i = 0; i < text.length(); ++i ) + for( i = 0; i < strlen(text); ++i ) { - texture_glyph_t *glyph = texture_font_get_glyph( font, ( const char*)&text.at(i) ); + texture_glyph_t *glyph = texture_font_get_glyph( font, text + i ); if( glyph != NULL ) { float kerning = 0.0f; - if(i > 0) kerning = texture_glyph_get_kerning( glyph, ( const char*)&text.at(i - 1) ); + if(i > 0) + { + kerning = texture_glyph_get_kerning( glyph, text + i - 1 ); + } pen->x += kerning; int x0 = (int)( pen->x + glyph->offset_x ); int y0 = (int)( pen->y + glyph->offset_y ); @@ -179,50 +103,16 @@ void add_text(vertex_buffer_t* buffer, texture_font_t* font, std::string text, v float t1 = glyph->t1; GLuint indices[6] = {0,1,2, 0,2,3}; // (two triangles) /* VBO is setup as: "vertex:3f, tex_coord:2f, color:4f" */ - vertex_t vertices[4]; - - vertices[0].x = vertices[1].x = x0; - vertices[2].x = vertices[3].x = x1; - - vertices[0].y = vertices[3].y = y0; - vertices[1].y = vertices[2].y = y1; - - vertices[0].z = vertices[1].z = vertices[2].z = vertices[3].z = 0; - - vertices[1].s = vertices[0].s = s0; - vertices[2].s = vertices[3].s = s1; - - vertices[1].t = vertices[2].t = t1; - vertices[0].t = vertices[3].t = t0; - - vertices[0].r = vertices[1].r = vertices[2].r = vertices[3].r = r; - vertices[0].g = vertices[1].g = vertices[2].g = vertices[3].g = g; - vertices[0].b = vertices[1].b = vertices[2].b = vertices[3].b = b; - vertices[0].a = vertices[1].a = vertices[2].a = vertices[3].a = a; - + vertex_t vertices[4] = { { x0,y0,0, s0,t0, r,g,b,a }, + { x0,y1,0, s0,t1, r,g,b,a }, + { x1,y1,0, s1,t1, r,g,b,a }, + { x1,y0,0, s1,t0, r,g,b,a } }; vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); pen->x += glyph->advance_x; } } } -/* - my wrapper for standard ft-gl add_text(), but with - added indexing of text to render then by selection! - we append to default ft-gl VBOs, atlas and texture! -*/ -static void my_add_text( vertex_buffer_t * buffer, texture_font_t * font, - std::string text, vec4 * color, vec2 * pen, - textline_t *idx ) -{ /* index text */ - idx->offset = vector_size( buffer->items ); - add_text( buffer, font, text, color, pen ); // set vertexes - idx->count = vector_size( buffer->items ) - idx->offset; - num_of_texts++; - /* report the item info */ - log_info( "item[%.2d] .off: %3d, .len: %2d, buffer glyph count: %3zu, '%s'", - num_of_texts, idx->offset, idx->count, vector_size( buffer->items ), text.c_str() ); -} // ------------------------------------------------------ freetype-gl shaders --- static GLuint CreateProgram( void ) { @@ -247,63 +137,9 @@ texture_font_t *font = NULL; // ------------------------------------------------------------------- main --- int es2init_text (int width, int height) { - size_t h; // text size in pt - /* init page: compose all texts to draw */ atlas = texture_atlas_new( 1024, 1024, 1 ); - /* load .ttf in memory */ - buffer = vertex_buffer_new( "vertex:3f,tex_coord:2f,color:4f" ); - - std::string text = "this is a DKS Studios store homebrew"; - - int tx = 20; // text position - vec2 pen = { 100, 100 }; // init pen: 0,0 is lower left - vec4 white = { 1.f, 1.f, 1.f, 1.f }; - - font = texture_font_new_from_memory(atlas, 24, (const void*)font_ttf, font_len); - - if(!font) exit(-1); - - // 1. - text = "coming before xmas :lol:"; // set text - - texture_font_load_glyphs( font, text.c_str()); // set textures - pen.x = (width - tl); // use Text_Length to align pen.x - pen.x = 0, - pen.y = 10; - - my_add_text( buffer, font, text, &white, &pen, &texts[num_of_texts] ); // set vertexes - - // 2. - text = "System Version: 0xdeadcafe"; // set text - - texture_font_load_glyphs( font, text.c_str() ); // set textures - pen.x = (width - tl); // use Text_Length to align pen.x - pen.y -= font->height; // 1 line down! - pen.x = 10, - pen.y = height - font->height; - my_add_text( buffer, font, text, &white, &pen, &texts[num_of_texts] ); // set vertexes - - texture_font_delete( font ); // end with font, cleanup - - pen.y = 400; - for( h=16; h < 24; h += 2) - { - - font = texture_font_new_from_memory(atlas, h, font_ttf, font_len); - pen.x = tx; - pen.y -= font->height; // reset pen, 1 line down - - texture_font_load_glyphs( font, (char*)text.c_str() ); - - pen.x = (width - tl) /2; // use Text_Length to center pen.x - - my_add_text( buffer, font, text, &white, &pen, &texts[num_of_texts] ); // set vertexes - - texture_font_delete( font ); // end with font, cleanup - } - /* create texture and upload atlas into gpu memory */ glGenTextures ( 1, &atlas->id ); glBindTexture ( GL_TEXTURE_2D, atlas->id ); @@ -315,10 +151,10 @@ int es2init_text (int width, int height) 0, GL_ALPHA, GL_UNSIGNED_BYTE, atlas->data ); // compile, link and use shader shader = CreateProgram(); + + if(!shader) + msgok(MSG_DIALOG::FATAL, fmt::format("{} {:x}", getLangSTR(LANG_STR::FAILED_W_CODE), shader)); -#if defined (__ORBIS__) - if(!shader) { msgok(FATAL, fmt::format("{0:} {1:#x}", getLangSTR(FAILED_W_CODE),shader)); } -#endif mat4_set_identity( &projection ); mat4_set_identity( &model ); diff --git a/itemzflow/source/game_saves.cpp b/itemzflow/source/game_saves.cpp index b098351..2eacad8 100644 --- a/itemzflow/source/game_saves.cpp +++ b/itemzflow/source/game_saves.cpp @@ -39,6 +39,7 @@ uint32_t bswap_32(uint32_t val) { // 3. key_table.content (with trailing 4-byte alignment) // 4. data_table.content +using namespace multi_select_options; struct header { uint32_t magic; uint32_t version; @@ -467,7 +468,8 @@ const orbis_patch_t shellcore_lnc_debug_505[] = { {0, NULL, 0} }; -/*const orbis_patch_t shellcore_trophy_505[] = { +/* +const orbis_patch_t shellcore_trophy_505[] = { {0x03ECEBF, (char*)"\x90\x90\x90\x90\x90\x90", 6}, {0, NULL, 0} }; @@ -1060,13 +1062,13 @@ bool patch_lnc_debug() { if (!check_syscalls()) { - ani_notify(NOTIFI_WARNING, getLangSTR(NO_GOLDHEN), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_GOLDHEN), ""); return false; } if (!patch_SceShellCore(shellcore_lnc_debug_505)) { - ani_notify(NOTIFI_WARNING, getLangSTR(FAILED_SAVE_PATCHES), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_SAVE_PATCHES), ""); return false; } @@ -1121,19 +1123,19 @@ bool patch_save_libraries() break; default: - ani_notify(NOTIFI_WARNING, getLangSTR(UNSUPPORTED_FW), fmt::format("{0:}: {1:#x}", getLangSTR(UNSUPPORTED_FW), version >> 8)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::UNSUPPORTED_FW), fmt::format("{0:}: {1:#x}", getLangSTR(LANG_STR::UNSUPPORTED_FW), version >> 8)); return false; } if (!check_syscalls()) { - ani_notify(NOTIFI_WARNING, getLangSTR(NO_GOLDHEN), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_GOLDHEN), ""); return false; } if (!patch_SceShellCore(shellcore_patch) || !patch_SceSaveData(savedata_patch)) { - ani_notify(NOTIFI_WARNING, getLangSTR(FAILED_SAVE_PATCHES), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_SAVE_PATCHES), ""); return false; } @@ -1386,17 +1388,17 @@ void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path) int saves = (sv == RESTORE_GAME_SAVE) ? 1 : gm_save.numb_of_saves; - loadmsg(getLangSTR(SAVE_OP_ON_GOING)); + loadmsg(getLangSTR(LANG_STR::SAVE_OP_ON_GOING)); //Check to see if its a devkit or testkit filesystem //only Backing up saves works with devkit modules if (sv != BACKUP_GAME_SAVE && if_exists("/system/sys/set_upper.self")) { - ani_notify(NOTIFI_WARNING, getLangSTR(UNSUPPORTED_FS), getLangSTR(RETAILO)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::UNSUPPORTED_FS), getLangSTR(LANG_STR::RETAILO)); sceMsgDialogTerminate(); return; } if (sv < RESTORE_GAME_SAVE) { if (usbpath() == -1) { - ani_notify(NOTIFI_WARNING, getLangSTR(SAVE_OPS_FAILED), getLangSTR(NO_USB)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::SAVE_OPS_FAILED), getLangSTR(LANG_STR::NO_USB)); sceMsgDialogTerminate(); return; } @@ -1416,11 +1418,11 @@ void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path) } if (saves <= 0 && sv != RESTORE_GAME_SAVE) { - ani_notify(NOTIFI_WARNING, getLangSTR(NO_SAVES), getLangSTR(CANT_FIND_SAVE)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_SAVES), getLangSTR(LANG_STR::CANT_FIND_SAVE)); goto unpatch; } if (sceSaveDataInitialize3(0) != ITEMZCORE_SUCCESS){ - ani_notify(NOTIFI_WARNING, getLangSTR(SAVE_OPS_FAILED), getLangSTR(FAILED_INI_SD)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::SAVE_OPS_FAILED), getLangSTR(LANG_STR::FAILED_INI_SD)); goto unpatch; } for (int j = 0; j < saves; j++) { @@ -1434,14 +1436,14 @@ void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path) if(sv != RESTORE_GAME_SAVE){ log_info("Save Data Operations :: %d %d %d %s", sv, saves, (sv == DELETE_GAME_SAVE) ? 0 : j, save.dir_name.c_str()); if (!GameSave_Info(&save, (sv == DELETE_GAME_SAVE) ? 0 : j)) { - ani_notify(NOTIFI_WARNING, getLangSTR(NO_SAVES), getLangSTR(CANT_FIND_SAVE)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_SAVES), getLangSTR(LANG_STR::CANT_FIND_SAVE)); goto unpatch; } } else if(sv == RESTORE_GAME_SAVE){ dest = fmt::format("{}/sce_sys/param.sfo", path); if (!if_exists(dest.c_str()) || !is_sfo(dest.c_str())) { - ani_notify(NOTIFI_WARNING, getLangSTR(NO_SAVES), getLangSTR(CANT_FIND_SAVE)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_SAVES), getLangSTR(LANG_STR::CANT_FIND_SAVE)); goto unpatch; } @@ -1449,7 +1451,7 @@ void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path) save.blocks = -1; //ini sets blocks if(sfo_data.empty() || !get_save_details(sfo_data, save)){ - ani_notify(NOTIFI_WARNING, getLangSTR(NO_SAVES), getLangSTR(CANT_FIND_SAVE)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::NO_SAVES), getLangSTR(LANG_STR::CANT_FIND_SAVE)); goto unpatch; } @@ -1458,12 +1460,12 @@ void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path) save_flag = ORBIS_SAVE_DATA_MOUNT_MODE_RDWR | ORBIS_SAVE_DATA_MOUNT_MODE_CREATE2 | ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON; // see if the dir_name (save) is already exists if(If_Save_Exists(tmp.c_str(), &save)){ - if(Confirmation_Msg(getLangSTR(OVERWRITE_SAVE)) == YES){ + if(Confirmation_Msg(getLangSTR(LANG_STR::OVERWRITE_SAVE)) == YES){ orbis_SaveDelete(&save); } else{ log_debug("Save overwrite cancelled"); - ani_notify(NOTIFI_WARNING, getLangSTR(OP_CANNED), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OP_CANNED), getLangSTR(LANG_STR::OP_CANNED_1)); goto unpatch; } } @@ -1471,7 +1473,7 @@ void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path) if (sv != DELETE_GAME_SAVE) { if (!orbis_SaveMount(&save, save_flag, mount)) { - ani_notify(NOTIFI_WARNING, getLangSTR(SAVE_OPS_FAILED), getLangSTR(FAILED_MOUNT_SD)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::SAVE_OPS_FAILED), getLangSTR(LANG_STR::FAILED_MOUNT_SD)); log_error("Error! Failed to mount save data!"); goto unpatch; } @@ -1495,25 +1497,25 @@ void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path) fmt::print("Copying save data to {0:}", dest); - if (!copy_dir((char*)save.path.c_str(), (char*)dest.c_str())){ - ani_notify(NOTIFI_WARNING, getLangSTR(SAVE_OPS_FAILED), getLangSTR(SD_TRANSFER_FAIL)); + if (!copy_dir(save.path, dest)){ + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::SAVE_OPS_FAILED), getLangSTR(LANG_STR::SD_TRANSFER_FAIL)); break; } - ani_notify(NOTIFI_GAME, getLangSTR(SD_SAVED), fmt::format("{0:d}/{1:d}", j+1, saves)); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::SD_SAVED), fmt::format("{0:d}/{1:d}", j+1, saves)); break; } case DELETE_GAME_SAVE: { if(orbis_SaveDelete(&save)){ - ani_notify(NOTIFI_GAME, getLangSTR(SD_DELETED), fmt::format("{0:} ({1:d}/{2:d})", save.title_id, j+1, saves)); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::SD_DELETED), fmt::format("{0:} ({1:d}/{2:d})", save.title_id, j+1, saves)); //1 less save for the UI gm_save.is_loaded = false; gm_save.numb_of_saves--; } else - ani_notify(NOTIFI_WARNING, getLangSTR(SAVE_OPS_FAILED), getLangSTR(CANT_DELETE_SD)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::SAVE_OPS_FAILED), getLangSTR(LANG_STR::CANT_DELETE_SD)); break; } @@ -1522,18 +1524,18 @@ void SaveData_Operations(SAVE_Multi_Sel sv, std::string tid, std::string path) tmp2 = fmt::format("/mnt/sandbox/ITEM00001_000{0:}/", mount); fmt::print("Copying save data to {0:} {1:}", tmp2, dest); - if(!copy_dir((char*)path.c_str(), (char*)tmp2.c_str())) goto restore_error; + if(!copy_dir(path, tmp2)) goto restore_error; if(!_update_save_details(mount, path, &save)) goto restore_error; if((gm_save.is_loaded = GameSave_Info(&gm_save, gm_save.numb_of_saves))){ - dest = fmt::format("{0:}: {1:}", getLangSTR(SAVE_IMPORT_2), gm_save.main_title); - ani_notify(NOTIFI_GAME, getLangSTR(SAVE_IMPORTED), dest); + dest = fmt::format("{0:}: {1:}", getLangSTR(LANG_STR::SAVE_IMPORT_2), gm_save.main_title); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::SAVE_IMPORTED), dest); break; } restore_error: - ani_notify(NOTIFI_WARNING, getLangSTR(RESTORE_FAILED), getLangSTR(SAVE_OPS_FAILED)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::RESTORE_FAILED), getLangSTR(LANG_STR::SAVE_OPS_FAILED)); break; } default: @@ -1550,8 +1552,8 @@ restore_error: } else { log_info("%x", ret); - dest = fmt::format("{0:}: {1:#x}", getLangSTR(NO_USERID), ret); - ani_notify(NOTIFI_WARNING, getLangSTR(SAVE_OPS_FAILED), dest); + dest = fmt::format("{0:}: {1:#x}", getLangSTR(LANG_STR::NO_USERID), ret); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::SAVE_OPS_FAILED), dest); } unpatch: diff --git a/itemzflow/source/http.cpp b/itemzflow/source/http.cpp index ff4a3a1..73fcf1e 100644 --- a/itemzflow/source/http.cpp +++ b/itemzflow/source/http.cpp @@ -48,7 +48,7 @@ void DL_ERROR(const char* name, int statusCode, struct dl_args *i) if (!i->is_threaded) log_warn( "[HTTP] Download Failed with code HEX: %x Int: %i from Function %s src: %s", statusCode, statusCode, name, i->src); else - msgok(WARNING, fmt::format("{0:}\n\nHEX: {1:#x} Int: {2:d}\nfrom Function {3:} src: {4:}", getLangSTR(DL_FAILED_W), statusCode, statusCode, name, i->src)); + msgok(MSG_DIALOG::WARNING, fmt::format("{0:}\n\nHEX: {1:#x} Int: {2:d}\nfrom Function {3:} src: {4:}", getLangSTR(LANG_STR::DL_FAILED_W), statusCode, statusCode, name, i->src)); } diff --git a/itemzflow/source/installpkg.cpp b/itemzflow/source/installpkg.cpp index 7803049..b6bbc15 100644 --- a/itemzflow/source/installpkg.cpp +++ b/itemzflow/source/installpkg.cpp @@ -9,7 +9,7 @@ extern std::string tmp; int PKG_ERROR(const char* name, int ret) { - msgok(WARNING, fmt::format("{0:}\nHEX: {1:#x} Int: {2:d}\n{3:}", getLangSTR(INSTALL_FAILED), ret, ret, name)); + msgok(MSG_DIALOG::WARNING, fmt::format("{0:}\nHEX: {1:#x} Int: {2:d}\n{3:}", getLangSTR(LANG_STR::INSTALL_FAILED), ret, ret, name)); log_error( "%s error: %x", name, ret); return ret; @@ -195,15 +195,15 @@ void *install_prog(void* argument) SceBgftTaskProgress progress_info; bool is_threaded = args->is_thread; - tmp = fmt::format("{0:.60}\n\n{1:}", args->fname, getLangSTR(INSTALLING)); + tmp = fmt::format("{0:.60}\n\n{1:} {2:d}/{3:d}", args->fname, getLangSTR(LANG_STR::INSTALLING), args->curr_pkg, args->max_pkgs); if (!is_threaded) - progstart((char*)tmp.c_str()); + progstart(tmp.c_str()); else log_info("Starting PKG Install"); int prog = 0; - while (prog < 99) + while (prog < 100) { memset(&progress_info, 0, sizeof(progress_info)); @@ -239,8 +239,10 @@ void *install_prog(void* argument) if (progress_info.error_result == 0) { if (!is_threaded) { + if(args->curr_pkg >= args->max_pkgs) sceMsgDialogTerminate(); - ani_notify(NOTIFI_GAME, getLangSTR(PKG_INST_SUCCESS), fmt::format("{0:}", args->title_id)); + + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::PKG_INST_SUCCESS), fmt::format("{0:}", args->title_id)); } if(args->delete_pkg) unlink(args->path); @@ -248,8 +250,8 @@ void *install_prog(void* argument) else { if (!is_threaded) { sceMsgDialogTerminate(); - ani_notify(NOTIFI_WARNING, getLangSTR(PKG_INST_FAILED),fmt::format("{0:#x}", progress_info.error_result)); - msgok(WARNING, fmt::format("{0:} {1:} {2:} {3:#X}", getLangSTR(INSTALL_OF), args->title_id, getLangSTR(INSTALL_FAILED), progress_info.error_result)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::PKG_INST_FAILED),fmt::format("{0:#x}", progress_info.error_result)); + msgok(MSG_DIALOG::WARNING, fmt::format("{0:} {1:} {2:} {3:#X}", getLangSTR(LANG_STR::INSTALL_OF), args->title_id, getLangSTR(LANG_STR::INSTALL_FAILED), progress_info.error_result)); } else log_error("Installation of %s has failed with code 0x%x", args->title_id, progress_info.error_result); @@ -269,11 +271,12 @@ void *install_prog(void* argument) } uint32_t pkginstall(const char *fullpath, const char* filename, -bool Show_install_prog, bool delete_pkg_after) +bool Show_install_prog, bool delete_pkg_after, int max_pkgs, int curr_pkg ) { char title_id[16]; int is_app, ret = -1; int task_id = -1; + bool is_app_exists = false; std::string tmp; if( if_exists(fullpath) ) @@ -294,6 +297,9 @@ bool Show_install_prog, bool delete_pkg_after) if (ret) return PKG_ERROR("sceAppInstUtilGetTitleIdFromPkg", ret); + if(pkg_is_patch(fullpath) && app_inst_util_is_exists(title_id, &is_app_exists) && !is_app_exists) + return PKG_ERROR("BASE_GAME_NOT_INSTALLED", ret); + tmp = fmt::format("ItemzFlow: {}", title_id); struct bgft_download_param_ex download_params; @@ -316,7 +322,7 @@ bool Show_install_prog, bool delete_pkg_after) ret = sceBgftServiceIntDownloadRegisterTaskByStorageEx(&download_params, &task_id); if(ret == 0x80990088 || ret == 0x80990015) { - if(Confirmation_Msg(getLangSTR(REINSTALL_APP_CONFIRMATION)) == YES){ + if(Confirmation_Msg(getLangSTR(LANG_STR::REINSTALL_APP_CONFIRMATION)) == YES){ ret = sceAppInstUtilAppUnInstall(&title_id[0]); if(ret != 0) return PKG_ERROR("sceAppInstUtilAppUnInstall", ret); @@ -348,6 +354,8 @@ bool Show_install_prog, bool delete_pkg_after) args->fname = strdup(filename); args->is_thread = !Show_install_prog; args->delete_pkg = delete_pkg_after; + args->max_pkgs = max_pkgs; + args->curr_pkg = curr_pkg; if (Show_install_prog){ install_prog((void*)args); diff --git a/itemzflow/source/itemzcore_init.cpp b/itemzflow/source/itemzcore_init.cpp index 595e41f..49f889f 100644 --- a/itemzflow/source/itemzcore_init.cpp +++ b/itemzflow/source/itemzcore_init.cpp @@ -15,7 +15,7 @@ #include #include #include -extern Dump_Multi_Sels dump; +extern multi_select_options::Dump_Multi_Sels dump; extern std::vector gm_p_text; #if defined (__ORBIS__) @@ -167,7 +167,7 @@ extern vertex_buffer_t* text_buffer[4]; extern dl_arg_t* pt_info; // available num of jsons int json_c = 0; -std::vector all_apps; +ThreadSafeVector all_apps; // common title in different panels GLuint fallback_t = 0; // fallback texture @@ -177,27 +177,28 @@ GLuint fallback_t = 0; // fallback texture extern bool use_reflection, use_pixelshader; -int layout_fill_item_from_list(layout_t *l, std::vector &i_list) + +int layout_fill_item_from_list(layout_t & l, std::vector &i_list) { int i, count = 0; - log_debug("layout_fill_item_from_list size: %d", i_list.size()); - l->item_d = std::vector(l->item_c); + l.item_d = ThreadSafeVector(l.item_c); - for (i = 0; i < l->item_c; i++) // iterate item_d + for (i = 0; i < l.item_c; i++) // iterate item_d { // dynallocs each token_d - l->item_d[i].token_c = 1; + l.item_d[i].count.token_c = 1; +// log_info("i_list[%i] = %s", i, i_list[i]); // we use just the first token_d - l->item_d[i].name = i_list.at(i); + l.item_d[i].info.name = i_list.at(i); if (1) { //token->len = strlen( i_list[i] ); count += 1; - log_debug("%s, %s, %d", __FUNCTION__, l->item_d[i].name.c_str(), count); + log_debug("%s, %s, %d", __FUNCTION__, l.item_d[i].info.name.c_str(), count); } } /* update */ - l->item_c = count; // layout items count + l.item_c = count; // layout items count layout_update_fsize(l); // and the field_size return count; @@ -216,17 +217,17 @@ void init_ItemzCore(int w, int h) grey = (vec4){ 66, 66, 66, 256 } / 256; // fg /* first, scan folder, populate Installed_Apps */ - loadmsg(getLangSTR(LOADING_GAME_LIST)); + loadmsg(getLangSTR(LANG_STR::LOADING_GAME_LIST)); // build an array of indexes for each one entry there mkdir("/user/app/", 0777); // avoid non existent path issues mkdir("/user/data/GoldHEN/", 0777); mkdir("/user/data/GoldHEN/patches/", 0777); mkdir("/user/data/GoldHEN/patches/settings/", 0777); - mkdir("/user/data/GoldHEN/patches/json/", 0777); + mkdir("/user/data/GoldHEN/patches/xml/", 0777); log_info("Searching for Apps"); - index_items_from_dir(all_apps, "/user/app", "/mnt/ext0/user/app", NO_CATEGORY_FILTER); + index_items_from_dir(all_apps, APP_PATH("../"), "/mnt/ext0/user/app", NO_CATEGORY_FILTER); // setup one for all missing, the fallback icon0 if (!fallback_t) @@ -246,7 +247,7 @@ void init_ItemzCore(int w, int h) ORBIS_RenderFillRects_init(w, h); // init ttf fonts GLES2_fonts_from_ttf(get->setting_strings[FNT_PATH].c_str()); - // init ani_notify() + // init ani_notify(NOTIFI::) GLES2_ani_init(w, h); // fragments shaders, effects etc pixelshader_init(w, h); @@ -256,7 +257,7 @@ void init_ItemzCore(int w, int h) InitScene_4(w, h); InitScene_5(w, h); - if(get->sort_by != NA_SORT){ + if(get->sort_by != multi_select_options::NA_SORT){ log_info("Sorting by %d", get->sort_by); refresh_apps_for_cf(get->sort_by, get->sort_cat); } @@ -264,48 +265,51 @@ void init_ItemzCore(int w, int h) } -void GLES2_UpdateVboForLayout(layout_t* l) +void GLES2_UpdateVboForLayout(layout_t& l) { - if (l) - { // skip until we ask to refresh VBO - if (l->vbo_s < ASK_REFRESH) return; - - if (l->vbo) vertex_buffer_delete(l->vbo), l->vbo = NULL; - - l->vbo_s = EMPTY; - } + // skip until we ask to refresh VBO + if (l.vbo_s < ASK_REFRESH) return; + l.vbo .clear(); + l.vbo_s = EMPTY; + } + void O_action_dispatch(void) { refresh_atlas(); } -bool init_daemon_services(bool redirect) -{ - char IPC_BUFFER[100]; - get->ItemzDaemon_AppId = sceLncUtilGetAppId((char*)"ITEM00002"); +bool init_daemon_services(bool redirect) { + + IPC_Client& ipc = IPC_Client::getInstance(); + std::string IPC_BUFFER; + IPC_Ret error = IPC_Ret::INVALID; + int wait = 0; + + get->ItemzDaemon_AppId = sceLncUtilGetAppId((char*)"ITEM00002"); + //Launch Daemon with silent if ((get->ItemzDaemon_AppId & ~0xFFFFFF) != 0x60000000) { log_error("Daemon not launched: error %x", get->ItemzDaemon_AppId); return false; - } - else + } else { log_info("Found Daemon AppId: %x", get->ItemzDaemon_AppId); + } + + loadmsg(getLangSTR(LANG_STR::WAITING_FOR_DAEMON)); - loadmsg(getLangSTR(WAITING_FOR_DAEMON)); - int error = INVALID, wait = INVALID; // Wait for the Daemon to respond - do { + do { + error = ipc.IPCSendCommand(IPC_Commands::CONNECTION_TEST, IPC_BUFFER); + log_info("[ItemzDaemon] Status: %s", error == IPC_Ret::INVALID ? "Failed to Connect" : "Success"); - error = IPCSendCommand(CONNECTION_TEST, (uint8_t*)&IPC_BUFFER[0]); - log_info("[ItemzDaemon] Status: %s", error == INVALID ? "Failed to Connect" : "Success"); - if (error == NO_ERROR) { + if (error == IPC_Ret::NO_ERROR) { sceMsgDialogTerminate(); log_debug(" Took the Daemon %i extra commands attempts to respond", wait); - is_connected_app = true; + is_connected_app = true; // You should declare and define 'is_connected_app' somewhere } if (wait >= 60) @@ -314,35 +318,33 @@ bool init_daemon_services(bool redirect) sleep(1); wait++; - //File Flag the Daemon creates when initialization is complete - // and the Daemon IPC server is active - } while (error == INVALID); + // File Flag the Daemon creates when initialization is complete + // and the Daemon IPC server is active + } while (error == IPC_Ret::INVALID); - if (is_connected_app) - { - if (redirect) // is setting get->HomeMenu_Redirection enabled - { + sceMsgDialogTerminate(); + + if (is_connected_app) { // You should declare and define 'is_connected_app' somewhere + if (redirect) { // is setting get->HomeMenu_Redirection enabled log_info("Redirect on with app connected"); - error = IPCSendCommand(ENABLE_HOME_REDIRECT, (uint8_t*)&IPC_BUFFER[0]); - if (error == NO_ERROR) + error = ipc.IPCSendCommand(IPC_Commands::ENABLE_HOME_REDIRECT, IPC_BUFFER); + if (error == IPC_Ret::NO_ERROR) { log_debug(" HOME MENU REDIRECT IS ENABLED"); - else { + } else { log_debug("[ItemzDaemon][ERROR] Home menu redirect failed"); return false; } } - } - else + } else { return false; + } return true; - } - -int Start_IF_internal_Services(int reboot_num) +int Start_IF_internal_Services(const char* launch_q) { int ret = 0; unlink(ITEMZ_LOG); @@ -358,8 +360,8 @@ int Start_IF_internal_Services(int reboot_num) generate_build_time(); - log_info("------------------------ Itemzflow Compiled Time: %s @ %s -------------------------", __DATE__, __TIME__); - log_info("--------------------------- Itemzflow Version: %s Num of Reboots: %i----------------------------", completeVersion.c_str(), reboot_num); + log_info("------------------ Itemzflow Compiled Time: %s @ %s LQ: %s -------------------------", __DATE__, __TIME__, launch_q == NULL || strlen(launch_q) < 1 ? "No Launch Query (LQ) provided" : launch_q); + log_info("--------------------------- Itemzflow Version: %s FW: 0%X ----------------------------", completeVersion.c_str(), ps4_fw_version() >> 16); get = &set; @@ -447,10 +449,13 @@ int Start_IF_internal_Services(int reboot_num) mkdir("/user/app/ITEM00001/covers", 0777);// mkdir("/user/app/ITEM00001/custom_app_names", 0777); + if(if_exists("/mnt/ext0/user/app/ITEM00001/app.pkg")){ + msgok(MSG_DIALOG::FATAL, "Itmezflow is not designed to be installed on external storage. Please install it on internal storage and try again."); + } log_info("Loading Settings... "); if (!LoadOptions(get)) - msgok(WARNING, getLangSTR(INI_ERROR)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::INI_ERROR)); else log_info("Loaded Settings"); @@ -478,7 +483,7 @@ int Start_IF_internal_Services(int reboot_num) int BigAppid = sceSystemServiceGetAppIdOfBigApp(); if ((BigAppid & ~0xFFFFFF) == 0x60000000) { log_info("FOUND Open Game: %x : Bool: %d", BigAppid, (BigAppid & ~0xFFFFFF) == 0x60000000); - gm_p_text[0] = getLangSTR(CLOSE_GAME); + gm_p_text[0] = getLangSTR(LANG_STR::CLOSE_GAME); } else log_info("No Open Games detected"); @@ -487,23 +492,23 @@ int Start_IF_internal_Services(int reboot_num) { log_info("Starting Daemon... "); if (!init_daemon_services(get->setting_bools[HomeMenu_Redirection])) - ani_notify(NOTIFI_WARNING, getLangSTR(FAILED_DAEMON), getLangSTR(DAEMON_OFF2)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_DAEMON), getLangSTR(LANG_STR::DAEMON_OFF2)); else log_debug(" The Itemzflow init_daemon_services succeeded."); } else - ani_notify(NOTIFI_WARNING, getLangSTR(DAEMON_OFF), getLangSTR(DAEMON_OFF2)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::DAEMON_OFF), getLangSTR(LANG_STR::DAEMON_OFF2)); if(MP3_Player(get->setting_strings[MP3_PATH]) != MP3_STATUS_ERROR) - ani_notify(NOTIFI_SUCCESS, getLangSTR(MP3_PLAYING), getLangSTR(MP3_LOADED)); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::MP3_PLAYING), getLangSTR(LANG_STR::MP3_LOADED)); sceMsgDialogTerminate(); } else { #ifndef HAVE_SHADER_COMPILER - msgok(FATAL, fmt::format("{} {}\nPS4 Path: /system/common/lib/libScePigletv2VSH.sprx", ret, getLangSTR(PIG_FAIL))); + msgok(MSG_DIALOG::FATAL, fmt::format("{} {}\nPS4 Path: /system/common/lib/libScePigletv2VSH.sprx", ret, getLangSTR(LANG_STR::PIG_FAIL))); #else - msgok(FATAL, "HAS_SHADER_COMP: Piglet (custom) failed to load with 0x%x\nPiglet Path: /data/piglet.sprx, Compiler Path: /data/compiler.sprx", s_piglet_module); + msgok(MSG_DIALOG::FATAL, "HAS_SHADER_COMP: Piglet (custom) failed to load with 0x%x\nPiglet Path: /data/piglet.sprx, Compiler Path: /data/compiler.sprx", s_piglet_module); #endif return INIT_FAILED; } @@ -512,8 +517,10 @@ int Start_IF_internal_Services(int reboot_num) check_for_update_file(); //dont_show_donate_message - if (!if_exists("/user/app/ITEM00001/support.flag")) - ani_notify(NOTIFI_KOFI, getLangSTR(CONSIDER_SUPPORT), "https://ko-fi.com/lightningmods"); + if (!if_exists("/user/app/ITEM00001/support.flag")){ + ani_notify(NOTIFI::KOFI, getLangSTR(LANG_STR::CONSIDER_SUPPORT), "https://ko-fi.com/lightningmods"); + msgok(MSG_DIALOG::NORMAL, "If you would like to see future itemzflow updates consider supporting the project on ko-fi @ https://ko-fi.com/lightningmods"); + } // all fine. return PS4_OK; } diff --git a/itemzflow/source/lang.cpp b/itemzflow/source/lang.cpp index 46f18fc..8cc514e 100644 --- a/itemzflow/source/lang.cpp +++ b/itemzflow/source/lang.cpp @@ -7,12 +7,13 @@ extern int numb_of_settings; std::unordered_map stropts; std::vector gm_p_text(20); std::vector ITEMZ_SETTING_TEXT(NUMBER_OF_SETTINGS + 10); +using namespace LANG_STR; // // https://github.com/Al-Azif/ps4-payload-guest/blob/main/src/Language.cpp // -const char *Language_GetName(int m_Code) +std::string Language_GetName(int m_Code) { - const char *s_Name; + std::string s_Name; switch (m_Code) { @@ -127,7 +128,7 @@ int32_t PS4GetLang() return lang; } -std::string getLangSTR(enum Lang_STR str) +std::string getLangSTR(LANG_STR::LANG_STRINGS str) { if (lang_is_initialized) { @@ -158,37 +159,37 @@ void fill_menu_text() log_info("In Advanced menu, skipping.."); return; } - ITEMZ_SETTING_TEXT[0] = fmt::format("{0:.20}", getLangSTR(SETTINGS_1)); - ITEMZ_SETTING_TEXT[1] = fmt::format("{0:.30}", getLangSTR(SETTINGS_2)); - ITEMZ_SETTING_TEXT[2] = fmt::format("{0:.30}", getLangSTR(SETTINGS_3)); - ITEMZ_SETTING_TEXT[3] = fmt::format("{0:.30}", getLangSTR(SETTINGS_4)); - ITEMZ_SETTING_TEXT[4] = fmt::format("{0:.30}", getLangSTR(SETTINGS_5)); - ITEMZ_SETTING_TEXT[5] = fmt::format("{0:.30}", getLangSTR(SETTINGS_6)); - ITEMZ_SETTING_TEXT[6] = fmt::format("{0:.30}", getLangSTR(SETTINGS_7)); - ITEMZ_SETTING_TEXT[7] = fmt::format("{0:.30}", getLangSTR(SETTINGS_8)); - ITEMZ_SETTING_TEXT[8] = fmt::format("{0:.30}", getLangSTR(SETTINGS_9)); - ITEMZ_SETTING_TEXT[9] = fmt::format("{0:.30}", getLangSTR(SETTINGS_10)); - ITEMZ_SETTING_TEXT[10] = fmt::format("{0:.30}", getLangSTR(SETTINGS_11)); + ITEMZ_SETTING_TEXT[0] = fmt::format("{0:.20}", getLangSTR(LANG_STR::SETTINGS_1)); + ITEMZ_SETTING_TEXT[1] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_2)); + ITEMZ_SETTING_TEXT[2] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_3)); + ITEMZ_SETTING_TEXT[3] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_4)); + ITEMZ_SETTING_TEXT[4] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_5)); + ITEMZ_SETTING_TEXT[5] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_6)); + ITEMZ_SETTING_TEXT[6] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_7)); + ITEMZ_SETTING_TEXT[7] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_8)); + ITEMZ_SETTING_TEXT[8] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_9)); + ITEMZ_SETTING_TEXT[9] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_10)); + ITEMZ_SETTING_TEXT[10] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_11)); - ITEMZ_SETTING_TEXT[11] = fmt::format("{0:.30}", getLangSTR(SETTINGS_12)); + ITEMZ_SETTING_TEXT[11] = fmt::format("{0:.30}", getLangSTR(LANG_STR::SETTINGS_12)); - ITEMZ_SETTING_TEXT[12] = fmt::format("{0:.30}", getLangSTR(EXTRA_SETTING_1)); - ITEMZ_SETTING_TEXT[13] = fmt::format("{0:.30}", getLangSTR(EXTRA_SETTING_2)); - ITEMZ_SETTING_TEXT[14] = fmt::format("{0:.30}", getLangSTR(EXTRA_SETTING_3)); - ITEMZ_SETTING_TEXT[15] = fmt::format("{0:.30}", getLangSTR(EXTRA_SETTING_4)); - ITEMZ_SETTING_TEXT[16] = fmt::format("{0:.30}", getLangSTR(EXTRA_SETTING_5)); - ITEMZ_SETTING_TEXT[17] = fmt::format("{0:.30}", getLangSTR(EXTRA_SETTING_6)); + ITEMZ_SETTING_TEXT[12] = fmt::format("{0:.30}", getLangSTR(LANG_STR::EXTRA_SETTING_1)); + ITEMZ_SETTING_TEXT[13] = fmt::format("{0:.30}", getLangSTR(LANG_STR::EXTRA_SETTING_2)); + ITEMZ_SETTING_TEXT[14] = fmt::format("{0:.30}", getLangSTR(LANG_STR::EXTRA_SETTING_3)); + ITEMZ_SETTING_TEXT[15] = fmt::format("{0:.30}", getLangSTR(LANG_STR::EXTRA_SETTING_4)); + ITEMZ_SETTING_TEXT[16] = fmt::format("{0:.30}", getLangSTR(LANG_STR::EXTRA_SETTING_5)); + ITEMZ_SETTING_TEXT[17] = fmt::format("{0:.30}", getLangSTR(LANG_STR::EXTRA_SETTING_6)); - gm_p_text[0] = fmt::format("{0:.20}", getLangSTR(LAUNCH_GAME)); - gm_p_text[1] = fmt::format("{0:.20}", getLangSTR(DUMP_1)); - gm_p_text[2] = fmt::format("{0:.20}", getLangSTR(UNINSTALL)); - gm_p_text[3] = fmt::format("{0:.20}", getLangSTR(GAME_SAVE_OPTS)); - gm_p_text[4] = fmt::format("{0:.20}", getLangSTR(TRAINERS)); - gm_p_text[5] = fmt::format("{0:.20}", getLangSTR(HIDE_APP_OPT)); - gm_p_text[6] = fmt::format("{0:.20}", getLangSTR(CHANGE_ICON)); - gm_p_text[7] = fmt::format("{0:.20}", getLangSTR(CHANGE_APP_NAME)); - gm_p_text[8] = fmt::format("{0:.20}", getLangSTR(MOVE_APP_OPT)); - gm_p_text[9] = fmt::format("{0:.20}", getLangSTR(RESTORE_APP_OPT)); + gm_p_text[0] = fmt::format("{0:.20}", getLangSTR(LANG_STR::LAUNCH_GAME)); + gm_p_text[1] = fmt::format("{0:.20}", getLangSTR(LANG_STR::DUMP_1)); + gm_p_text[2] = fmt::format("{0:.20}", getLangSTR(LANG_STR::UNINSTALL)); + gm_p_text[3] = fmt::format("{0:.20}", getLangSTR(LANG_STR::GAME_SAVE)); + gm_p_text[4] = fmt::format("{0:.20}", getLangSTR(LANG_STR::TRAINERS)); + gm_p_text[5] = fmt::format("{0:.20}", getLangSTR(LANG_STR::HIDE_APP_OPT)); + gm_p_text[6] = fmt::format("{0:.20}", getLangSTR(LANG_STR::CHANGE_ICON)); + gm_p_text[7] = fmt::format("{0:.20}", getLangSTR(LANG_STR::CHANGE_APP_NAME)); + gm_p_text[8] = fmt::format("{0:.20}", getLangSTR(LANG_STR::MOVE_APP_OPT)); + gm_p_text[9] = fmt::format("{0:.20}", getLangSTR(LANG_STR::RESTORE_APP_OPT)); } // OVERWRITE_SAVE extern uint8_t lang_ini[]; diff --git a/itemzflow/source/ls_dir.cpp b/itemzflow/source/ls_dir.cpp index 915bce7..06c9ec8 100644 --- a/itemzflow/source/ls_dir.cpp +++ b/itemzflow/source/ls_dir.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include /* Glibc does not provide a wrapper for @@ -40,7 +42,7 @@ int HDD_count = -1; const char *SPECIAL_XMB_ICON_TID[] = {APP_HOME_DATA_TID, APP_HOME_HOST_TID, DEBUG_SETTINGS_TID}; -extern "C" bool if_exists(const char *path) +bool if_exists(const char *path) { /* int dfd = open(path, O_RDONLY, 0); // try to open dir @@ -102,6 +104,7 @@ static int app_info(void *, const char *s, const char *n, bool getEntries(std::string path, std::vector &cvEntries, FS_FILTER filter, bool for_fs_browser = false) { struct dirent *pDirent; + struct stat st; DIR *pDir = NULL; if (path.empty()) @@ -119,19 +122,28 @@ bool getEntries(std::string path, std::vector &cvEntries, FS_FILTER while ((pDirent = readdir(pDir)) != NULL) { - - if (strcmp(pDirent->d_name, ".") == 0 || strcmp(pDirent->d_name, "..") == 0) + + std::string upper_case_ext; + std::string filename = pDirent->d_name; + std::string full_path = path + "/" + filename; + + if (filename == "." || filename == ".." ) continue; + int err = stat(full_path.c_str(), &st); + if (err == -1) { + log_error("%s stat returned %s", full_path.c_str(), strerror(errno)); + continue; + } + //log_info("d_name %s pDirent->d_type %i", pDirent->d_name, pDirent->d_type); // Dir is title_id, it WILL go through 3 filters // so lets assume they are vaild until the filters say otherwise - if (pDirent->d_type == DT_DIR && filter != FS_MP3_ONLY) // MP3S ONLY NO DIR NAMES - cvEntries.push_back(pDirent->d_name); + if (S_ISDIR(st.st_mode) && filter != FS_MP3_ONLY) // MP3S ONLY NO DIR NAMES + cvEntries.push_back(filename); else if (filter != FS_FOLDER) // if folders only do ntohing { - const char *ext = NULL; - + std::string ext; switch (filter) { case FS_MP3_ONLY: @@ -151,21 +163,21 @@ bool getEntries(std::string path, std::vector &cvEntries, FS_FILTER ext = ".zip"; break; case FS_NONE: - cvEntries.push_back(pDirent->d_name); + cvEntries.push_back(filename); default: ext = ".lm_is_da_best"; // an ext no one will ever have break; } - if (strstr(pDirent->d_name, ext)) - cvEntries.push_back(pDirent->d_name); + if (filename.rfind(ext) != std::string::npos) + cvEntries.push_back(filename); else { // try with all caps - char upper_case_ext[strlen(ext) + 1]; - for (int i = 0; i < strlen(ext); i++) - upper_case_ext[i] = toupper(ext[i]); - - if (strstr(pDirent->d_name, &upper_case_ext[0])) - cvEntries.push_back(pDirent->d_name); + // Transform the string to uppercase using the lambda function + upper_case_ext = ext; + std::transform(upper_case_ext.begin(), upper_case_ext.end(), upper_case_ext.begin(), ::toupper); + if(filename.rfind(upper_case_ext) != std::string::npos){ + cvEntries.push_back(filename); + } } } } @@ -271,15 +283,24 @@ std::vector readFile(std::string filename) return vec; } -bool AppExists(std::string tid) +bool AppExists(std::string tid, std::string &pkg_path, item_t& out_vec) { - std::string path = fmt::format("/user/app/{}/app.pkg", tid); - if (!if_exists(path.c_str())) +#if defined(__ORBIS__) + pkg_path = fmt::format("/user/app/{}/app.pkg", tid); + if (!if_exists(pkg_path.c_str())) { - path = fmt::format("/mnt/ext0/user/app/{}/app.pkg", tid); - if (!if_exists(path.c_str())) - return false; + pkg_path = fmt::format("/mnt/ext0/user/app/{}/app.pkg", tid); + if (!if_exists(pkg_path.c_str())) { + pkg_path = fmt::format("/mnt/ext1/user/app/{}/app.pkg", tid); + } + else { + out_vec.flags.is_ext_hdd = true; + } } +#else + pkg_path = fmt::format("{}/{}/app.pkg", APP_PATH("apps"), tid); +#endif + return true; } // the Settings @@ -287,10 +308,10 @@ extern ItemzSettings set, *get; // each token_data is filled by dynalloc'd mem by strdup! -void index_items_from_dir(std::vector &out_vec, std::string dirpath, std::string dirpath2, Sort_Category category) +void index_items_from_dir(ThreadSafeVector &out_vec, std::string dirpath, std::string dirpath2, Sort_Category category) { int ext_count = -1; - + std::string pkg_path; std::string tmp, no_icon_path; #if defined(__ORBIS__) @@ -304,8 +325,13 @@ void index_items_from_dir(std::vector &out_vec, std::string dirpath, std log_info("Starting index_items_from_dir ..."); - std::vector cvEntries; + Favorites favorites; + if(favorites.loadFromFile(APP_PATH("favorites.dat"))) + log_info("Favorites have beend loaded!"); + else + log_warn("Failed to load favorites.dat does it exist??"); + std::vector cvEntries; if (!getEntries(dirpath, cvEntries, FS_FOLDER)) log_info("failed to get dir"); else @@ -325,6 +351,14 @@ void index_items_from_dir(std::vector &out_vec, std::string dirpath, std } } + + //remove dups + // Sort the vector to group duplicates together + std::sort(cvEntries.begin(), cvEntries.end()); + + // Erase duplicates + cvEntries.erase(std::unique(cvEntries.begin(), cvEntries.end()), cvEntries.end()); + uint32_t entCt = cvEntries.size(); log_debug("Have %d entries:", entCt); #if 0 @@ -335,11 +369,17 @@ void index_items_from_dir(std::vector &out_vec, std::string dirpath, std #endif // add one for reserve entCt++; - out_vec.resize(entCt); - int i = 1; // vaild app count + item_t if_app; + if_app.info.picpath = no_icon_path; + if_app.info.name = "ItemzCore_ls_dir"; + if_app.info.id = "ITEM99999"; + if_app.count.HDD_count = HDD_count - 1; + out_vec.push_back(if_app); + for (int not_used = 1; not_used < entCt; not_used++) { + item_t item; // we use just those token_data std::string fname = cvEntries[not_used - 1]; if (!is_vapp(fname.c_str())) @@ -349,7 +389,7 @@ void index_items_from_dir(std::vector &out_vec, std::string dirpath, std log_debug("[F1] Filtered out %s", fname.c_str()); continue; } -#if defined(__ORBIS__) + #if 0 // doesnt work for recoverying so for now we ignore // skip first reserved: take care bool does_app_exist = false; @@ -361,39 +401,31 @@ void index_items_from_dir(std::vector &out_vec, std::string dirpath, std } } #else - if (AppExists(fname)) - log_debug("[F2] App exists %s", fname.c_str()); - else - { + if (!AppExists(fname, pkg_path, item)){ log_debug("[F2] Filtered out %s, the App doesnt exist", fname.c_str()); continue; } - -#endif #endif } - if (i >= HDD_count - 1) - out_vec[i].is_ext_hdd = true; - else - out_vec[i].is_ext_hdd = false; + //(i >= HDD_count - 1) ? (item.flags.is_ext_hdd = true) : (item.flags.is_ext_hdd = false); if (category == FILTER_HOMEBREW) { - if (fname.find("CUSA") != std::string::npos) + if (fname.find("CUSA") != std::string::npos || fname.find("PPSA") != std::string::npos) continue; } else if (category == FILTER_GAMES) { - if (fname.find("CUSA") == std::string::npos) + if (fname.find("CUSA") == std::string::npos || fname.find("PPSA") == std::string::npos) continue; } // dynalloc for user tokens - out_vec[i].token_c = TOTAL_NUM_OF_TOKENS; - out_vec[i].name = out_vec[i].id = fname; - out_vec[i].extra_sfo_data.clear(); - out_vec[i].is_ph = false; + item.count.token_c = TOTAL_NUM_OF_TOKENS; + item.info.name= item.info.id = fname; + item.extra_data.extra_sfo_data.clear(); + item.flags.is_ph = false; #if defined(__ORBIS__) tmp = fmt::format("/user/appmeta/{}/icon0.png", fname); @@ -408,34 +440,32 @@ void index_items_from_dir(std::vector &out_vec, std::string dirpath, std tmp = fmt::format("{}/{}/icon0.png", std::string(APP_PATH("apps")), fname); #endif - out_vec[i].picpath = tmp; + item.info.picpath = tmp; // don't try lo load - out_vec[i].texture = GL_NULL; // XXX load_png_asset_into_texture(tmp); -#if defined(__ORBIS__) - tmp = fmt::format("/user/app/{}/app.pkg", fname); - log_info("[XMB OP] TItLE ID: %s", fname.c_str()); - if (!if_exists(tmp.c_str())) - tmp = fmt::format("/mnt/ext0/user/app/{}/app.pkg", fname); -#else // on pc - tmp = fmt::format("{}/{}/app.pkg", std::string(APP_PATH("apps")), fname); -#endif - out_vec[i].package = tmp; + item.icon.texture = GL_NULL; // XXX load_png_asset_into_texture(tmp); + item.info.package = pkg_path; #if defined(__ORBIS__) - out_vec[i].is_fpkg = is_fpkg(out_vec[i].package); - out_vec[i].app_vis = AppDBVisiable(out_vec[i].id, VIS_READ, 0); + item.flags.is_fpkg = is_fpkg(item.info.package); + item.flags.app_vis = AppDBVisiable(item.info.id, VIS_READ, 0); tmp = fmt::format("/system_data/priv/appmeta/{}/param.sfo", fname); - if (!if_exists(tmp.c_str())) + if (!if_exists(tmp.c_str())) { + log_info("%s: sfo %s does not exist", fname.c_str(), tmp.c_str()); tmp = fmt::format("/system_data/priv/appmeta/external/{}/param.sfo", fname); + } #else // on pc tmp = fmt::format("{}/{}/param.sfo", APP_PATH("apps"), fname); #endif - out_vec[i].sfopath = tmp; + item.info.sfopath = tmp; - if ((category == FILTER_FPKG && !out_vec[i].is_fpkg) || (category == FILTER_RETAIL_GAMES && out_vec[i].is_fpkg)) + if ((category == FILTER_FPKG && !item.flags.is_fpkg) || (category == FILTER_RETAIL_GAMES && item.flags.is_fpkg)) + continue; + + item.flags.is_favorite = favorites.isFavorite(item); + if(category == FILTER_FAVORITES && !item.flags.is_favorite) continue; // read sfo @@ -443,160 +473,150 @@ void index_items_from_dir(std::vector &out_vec, std::string dirpath, std { if(fname == APP_HOME_DATA_TID) { - out_vec[i].picpath = "/data/APP_HOME.png"; - out_vec[i].name = "APP_HOME(Data)"; - out_vec[i].id = APP_HOME_DATA_TID; + item.info.picpath = "/data/APP_HOME.png"; + item.info.name = "APP_HOME(Data)"; + item.info.id = APP_HOME_DATA_TID; } else if(fname == DEBUG_SETTINGS_TID) { - out_vec[i].picpath = "/data/debug.png"; - out_vec[i].name = "PS4 Debug Settings"; - out_vec[i].id = DEBUG_SETTINGS_TID; + item.info.picpath = "/data/debug.png"; + item.info.name = "PS4 Debug Settings"; + item.info.id = DEBUG_SETTINGS_TID; } else if(fname == APP_HOME_HOST_TID) { - out_vec[i].picpath = asset_path("app_home_host.png"); - out_vec[i].name = fmt::format("{} (/HOSTAPP)", getLangSTR(REMOTE_APP_TITLE)); - out_vec[i].id = APP_HOME_HOST_TID; + item.info.picpath = asset_path("app_home_host.png"); + item.info.name = fmt::format("{} (/HOSTAPP)", getLangSTR(LANG_STR::REMOTE_APP_TITLE)); + item.info.id = APP_HOME_HOST_TID; } - out_vec[i].version = "0.00"; - out_vec[i].extra_sfo_data.insert(std::pair("CATEGORY", "gde")); - out_vec[i].is_vapp = true; + item.info.version = "0.00"; + item.extra_data.extra_sfo_data.insert(std::pair("CATEGORY", "gde")); + item.flags.is_vapp = true; - log_info("[XMB OP] SFO Name: %s", out_vec[i].name.c_str()); - log_info("[XMB OP] SFO ID: %s", out_vec[i].id.c_str()); - log_info("[XMB OP] SFO VERSION: %s", out_vec[i].version.c_str()); + log_info("[XMB OP] SFO Name: %s", item.info.name.c_str()); + log_info("[XMB OP] SFO ID: %s", item.info.id.c_str()); + log_info("[XMB OP] SFO VERSION: %s", item.info.version.c_str()); } else { - std::vector sfo_data = readFile(out_vec[i].sfopath); + + std::vector sfo_data = readFile(item.info.sfopath); if (sfo_data.empty()) continue; SfoReader sfo(sfo_data); - std::string path = fmt::format("/user/app/ITEM00001/custom_app_names/{}.ini", out_vec[i].id); + std::string path = fmt::format("/user/app/ITEM00001/custom_app_names/{}.ini", item.info.id); if (if_exists(path.c_str()) && ini_parse(path.c_str(), &app_info, NULL) >= 0) { log_info("[XMB OP] Custom Name Found"); - out_vec[i].name = app_name_callback; - log_info("[XMB OP] Name Set to: %s", out_vec[i].name.c_str()); + item.info.name= app_name_callback; + log_info("[XMB OP] Name Set to: %s", item.info.name.c_str()); } else { - out_vec[i].name = sfo.GetValueFor("TITLE"); + item.info.name= sfo.GetValueFor("TITLE"); if (get->lang != 1) { - out_vec[i].name = sfo.GetValueFor(fmt::format("TITLE_{0:}{1:d}", (get->lang < 10) ? "0" : "", get->lang)); - if (out_vec[i].name.empty()) - out_vec[i].name = sfo.GetValueFor("TITLE"); + item.info.name= sfo.GetValueFor(fmt::format("TITLE_{0:}{1:d}", (get->lang < 10) ? "0" : "", get->lang)); + if (item.info.name.empty()) + item.info.name= sfo.GetValueFor("TITLE"); } } - out_vec[i].extra_sfo_data.insert(std::pair("CONTENT_ID", sfo.GetValueFor("CONTENT_ID"))); - out_vec[i].extra_sfo_data.insert(std::pair("VERSION", sfo.GetValueFor("VERSION"))); - out_vec[i].extra_sfo_data.insert(std::pair("ATTRIBUTE_INTERNAL", std::to_string(sfo.GetValueFor("ATTRIBUTE_INTERNAL")))); + // log_info("[XMB OP] %s is a favorite", item.info.id.c_str()); + item.extra_data.extra_sfo_data.insert(std::pair("CONTENT_ID", sfo.GetValueFor("CONTENT_ID"))); + item.extra_data.extra_sfo_data.insert(std::pair("VERSION", sfo.GetValueFor("VERSION"))); + item.extra_data.extra_sfo_data.insert(std::pair("ATTRIBUTE_INTERNAL", std::to_string(sfo.GetValueFor("ATTRIBUTE_INTERNAL")))); for (int z = 1; z <= 7; z++){ // SERVICE_ID_ADDCONT_ADD_1 std::string s = sfo.GetValueFor(fmt::format("SERVICE_ID_ADDCONT_ADD_{0:d}", z)); if (!s.empty()){ - out_vec[i].extra_sfo_data.insert(std::pair(fmt::format("SERVICE_ID_ADDCONT_APPINFO_ADD_{0:d}", z), s)); + item.extra_data.extra_sfo_data.insert(std::pair(fmt::format("SERVICE_ID_ADDCONT_APPINFO_ADD_{0:d}", z), s)); std::string sub = s.substr(s.find("-")+1); sub.pop_back(), sub.pop_back(), sub.pop_back(); - out_vec[i].extra_sfo_data.insert(std::pair(fmt::format("SERVICE_ID_ADDCONT_ADD_{0:d}", z), sub)); + item.extra_data.extra_sfo_data.insert(std::pair(fmt::format("SERVICE_ID_ADDCONT_ADD_{0:d}", z), sub)); } } for (int z = 1; z <= 4; z++) // USER_DEFINED_PARAM_ - out_vec[i].extra_sfo_data.insert(std::pair(fmt::format("USER_DEFINED_PARAM_{0:d}", z), std::to_string(sfo.GetValueFor(fmt::format("USER_DEFINED_PARAM_{0:d}", z))))); + item.extra_data.extra_sfo_data.insert(std::pair(fmt::format("USER_DEFINED_PARAM_{0:d}", z), std::to_string(sfo.GetValueFor(fmt::format("USER_DEFINED_PARAM_{0:d}", z))))); for (int z = 0; z <= 30; z++) { // TITLE_ std::string key = fmt::format("TITLE_{0:}{1:d}", (z < 10) ? "0" : "", z); - out_vec[i].extra_sfo_data.insert(std::pair(key, sfo.GetValueFor(key))); + item.extra_data.extra_sfo_data.insert(std::pair(key, sfo.GetValueFor(key))); } // if its a patch just set it to GD if (sfo.GetValueFor("CATEGORY") == "gp") - out_vec[i].extra_sfo_data.insert(std::pair("CATEGORY", "gd")); + item.extra_data.extra_sfo_data.insert(std::pair("CATEGORY", "gd")); else - out_vec[i].extra_sfo_data.insert(std::pair("CATEGORY", sfo.GetValueFor("CATEGORY"))); + item.extra_data.extra_sfo_data.insert(std::pair("CATEGORY", sfo.GetValueFor("CATEGORY"))); - if (out_vec[i].extra_sfo_data["CATEGORY"] == "gd") - out_vec[i].is_dumpable = true; + if (item.extra_data.extra_sfo_data["CATEGORY"] == "gd") + item.flags.is_dumpable = true; - out_vec[i].extra_sfo_data.insert(std::pair("APP_TYPE", std::to_string(sfo.GetValueFor("APP_TYPE")))); - out_vec[i].extra_sfo_data.insert(std::pair("APP_VER", sfo.GetValueFor("APP_VER"))); - out_vec[i].extra_sfo_data.insert(std::pair("CATEGORY", "gd")); - out_vec[i].extra_sfo_data.insert(std::pair("SYSTEM_VER", std::to_string(sfo.GetValueFor("SYSTEM_VER")))); - out_vec[i].extra_sfo_data.insert(std::pair("PARENTAL_LEVEL", std::to_string(sfo.GetValueFor("PARENTAL_LEVEL")))); - out_vec[i].extra_sfo_data.insert(std::pair("DOWNLOAD_DATA_SIZE", std::to_string(sfo.GetValueFor("DOWNLOAD_DATA_SIZE")))); - out_vec[i].extra_sfo_data.insert(std::pair("ATTRIBUTE", std::to_string(sfo.GetValueFor("ATTRIBUTE")))); - out_vec[i].extra_sfo_data.insert(std::pair("ATTRIBUTE2", std::to_string(sfo.GetValueFor("ATTRIBUTE2")))); - out_vec[i].extra_sfo_data.insert(std::pair("REMOTE_PLAY_KEY_ASSIGN", std::to_string(sfo.GetValueFor("REMOTE_PLAY_KEY_ASSIGN")))); - out_vec[i].extra_sfo_data.insert(std::pair("PT_PARAM", std::to_string(sfo.GetValueFor("PT_PARAM")))); - out_vec[i].extra_sfo_data.insert(std::pair("INSTALL_DIR_SAVEDATA", sfo.GetValueFor("INSTALL_DIR_SAVEDATA"))); - out_vec[i].extra_sfo_data.insert(std::pair("IRO_TAG", std::to_string(sfo.GetValueFor("IRO_TAG")))); + item.extra_data.extra_sfo_data.insert(std::pair("APP_TYPE", std::to_string(sfo.GetValueFor("APP_TYPE")))); + item.extra_data.extra_sfo_data.insert(std::pair("APP_VER", sfo.GetValueFor("APP_VER"))); + item.extra_data.extra_sfo_data.insert(std::pair("CATEGORY", "gd")); + item.extra_data.extra_sfo_data.insert(std::pair("SYSTEM_VER", std::to_string(sfo.GetValueFor("SYSTEM_VER")))); + item.extra_data.extra_sfo_data.insert(std::pair("PARENTAL_LEVEL", std::to_string(sfo.GetValueFor("PARENTAL_LEVEL")))); + item.extra_data.extra_sfo_data.insert(std::pair("DOWNLOAD_DATA_SIZE", std::to_string(sfo.GetValueFor("DOWNLOAD_DATA_SIZE")))); + item.extra_data.extra_sfo_data.insert(std::pair("ATTRIBUTE", std::to_string(sfo.GetValueFor("ATTRIBUTE")))); + item.extra_data.extra_sfo_data.insert(std::pair("ATTRIBUTE2", std::to_string(sfo.GetValueFor("ATTRIBUTE2")))); + item.extra_data.extra_sfo_data.insert(std::pair("REMOTE_PLAY_KEY_ASSIGN", std::to_string(sfo.GetValueFor("REMOTE_PLAY_KEY_ASSIGN")))); + item.extra_data.extra_sfo_data.insert(std::pair("PT_PARAM", std::to_string(sfo.GetValueFor("PT_PARAM")))); + item.extra_data.extra_sfo_data.insert(std::pair("INSTALL_DIR_SAVEDATA", sfo.GetValueFor("INSTALL_DIR_SAVEDATA"))); + item.extra_data.extra_sfo_data.insert(std::pair("IRO_TAG", std::to_string(sfo.GetValueFor("IRO_TAG")))); - out_vec[i].version = out_vec[i].extra_sfo_data["APP_VER"]; + item.info.version = item.extra_data.extra_sfo_data["APP_VER"]; + ///IRO_TAG //log_info("PARENTAL_LEVEL: %i", sfo.GetValueFor("PARENTAL_LEVEL")); } - i++; + out_vec.push_back(item); } // Skip one cover to only show it // place holder games - if (i > 2 && i < MIN_NUMBER_OF_IF_APPS) + int out_size = out_vec.size() - 1; + if (out_size < 2 || out_size < MIN_NUMBER_OF_IF_APPS) { - out_vec.resize(i + MIN_NUMBER_OF_IF_APPS - i); - for (int j = i; j < MIN_NUMBER_OF_IF_APPS; j++) + for (int j = out_size; j < MIN_NUMBER_OF_IF_APPS; j++) { - log_info("[XMB OP] Adding Placeholder Game %d", i); - out_vec[j].name = getLangSTR(PLACEHOLDER); - out_vec[j].id = "ITEM0000" + std::to_string(j); - out_vec[j].picpath = no_icon_path; - out_vec[j].version = "9.99"; - out_vec[j].extra_sfo_data.clear(); - out_vec[j].is_ph = true; + item_t pc_app; + log_info("[XMB OP] Adding Placeholder Game %d", j); + pc_app.info.name= getLangSTR(LANG_STR::PLACEHOLDER); + pc_app.info.id = "ITEM0000" + std::to_string(j); + pc_app.info.picpath = no_icon_path; + pc_app.info.version = "9.99"; + pc_app.extra_data.extra_sfo_data.clear(); + pc_app.flags.is_ph = true; + out_vec.push_back(pc_app); } - i += MIN_NUMBER_OF_IF_APPS - i; } - out_vec[0].token_c = i - 1; - log_info("valid count: %d", i); - HDD_count = i; - if(ext_count > 1) - HDD_count -= ext_count; + out_vec[0].count.token_c = out_size; + log_info("valid count: %d", out_size); + log_info("token_c: %i : %i", out_vec[0].count.token_c, out_size); - log_info("HDD_count: %i, arr sz: %i", HDD_count, out_vec.size()); - if (out_vec[0].token_c >= 1) - out_vec.resize(i); - // bounds check - if (out_vec[0].token_c < 0) - out_vec[0].token_c = 0; - - log_info("token_c: %i : %i", out_vec[0].token_c, i); - // report back - if (0) - { - for (int i = 1; i < out_vec[0].token_c; i++) - log_info("%3d: %s %p", i, out_vec[i].name.c_str(), out_vec[i].name.c_str()); + int cc = 0; + for (auto item : out_vec) { + log_info("%d: tid: %s, name %s", cc, item.info.id.c_str(), item.info.name.c_str()); + cc++; } cvEntries.clear(); - out_vec[0].picpath = no_icon_path; - out_vec[0].name = "ItemzCore_ls_dir"; - out_vec[0].id = "ITEM99999"; - out_vec[0].HDD_count = HDD_count-1; - if (out_vec.size() - 1 != out_vec[0].token_c && - out_vec.size() - 1 != out_vec[0].token_c + 1) + if (out_vec.size() - 1 != out_vec[0].count.token_c && + out_vec.size() - 1 != out_vec[0].count.token_c + 1) { - log_error("[LS_ERR] out_vec.size()|%i| != out_vec[0].token_c|%i| ", out_vec.size(), out_vec[0].token_c); - // ani_notify(NOTIFI_WARNING, "Should NOT see this", fmt::format( "|{0:d}| != |{1:d}| ", out_vec.size(), out_vec[0].token_c)); + log_error("[LS_ERR] out_vec.size()|%i| != out_vec[0].count.token_c|%i| ", out_vec.size(), out_vec[0].count.token_c); + // ani_notify(NOTIFI::WARNING, "Should NOT see this", fmt::format( "|{0:d}| != |{1:d}| ", out_vec.size(), out_vec[0].count.token_c)); } } + // from https://elixir.bootlin.com/busybox/0.39/source/df.c #include #include @@ -657,6 +677,7 @@ int check_free_space(const char *mountPoint) void get_stat_from_file(std::string &out_str, std::string &filepath) { struct stat fileinfo; + //log_info("get_stat_from_file: %s %s", filepath.c_str(), out_str.c_str()); if (stat(filepath.c_str(), &fileinfo) != -1) { diff --git a/itemzflow/source/main.cpp b/itemzflow/source/main.cpp index 090d079..4154f1f 100644 --- a/itemzflow/source/main.cpp +++ b/itemzflow/source/main.cpp @@ -14,6 +14,7 @@ #include #include #include +#include "feature_classes.hpp" #include "log.h" double dt, u_t = 0; @@ -22,10 +23,11 @@ int libcmi = -1; OrbisGlConfig* GlConf; OrbisPadConfig *confPad; bool flag=true; -extern std::vector all_apps; +extern ThreadSafeVector all_apps; std::atomic_bool is_idle = false; + int selected_icon; struct timeval t1, t2; @@ -42,7 +44,6 @@ extern "C" void standalone_main(void); extern void* __stack_chk_guard; std::atomic_bool is_disc_inserted = false; -pthread_mutex_t notifcation_lock, disc_lock, usb_lock; std::atomic_int mins_played = MIN_STATUS_RESET; std::atomic_long started_epoch = MIN_STATUS_RESET; std::atomic_int AppVis = -1; @@ -54,13 +55,12 @@ typedef struct UtiltiyPadConfig }UtiltiyPadConfig; + void* ItemzCore_Util_thread(void* args) { int ret = -1; - char stack_tid[16]; u32 userId = -1; bool usb_found = false; - // IPC IS USING C - char ipc_msg[100], name[100]; + char name[100]; UtiltiyPadConfig pad; @@ -68,13 +68,15 @@ void* ItemzCore_Util_thread(void* args) { if(sceUserServiceGetForegroundUser(&userId) == ITEMZCORE_SUCCESS){ memset(name, 0, sizeof(name)); sceUserServiceGetUserName(userId, name, sizeof(name)); - ani_notify(NOTIFI_PROFILE, fmt::format("{0:}: {1:}", getLangSTR(SIGN_IN_USER_NOTIF), name), ""); + ani_notify(NOTIFI::PROFILE, fmt::format("{0:}: {1:}", getLangSTR(LANG_STR::SIGN_IN_USER_NOTIF), name), ""); } time_t idle_clock = time(NULL); while (true) { + IPC_Client& ipc = IPC_Client::getInstance(); + std::string ipc_msg; if(reset_idle_timer.load()) { idle_clock = time(NULL); reset_idle_timer = false; @@ -99,82 +101,58 @@ void* ItemzCore_Util_thread(void* args) { if (ret < 0 || !pad.padDataCurrent.connected) { // Function is thread-safe // Notfiy func has mutexs in place for threads - ani_notify(NOTIFI_GAME, getLangSTR(CON_DIS), getLangSTR(CON_DIS_2)); + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::CON_DIS), getLangSTR(LANG_STR::CON_DIS_2)); is_idle = true; GLES2_refresh_sysinfo(); log_error("scePadReadState: %x, userid: %x, handle: %x", ret, userId, scePadGetHandle(userId,0,0)); } - //check if theres a disc - pthread_mutex_lock(&disc_lock); - memset(&stack_tid[0], 0, 16); - if ((ret = sceAppInstUtilAppGetInsertedDiscTitleId(&stack_tid[0])) >= 0 && !is_disc_inserted.load()) { - if (!all_apps.empty() && all_apps.size() > 1) { - for (int i = 0; i < all_apps[0].token_c + 1; i++) { - // skip if its an FPKG - if(all_apps[i].is_fpkg) - continue; - - // log_info("Disc inserted: %s : %i %s", stack_tid, i , all_apps[i].id.c_str()); - if (all_apps[i].id.find(&stack_tid[0]) != std::string::npos) { - //Store the texture ID of the inserted disc - inserted_disc = i; - //inserted_disc: 1 CUSA00900 1 18 - log_info("[UTIL] inserted_disc: %i %s %i %s %i", inserted_disc.load(), stack_tid, i, all_apps[i].id.c_str(), all_apps.size()); - is_disc_inserted = true; - break; - } - } - } - } - else if (ret == SCE_APP_INSTALLER_ERROR_DISC_NOT_INSERTED) { - // log_info("[UTIL] disc not inserted %x", ret); - inserted_disc = -999; - is_disc_inserted = false; - } - // else - //log_info("[UTIL] disc %x %i", ret, inserted_disc.load()); - - if(mins_played.load() == MIN_STATUS_RESET && //check if a game is open before asking the daemon - !title_id.empty() && app_status != APP_NA_STATUS) { // check if an app is already selected too, TODO: Atmoic int for app_status - - if(is_vapp(title_id)){ - mins_played = MIN_STATUS_VAPP; - continue; - } - memset(&ipc_msg[0], 0, sizeof ipc_msg); - strcpy((char*)&ipc_msg[1], title_id.c_str()); - - if(is_connected_app && IPCSendCommand(GAME_GET_MINS, (uint8_t*)&ipc_msg[0]) == NO_ERROR){ - mins_played = atoi((const char*)&ipc_msg[0]); //convert to INT - } - else - mins_played = MIN_STATUS_NA; - - memset(&ipc_msg[0], 0, sizeof ipc_msg); - strcpy((char*)&ipc_msg[1], title_id.c_str()); - if(is_connected_app && IPCSendCommand(GAME_GET_START_TIME, (uint8_t*)ipc_msg) == NO_ERROR) - started_epoch = atol((const char*)&ipc_msg[0]); - else - started_epoch = MIN_STATUS_NA; - - } - - pthread_mutex_unlock(&disc_lock); + scan_for_disc(); //check if theres a usb if(!usb_found && usbpath() != -1){ usb_found = true; //USB Notify - ani_notify(NOTIFI_INFO, getLangSTR(USB_NOTIFY), getLangSTR(USB_NOTIFY1)); + ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::USB_NOTIFY), getLangSTR(LANG_STR::USB_NOTIFY1)); log_info("[UTIL] USB Inserted"); } else if (usb_found && usbpath() == -1){ usb_found = false; - ani_notify(NOTIFI_INFO, getLangSTR(USB_DISCONNECTED), getLangSTR(USB_DISCONNECTED1)); + ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::USB_DISCONNECTED), getLangSTR(LANG_STR::USB_DISCONNECTED1)); log_info("[UTIL] USB removed"); } + // Replace the use of ipc_msg with vecto + if(title_id.get().empty() || app_status.load() == NO_APP_OPENED) + continue; + + int BigAppid = sceSystemServiceGetAppIdOfBigApp(); + // IS Big App Running? + if ((BigAppid & ~0xFFFFFF) == 0x60000000){ + memset(name, 0, 100); + // Get TID of Big App + if(sceLncUtilGetAppTitleId(BigAppid, &name[0]) == 0){ + // Check if its the same as the current app + if(title_id.get().compare(name) == 0){ + // log_info("[TEST] Big App is running"); + } + else{ + // log_info("[TEST] Big App is not running"); + continue; + } + } + } + + ipc_msg = title_id.get(); + + if (is_connected_app && ipc.IPCSendCommand(IPC_Commands::GAME_GET_MINS, ipc_msg, true) == IPC_Ret::NO_ERROR) { + int result; + sscanf(ipc_msg.c_str(), "%d", &result); + mins_played = result; + } else { + mins_played = MIN_STATUS_NA; + } + sleep(1); } @@ -182,11 +160,13 @@ void* ItemzCore_Util_thread(void* args) { return NULL; } + struct timeval last_tv; int button_numb = -1; int if_numb = -1; bool check_if_held = false; + void updateController() { unsigned int buttons=0; @@ -299,14 +279,14 @@ void updateController() } if(orbisPadGetButtonPressed(ORBISPAD_SQUARE)) { - log_info( "Square pressed"); + log_info( "Square pressed"); fw_action_to_cf(SQU); reset_idle_timer = true; } if(orbisPadGetButtonPressed(ORBISPAD_L1)) { log_info( "L1 pressed"); - if(v_curr != FILE_BROWSER) + if(v_curr != FILE_BROWSER_LEFT && v_curr != FILE_BROWSER_RIGHT) trigger_dump_frame(); fw_action_to_cf(L1); @@ -390,15 +370,31 @@ bool initApp() } + +float toMilliSeconds(uint64_t t) +{ + return (float)t / 1000.0f ; +} + /// for timing, fps #define WEN (2048) -unsigned int frame = 1, -time_ms = 0; +unsigned int frame = 1; +float time_ms = 0.f; int main(int argc, char* argv[]) { int ret = -1; bool speed1 = true,speed2 = false,speed3 = false; + vec2 pen = (vec2){ 26, 990 }; + vec4 col = (vec4){ .8164, .8164, .8125, 1. }; + int fpsCount = 0; + uint64_t fpsTime1 = 0; + uint64_t fpsTime2 = 0; + VertexBuffer fps_vbo; + std::chrono::time_point now, prev; + std::chrono::steady_clock::time_point lastUpdate; + // vec4 c = col * .75f; + pthread_t utility_thread = NULL; /* load custom .prx, resolve and call a function */ rejail_multi = (int (*)(void))prx_func_loader("/app0/Media/jb.prx", "rejail_multi"); @@ -411,14 +407,7 @@ int main(int argc, char* argv[]) else goto error; - if (Start_IF_internal_Services(atoi(argv[0])) == INIT_FAILED) goto error; - - if (pthread_mutex_init(¬ifcation_lock, NULL) != 0 || - pthread_mutex_init(&disc_lock, NULL) != 0 || - pthread_mutex_init(&usb_lock, NULL) != 0) { - log_error("mutex init has failed"); - goto error; - } + if (Start_IF_internal_Services(argv[0]) == INIT_FAILED) goto error; // init some libraries flag = initApp(); @@ -440,13 +429,17 @@ int main(int argc, char* argv[]) /// reset timers - time_ms = get_time_ms(); - gettimeofday(&t1, NULL); - t2 = t1; + now = std::chrono::system_clock::now(); + time_ms = get_time_ms(now); + + lastUpdate = std::chrono::steady_clock::now(); //close any still running dialogs sceMsgDialogTerminate(); - gettimeofday(&last_tv, NULL); + + // Initialize FPS counter + fpsTime1 = sceKernelGetProcessTime(); + prev = std::chrono::system_clock::now(); /// enter main render loop while (flag) @@ -454,6 +447,7 @@ int main(int argc, char* argv[]) //skip the first frame //so theres no race between //the countroller update and the GLES UI + if (frame != 1) updateController(); @@ -497,39 +491,57 @@ int main(int argc, char* argv[]) // Render ItemzCore Render_ItemzCore(); + fpsCount++; + + if (fpsCount == 120 ) { + fpsTime2 = sceKernelGetProcessTime(); + float time = get_time_ms(prev) * 1000.0f; + float avgFrameTime = time / 60.0f; + fps_vbo = VertexBuffer( "vertex:3f,tex_coord:2f,color:4f" ); + float s_fps = 60.0f / toMilliSeconds(fpsTime2 - fpsTime1) * 1000.0f; + std::string str = fmt::format("DEBUG FPS: {0:.2f}", s_fps); + vec2 bk = pen; + texture_font_load_glyphs(sub_font, str.c_str()); + fps_vbo.add_text(sub_font, str, col, bk); + str = fmt::format("DEBUG ms: {0:.2f}", avgFrameTime); + // log_info("%.f ", avgFrameTime); + texture_font_load_glyphs(sub_font, str.c_str()); + bk.x = pen.x; + bk.y -= 30; + fps_vbo.add_text(sub_font, str, col, bk); + + fpsTime1 = fpsTime2; + fpsCount = 0; + } + //fps_vbo.render_vbo(NULL); + + /// get timing, fps if (!(frame % WEN)) { - //ItemzCore funcs - double const_1000 = 1000.0f; - uint64_t now = get_time_ms(); - uint64_t frame_time = now - time_ms; - double time_to_draw = frame_time / const_1000; - double framerate = const_1000 / time_to_draw; + float ti = get_time_ms(now); + double framerate = 1000.f / ti; log_info("--------------------------------------------------"); - log_info("[Render] time: %lums FPS: %.3lf", frame_time, framerate); + log_info("[Render] AVG_time: %.3lf ms AVG_FPS: %.3lf", ti, framerate); print_memory(); //reset Frame so we dont overflow later in release frame = 1; // MIN_STATUS_RESET wait for 120 frames to reset the status mins_played = MIN_STATUS_RESET; log_info("---------------------------------------------------"); - time_ms = now; + //time_ms = now; } - frame++; if (1) // update for the GLES uniform time { - gettimeofday(&t2, NULL); - // calculate delta time - dt = t2.tv_sec - t1.tv_sec + (t2.tv_usec - t1.tv_usec) * 1e-6; - - t1 = t2; - // update total time - u_t += dt; + auto now = std::chrono::steady_clock::now(); + dt = std::chrono::duration_cast(now - lastUpdate).count() / 1000000.0f; + lastUpdate = now; + u_t += dt; } - orbisGlSwapBuffers(); /// flip frame + orbisGlSwapBuffers(); /// flip frame + frame++; MP3_Player(get->setting_strings[MP3_PATH]); dump_frame(); @@ -538,7 +550,7 @@ int main(int argc, char* argv[]) error: sceKernelIccSetBuzzer(2); - msgok(FATAL, fmt::format("App has Died: ({0:#x})", ret)); + msgok(MSG_DIALOG::FATAL, fmt::format("App has Died: ({0:#x})", ret)); // destructors ORBIS_RenderFillRects_fini(); diff --git a/itemzflow/source/patcher.cpp b/itemzflow/source/patcher.cpp index 5b283fd..cd32212 100644 --- a/itemzflow/source/patcher.cpp +++ b/itemzflow/source/patcher.cpp @@ -48,10 +48,10 @@ void write_enable(std::string cfg_file, std::string patch_name) { state_data = "1\n"; output_hash_file_stream << state_data; output_hash_file_stream.close(); - // msgok(NORMAL, "TRUE"); + // msgok(MSG_DIALOG::NORMAL, "TRUE"); log_debug("setting %s true", cfg_file.c_str()); - std::string patch_state = fmt::format("{} {}", getLangSTR(PATCH_SET1), patch_name); - ani_notify(NOTIFI_GAME, patch_state, ""); + std::string patch_state = fmt::format("{} {}", getLangSTR(LANG_STR::PATCH_SET1), patch_name); + ani_notify(NOTIFI::GAME, patch_state, ""); } if (data[0] == 0x31) { @@ -59,10 +59,10 @@ void write_enable(std::string cfg_file, std::string patch_name) { state_data = "0\n"; output_hash_file_stream << state_data; output_hash_file_stream.close(); - // msgok(NORMAL, "FALSE"); + // msgok(MSG_DIALOG::NORMAL, "FALSE"); log_debug("setting %s false", cfg_file.c_str()); - std::string patch_state = fmt::format("{} {}", getLangSTR(PATCH_SET0), patch_name); - ani_notify(NOTIFI_GAME, patch_state, ""); + std::string patch_state = fmt::format("{} {}", getLangSTR(LANG_STR::PATCH_SET0), patch_name); + ani_notify(NOTIFI::GAME, patch_state, ""); } } return; @@ -157,7 +157,7 @@ void dl_patches(){ std::string patch_path = patch_path_base + "/" + patch_zip; std::string patch_build_path = patch_path_base + "/patches/misc/patch_ver.txt"; std::string patch_dl_msg; - loadmsg(getLangSTR(PATCH_DL_QUESTION_YES)); + loadmsg(getLangSTR(LANG_STR::PATCH_DL_QUESTION_YES)); int ret = dl_from_url(patch_url.c_str(), patch_path.c_str()); if (ret != 0) // 0 != failed { @@ -171,11 +171,11 @@ void dl_patches(){ std::ifstream t(patch_build_path); std::stringstream buffer; buffer << t.rdbuf(); - patch_dl_msg = fmt::format("{}\n{}", getLangSTR(PATCH_DL_COMPLETE), buffer.str()); + patch_dl_msg = fmt::format("{}\n{}", getLangSTR(LANG_STR::PATCH_DL_COMPLETE), buffer.str()); } error: sceMsgDialogTerminate(); - msgok(NORMAL, patch_dl_msg); + msgok(MSG_DIALOG::NORMAL, patch_dl_msg); return; } diff --git a/itemzflow/source/png.cpp b/itemzflow/source/png.cpp index 9f1643f..bef21cb 100644 --- a/itemzflow/source/png.cpp +++ b/itemzflow/source/png.cpp @@ -3,184 +3,22 @@ #include #include +#ifdef __ORBIS__ +#include +#endif #include "defines.h" /////// png #include #include #include -#include "GLES2_common.h" - -#if __cplusplus -extern "C" { -#endif - -typedef struct { - const png_byte* data; - const png_size_t size; -} DataHandle; - -typedef struct { - const DataHandle data; - png_size_t offset; -} ReadDataHandle; - -typedef struct { - const png_uint_32 width; - const png_uint_32 height; - const int color_type; -} PngInfo; - -/// helpers -/*static void read_png_data_callback( - png_structp png_ptr, png_byte* png_data, png_size_t read_length); -static PngInfo read_and_update_info(const png_structp png_ptr, const png_infop info_ptr); -static DataHandle read_entire_png_image( - const png_structp png_ptr, const png_infop info_ptr, const png_uint_32 height); -static GLenum get_gl_color_format(const int png_color_format); -*/ -static PngInfo read_and_update_info(const png_structp png_ptr, const png_infop info_ptr) -{ - png_uint_32 width, height; - int bit_depth, color_type; - - png_read_info(png_ptr, info_ptr); - png_get_IHDR( - png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); - - // Convert transparency to full alpha - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha(png_ptr); - - // Convert grayscale, if needed. - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand_gray_1_2_4_to_8(png_ptr); - - // Convert paletted images, if needed. - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); - - // Add alpha channel, if there is none. - // Rationale: GL_RGBA is faster than GL_RGB on many GPUs) - if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_RGB) - png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); - - // Ensure 8-bit packing - if (bit_depth < 8) - png_set_packing(png_ptr); - else if (bit_depth == 16) - png_set_scale_16(png_ptr); - - png_read_update_info(png_ptr, info_ptr); - - // Read the new color type after updates have been made. - color_type = png_get_color_type(png_ptr, info_ptr); - - return (PngInfo) { width, height, color_type }; -} - -static void read_png_data_callback( - png_structp png_ptr, png_byte* raw_data, png_size_t read_length) { - ReadDataHandle* handle = (ReadDataHandle *)png_get_io_ptr(png_ptr); - const png_byte* png_src = handle->data.data + handle->offset; - - memcpy(raw_data, png_src, read_length); - handle->offset += read_length; -} - -static DataHandle read_entire_png_image( - const png_structp png_ptr, - const png_infop info_ptr, - const png_uint_32 height) -{ - const png_size_t row_size = png_get_rowbytes(png_ptr, info_ptr); - const int data_length = row_size * (height + 1); - assert(row_size > 0); - - png_byte* raw_image = (png_byte*)malloc(data_length); - - if (raw_image == NULL) - log_error("%s %p", __FUNCTION__, raw_image); - - png_byte* row_ptrs[height]; - - png_uint_32 i; - for (i = 0; i < height; i++) { - row_ptrs[i] = raw_image + i * row_size; - } - - png_read_image(png_ptr, &row_ptrs[0]); - - return (DataHandle) { raw_image, data_length }; -} - -static GLenum get_gl_color_format(const int png_color_format) { - assert(png_color_format == PNG_COLOR_TYPE_GRAY - || png_color_format == PNG_COLOR_TYPE_RGB_ALPHA - || png_color_format == PNG_COLOR_TYPE_GRAY_ALPHA); - - switch (png_color_format) { - case PNG_COLOR_TYPE_GRAY: - return GL_LUMINANCE; - case PNG_COLOR_TYPE_RGB_ALPHA: - return GL_RGBA; - case PNG_COLOR_TYPE_GRAY_ALPHA: - return GL_LUMINANCE_ALPHA; - } - return 0; -} - -/// main -RawImageData get_raw_image_data_from_png(const void* png_data, const int png_data_size, const char* fp) -{ - - assert(png_data != NULL && png_data_size > 8); - assert(png_check_sig((void*)png_data, 8)); - - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - assert(png_ptr != NULL); - png_infop info_ptr = png_create_info_struct(png_ptr); - assert(info_ptr != NULL); - - ReadDataHandle png_data_handle = (ReadDataHandle){ { (const png_byte*)png_data, png_data_size}, 0 }; - png_set_read_fn(png_ptr, &png_data_handle, read_png_data_callback); - - if (setjmp(png_jmpbuf(png_ptr))) { - log_info("Error reading PNG file!"); - if(fp) - unlink(fp); - return (RawImageData) { 512, 512, 0, 0, NULL }; - } - - const PngInfo png_info = read_and_update_info(png_ptr, info_ptr); - const DataHandle raw_image = read_entire_png_image(png_ptr, info_ptr, png_info.height); - - png_read_end(png_ptr, info_ptr); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - - return (RawImageData) { - png_info.width, - png_info.height, - raw_image.size, - get_gl_color_format(png_info.color_type), - raw_image.data - }; -} - - -void release_raw_image_data(const RawImageData* data) -{ - if (data != NULL) - { - if (data->data) free((void*)data->data); - } -} - +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" /// png format GLuint load_texture( const GLsizei width, const GLsizei height, - const GLenum type, const GLvoid* pixels) + const GLenum type, const GLvoid *pixels) { // create new OpenGL texture GLuint texture_object_id; @@ -191,174 +29,131 @@ GLuint load_texture( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // generate texture from bitmap data - glTexImage2D(GL_TEXTURE_2D, 0, type, width, height, 0, type, GL_UNSIGNED_BYTE, pixels); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // don't create MipMaps, we must assert npot2! // glGenerateMipmap(GL_TEXTURE_2D); // release and return texture glBindTexture(GL_TEXTURE_2D, 0); +// log_info("width: %d, height: %d, type: %d, pixels: %p", width, height, type, pixels); + return texture_object_id; } - -bool is_png_vaild(const char *relative_path, int* out_width, int* out_height) -{ - unsigned char *png_file = orbisFileGetFileContent(relative_path); - // texture creation - if (png_file != NULL && _orbisFile_lastopenFile_size > 0) - { - const RawImageData raw_image_data = get_raw_image_data_from_png(png_file, _orbisFile_lastopenFile_size, relative_path); - if (raw_image_data.data == NULL) - { - // dont leak png_file - log_error("PNG Is corrupted!"); - free((void *)png_file), png_file = NULL; - return false; - } - // delete buffers & set the image dem. - *out_width = raw_image_data.width; - *out_height = raw_image_data.height; - - release_raw_image_data(&raw_image_data); - // release_file_data(&png_file); - free((void *)png_file), png_file = NULL; - - return true; - } - //else - // log_debug("%s(%s) returned INVALID!", __FUNCTION__, relative_path); - - // no texture - return false; -} - -/* externed */ vec2 tex_size; // last loaded png size as (w, h) +/* externed */ +vec2 tex_size; // last loaded png size as (w, h) /// textures GLuint load_png_asset_into_texture(const char* relative_path) -{ - // const FileData png_file = get_file_data(relative_path); - // const RawImageData raw_image_data = get_raw_image_data_from_png(png_file.data, png_file.data_length); -#if defined (USE_NFS) - const unsigned char* png_file = orbisNfsGetFileContent(relative_path); -#else - const unsigned char* png_file = orbisFileGetFileContent(relative_path); -#endif -retry: - // texture creation - if (png_file != NULL && _orbisFile_lastopenFile_size > 8) - { - const RawImageData raw_image_data = get_raw_image_data_from_png(png_file, _orbisFile_lastopenFile_size, relative_path); - if (raw_image_data.data == NULL) { - //dont leak png_file - free((void*)png_file), png_file = NULL; -#if defined(__ORBIS__) - - if (if_exists("/user/appmeta/" ITEMZFLOW_TID "/icon0.png")) - png_file = orbisFileGetFileContent("/user/appmeta/" ITEMZFLOW_TID "/icon0.png"); -#endif - - goto retry; - } - const GLuint texture_object_id = load_texture(raw_image_data.width, raw_image_data.height, - raw_image_data.gl_color_format, - raw_image_data.data); - // take note of image resolution size to setup VBO in px size - tex_size = (vec2){ raw_image_data.width, raw_image_data.height }; - // delete buffers - release_raw_image_data(&raw_image_data); - //release_file_data(&png_file); - free((void*)png_file), png_file = NULL; - - //log_debug( "%s(%s) ret: %d", __FUNCTION__, relative_path, texture_object_id); - - return texture_object_id; +{ + int w = 0, h = 0, comp= 0; + unsigned char* image = stbi_load(relative_path, &w, &h, &comp, STBI_rgb_alpha); + if(image == nullptr){ + log_debug( "%s(%s) FAILED!", __FUNCTION__, relative_path); + return 0; } - else - log_debug("%s(%s) FAILED!", __FUNCTION__, relative_path); - // no texture - return 0; + GLuint tex = load_texture(w, h, comp, image); + log_info("loaded %s(%s) as %d", __FUNCTION__, relative_path, tex); + stbi_image_free(image); + return tex; +} + +unsigned char* load_png_asset(const char* relative_path, std::atomic &is_loaded, struct AppIcon::ImageData& data) +{ + //std::lock_guard lock(data.data_mutex); + + unsigned char* image = stbi_load(relative_path, &data.w, &data.h, &data.comp, STBI_rgb_alpha); + if(image == nullptr){ + log_debug( "%s(%s) FAILED!", __FUNCTION__, relative_path); + return 0; + } + is_loaded = true; + //log_info("returning data for %s", relative_path); + + return image; +} + +void load_png_cover_data_into_texture(struct AppIcon::ImageData& data, std::atomic& needs_loaded, std::atomic& tex) +{ + // std::lock_guard lock(data.data_mutex); + + if (!data.data) { + log_debug("%s(%p) FAILED!", __FUNCTION__, data.data); + return; + } + + tex = load_texture(data.w, data.h, data.comp, data.data); + log_info("loaded %s(%p) as %d", __FUNCTION__, data.data, tex.load()); + stbi_image_free(data.data), data.data = nullptr; + needs_loaded = false; } -/// textures GLuint load_png_data_into_texture(const char* data, int size) { - - // texture creation - if (size > 0) - { - const RawImageData raw_image_data = get_raw_image_data_from_png(data, size, NULL); - - const GLuint texture_object_id = load_texture(raw_image_data.width, raw_image_data.height, raw_image_data.gl_color_format, raw_image_data.data); - - // take note of image resolution size to setup VBO in px size - tex_size = (vec2){ raw_image_data.width, raw_image_data.height }; - // delete buffers - release_raw_image_data(&raw_image_data); - - //log_debug( "%s(%s) ret: %d", __FUNCTION__, relative_path, texture_object_id); - - return texture_object_id; + int w = 0, h = 0, comp = 0; + unsigned char* image = stbi_load_from_memory(reinterpret_cast(data), size, &w, &h, &comp, STBI_rgb_alpha); + if (image == nullptr) { + log_debug("%s(data, %d) FAILED!", __FUNCTION__, size); + return 0; } - else - log_debug("%s(%s) FAILED!", __FUNCTION__, "EMBEDDED DATA"); - // no texture - return 0; + GLuint tex = load_texture(w, h, comp, image); + log_info("loaded %s(data, %d) as %d", __FUNCTION__, size, tex); + stbi_image_free(image); + return tex; } - - // with int (u32) -void setRGB(png_byte* ptr, int* val) +void setRGB(png_byte *ptr, int *val) { - unsigned char* c = (unsigned char*)val; + unsigned char *c = (unsigned char *)val; ptr[0] = *c++; ptr[1] = *c++; ptr[2] = *c++; } -int writeImage(char* filename, int width, int height, int* buffer, char* title) +int writeImage(char* filename, int width, int height, int *buffer, const char* title) { int code = 0; - FILE* fp = NULL; + FILE *fp = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_bytep row = NULL; // Open file for writing (binary mode) fp = fopen(filename, "wb"); - if (fp == NULL) { log_error("Could not open file %s for writing", filename); code = 1; goto finalise; } + if (fp == NULL) { log_error( "Could not open file %s for writing", filename); code = 1; goto finalise; } // Initialize write structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png_ptr == NULL) { log_error("Could not allocate write struct"); code = 1; goto finalise; } + if (png_ptr == NULL) { log_error( "Could not allocate write struct"); code = 1; goto finalise; } // Initialize info structure info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { log_error("Could not allocate info struct"); code = 1; goto finalise; } + if (info_ptr == NULL) { log_error( "Could not allocate info struct"); code = 1; goto finalise; } // Setup Exception handling - if (setjmp(png_jmpbuf(png_ptr))) { log_error("Error during png creation"); code = 1; goto finalise; } + if (setjmp(png_jmpbuf(png_ptr))) { log_error( "Error during png creation"); code = 1; goto finalise; } png_init_io(png_ptr, fp); // Write header (8 bit colour depth) png_set_IHDR(png_ptr, info_ptr, width, height, - 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // Set title if (title != NULL) { png_text title_text; title_text.compression = PNG_TEXT_COMPRESSION_NONE; title_text.key = (char*)"Title"; - title_text.text = title; + title_text.text = (char*) title; png_set_text(png_ptr, info_ptr, &title_text, 1); } png_write_info(png_ptr, info_ptr); // Allocate memory for one row (3 bytes per pixel - RGB) - row = (png_bytep)calloc(3 * width + 1, sizeof(png_byte)); + row = (png_bytep)calloc(3 * width +1, sizeof(png_byte)); // Write image data int x, y; // note we flip vertically here! - for (y = height; y > -1; y--) { - for (x = 0; x < width; x++) { - setRGB(&(row[x * 3]), &buffer[y * width + x]); // converts RGBA to RGB + for (y=height ; y>-1 ; y--) { + for (x=0 ; x #include "lang.h" #include +#include "feature_classes.hpp" #include + + extern "C" int pthread_getthreadid_np(); -#define OBRIS_BACKTRACE 0 +#define OBRIS_BACKTRACE 1 #define SYS_mount 21 +using namespace multi_select_options; extern "C" int sys_mount(const char *type, const char *dir, int flags, void *data){ return syscall(SYS_mount, type, dir, flags, data); @@ -30,7 +34,7 @@ extern char* gm_p_text[20]; extern "C" int backtrace(void **buffer, int size); Dump_Multi_Sels dump = SEL_DUMP_ALL; // IPC IS USING C -extern char ipc_msg[]; +extern std::string ipc_msg; int wait_for_game(char *title_id) { @@ -71,93 +75,78 @@ int wait_for_game(char *title_id) return res; } -bool dump_patch(std::string &title_id, std::string& usb_p, std::string& gtitle) +bool dump_to_diff_folder(std::string &title_id, std::string& usb_p, std::string& gtitle, Dump_Multi_Sels dump) { std::string tmp; - tmp = fmt::format("/user/patch/{}/patch.pkg", title_id); + // make a switch case for dump print_memory(); - if (if_exists(tmp.c_str())) + if (dump != SEL_GAME_PATCH || if_exists(fmt::format("/user/patch/{}/patch.pkg", title_id).c_str())) { - log_debug("Dumping Patch..."); - if (Dumper(usb_p, title_id, GAME_PATCH, gtitle)) + switch(dump) { - tmp = fmt::format("{}/{}-patch.complete", usb_p, title_id); - touch_file(tmp.c_str()); - tmp = fmt::format("{}/{}-patch.dumping", usb_p, title_id); - unlink(tmp.c_str()); - - return true; + case SEL_GAME_PATCH: { + return Dumper(usb_p, title_id, GAME_PATCH, gtitle); + break; + } + case SEL_DLC:{ + return Dumper(usb_p, title_id, ADDITIONAL_CONTENT_DATA, gtitle); + break; + } + default: { + log_debug("Unknown Dump Type"); + break; + } } - else - return false; } else { log_debug("Patch doesnt exist..."); return true; } + + return false; } bool Dump_w_opts(std::string tid, std::string d_p, std::string gtitle, Dump_Multi_Sels dump) { - std::string tmp; - tmp = fmt::format("/mnt/sandbox/pfsmnt/{}-app0-nest/pfs_image.dat", title_id); + std::string tmp = fmt::format("/mnt/sandbox/pfsmnt/{}-app0-nest/pfs_image.dat", tid); + if (!if_exists(tmp.c_str())) { + log_error("Dump_w_opts: %s doesnt exist", tmp.c_str()); + return false; + } if (dump == SEL_DUMP_ALL) { - if (if_exists(tmp.c_str())) { - //false on error - //true on success - //true refers to the error flag, if the flag is true then fail - if (Dumper(d_p, title_id, BASE_GAME, gtitle)) { - - tmp = fmt::format("{}/{}.dumping", d_p, title_id); - unlink(tmp.c_str()); - - Dumper(d_p, title_id, ADDITIONAL_CONTENT_DATA, gtitle); - - return dump_patch(title_id, d_p, gtitle); - } - else { - log_debug("Dumping Base game failed, trying to dump as Remaster..."); - if (Dumper(d_p, title_id, REMASTER, gtitle)) { - - - tmp = fmt::format("{}/{}.dumping", d_p, title_id); - unlink(tmp.c_str()); - - return dump_patch(title_id, d_p, gtitle); - } + if (Dumper(d_p, tid, BASE_GAME, gtitle)) { + return dump_to_diff_folder(tid, d_p, gtitle, SEL_GAME_PATCH); + } + else { + log_debug("Dumping Base game failed, trying to dump as Remaster..."); + if (Dumper(d_p, tid, REMASTER, gtitle)) { + return dump_to_diff_folder(tid, d_p, gtitle, SEL_GAME_PATCH); } } + } else if (dump == SEL_BASE_GAME) { - if (Dumper(d_p, title_id, BASE_GAME, gtitle)) { + if (Dumper(d_p, tid, BASE_GAME, gtitle)) { - tmp = fmt::format("{}/{}.complete", d_p, title_id); + tmp = fmt::format("{}/{}.complete", d_p, tid); touch_file(tmp.c_str()); - tmp = fmt::format("{}/{}.dumping", d_p, title_id); + tmp = fmt::format("{}/{}.dumping", d_p, tid); unlink(tmp.c_str()); return true; } } else if (dump == SEL_GAME_PATCH) { - if (Dumper(d_p, title_id, GAME_PATCH, gtitle)) { - - tmp = fmt::format("{}/{}.complete", d_p, title_id); - touch_file(tmp.c_str()); - tmp = fmt::format("{}/{}.dumping", d_p, title_id); - unlink(tmp.c_str()); - - return true; - } + return dump_to_diff_folder(tid, d_p, gtitle, dump); } else if (dump == SEL_REMASTER) { - return Dumper(d_p, title_id, REMASTER, gtitle); + return Dumper(d_p, tid, REMASTER, gtitle); } else if (dump == SEL_DLC) { - return Dumper(d_p, title_id, ADDITIONAL_CONTENT_DATA, gtitle); + return dump_to_diff_folder(tid, d_p, gtitle, dump); } return false; @@ -169,7 +158,6 @@ void Exit_Success(const char* text) { log_info("%s", text); log_info("Disable Home Menu Redirect Called for Exit...."); - IPCSendCommand(DISABLE_HOME_REDIRECT, (uint8_t*)&ipc_msg[0]); if (rejail_multi) { log_debug("Rejailing App >>>"); @@ -186,7 +174,7 @@ void Exit_Success(const char* text) void Exit_App_With_Message(int sig_numb) { - msgok(NORMAL, fmt::format("############# Program has gotten a FATAL Signal/Error: {0:d} ##########\n\n {1:}\n\n", sig_numb, getLangSTR(PKG_TEAM))); + msgok(MSG_DIALOG::NORMAL, fmt::format("############# Program has gotten a FATAL Signal/Error: {0:d} ##########\n\n {1:}\n\n", sig_numb, getLangSTR(LANG_STR::PKG_TEAM))); std::string tmp; @@ -257,13 +245,12 @@ void OrbisKernelBacktrace(const char* reason) { #endif bool is_dumper_running = false; - - - void SIG_Handler(int sig_numb) { std::string profpath; + IPC_Client& ipc = IPC_Client::getInstance(); rejail_multi = (int (*)(void))prx_func_loader("/app0/Media/jb.prx", "rejail_multi"); + int fw = (ps4_fw_version() >> 16); //void* bt_array[255]; // switch (sig_numb) @@ -299,10 +286,9 @@ void SIG_Handler(int sig_numb) case SIGSEGV: { SIGSEGV_handler: log_debug("App has crashed"); - IPCSendCommand(DISABLE_HOME_REDIRECT, (uint8_t*)&ipc_msg[0]); if (dump != SEL_RESET) { - msgok(WARNING, getLangSTR(FATAL_DUMP_ERROR)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::FATAL_DUMP_ERROR)); sceSystemServiceLoadExec("/app0/ItemzCore.self", 0); } unlink("/user/app/if_update.pkg"); @@ -319,17 +305,17 @@ SIGSEGV_handler: backtrace_and_exit: log_debug("backtrace_and_exit called"); + ipc.IPCSendCommand(IPC_Commands::DISABLE_HOME_REDIRECT, ipc_msg); if(get->setting_bools[INTERNAL_UPDATE]) mkdir("/user/recovery.flag", 0777); profpath = fmt::format("/mnt/proc/{0:d}/", getpid()); - if (getuid() == 0 && !if_exists(profpath.c_str())) { int result = mkdir("/mnt/proc", 0777); if (result < 0 && errno != 17) - log_debug("Failed to create /mnt/proc"); + log_debug("Failed to create /mnt/proc: %s", strerror(errno)); result = sys_mount("procfs", "/mnt/proc", 0, NULL); if (result < 0) @@ -368,12 +354,13 @@ extern "C" { #if __cpplusplus } #endif -#else -void* bt_array[255]; -backtrace(bt_array, 255); #endif +void* bt_array[255]; - Exit_App_With_Message(sig_numb); +if(fw <= 0x505) + backtrace(bt_array, 255); + +Exit_App_With_Message(sig_numb); @@ -385,13 +372,11 @@ log_debug("App Resumed Checking for opened games..."); //lock thread mutex so it doesnt constantly continue running //while we are dumping or checking if a game is open // only unlcok at the end of the func -pthread_mutex_lock(&disc_lock); - -if(!is_remote_va_launched && app_status != APP_NA_STATUS){ +if(!is_remote_va_launched && app_status.load() != APP_NA_STATUS){ int BigAppid = sceSystemServiceGetAppIdOfBigApp(); if ((BigAppid & ~0xFFFFFF) == 0x60000000) { log_info("[SIGNAL] FOUND Open Game: %x : Bool: %d", BigAppid, (BigAppid & ~0xFFFFFF) == 0x60000000); - if(sceLncUtilGetAppId(title_id.c_str()) == sceSystemServiceGetAppIdOfBigApp()) + if(sceLncUtilGetAppId(title_id.get().c_str()) == sceSystemServiceGetAppIdOfBigApp()) app_status = RESUMABLE; else app_status = OTHER_APP_OPEN; @@ -401,35 +386,35 @@ else } -// char* title_id = "CUSA02290"; +// char* title_id.get() = "CUSA02290"; if ( dump != SEL_RESET) { is_dumper_running = true; std::string tmp; std::string tmp2; - progstart((char*)getLangSTR(STARTING_DUMPER).c_str()); + progstart((char*)getLangSTR(LANG_STR::STARTING_DUMPER).c_str()); - fmt::print("title_id: {}, title: {}, DUMPER_PATH: {}", title_id, title, get->setting_strings[DUMPER_PATH]); - tmp = fmt::format("{}/{}", get->setting_strings[DUMPER_PATH], title_id); + fmt::print("title_id.get(): {}, DUMPER_PATH: {}", title_id.get(), get->setting_strings[DUMPER_PATH]); + tmp = fmt::format("{}/{}", get->setting_strings[DUMPER_PATH], title_id.get()); - if (if_exists(tmp.c_str())) + if (dump == SEL_BASE_GAME && if_exists(tmp.c_str())) { - ProgSetMessagewText(5, getLangSTR(DELETING_GAME_FOLDER).c_str()); + ProgSetMessagewText(5, getLangSTR(LANG_STR::DELETING_GAME_FOLDER).c_str()); rmtree(tmp.c_str()); - tmp = fmt::format("{}/{}.complete", get->setting_strings[DUMPER_PATH], title_id); + tmp = fmt::format("{}/{}.complete", get->setting_strings[DUMPER_PATH], title_id.get()); unlink(tmp.c_str()); } - tmp = fmt::format("{}/{}-patch/", get->setting_strings[DUMPER_PATH], title_id); - if (if_exists(tmp.c_str())) + tmp = fmt::format("{}/{}-patch/", get->setting_strings[DUMPER_PATH], title_id.get()); + if (dump == SEL_GAME_PATCH && if_exists(tmp.c_str())) { - ProgSetMessagewText(5, getLangSTR(DELETING_GAME_FOLDER).c_str()); + ProgSetMessagewText(5, getLangSTR(LANG_STR::DELETING_GAME_FOLDER).c_str()); rmtree(tmp.c_str()); } //dump_game has all the dialoge for copied proc - if (Dump_w_opts(title_id, get->setting_strings[DUMPER_PATH], title, dump)){ - msgok(NORMAL, fmt::format("{0:} {1:} {2:}", getLangSTR(DUMP_OF), title_id, getLangSTR(COMPLETE_WO_ERRORS))); + if (Dump_w_opts(title_id.get(), get->setting_strings[DUMPER_PATH], g_idx > all_apps.size() ? "WTF" : all_apps[g_idx].info.name, dump)){ + msgok(MSG_DIALOG::NORMAL, fmt::format("{0:} {1:} {2:}", getLangSTR(LANG_STR::DUMP_OF), title_id.get(), getLangSTR(LANG_STR::COMPLETE_WO_ERRORS))); } else{ @@ -448,7 +433,7 @@ else log_error("[DUMP] failed to get usbpath"); - msgok(WARNING, fmt::format("{0:} {1:} {2:}", getLangSTR(DUMP_OF), title_id, getLangSTR(DUMP_FAILED))); + msgok(MSG_DIALOG::WARNING, fmt::format("{0:} {1:} {2:}", getLangSTR(LANG_STR::DUMP_OF), title_id.get(), getLangSTR(LANG_STR::DUMP_FAILED))); } print_memory(); @@ -459,11 +444,9 @@ else } if (is_dumper_running){ - msgok(NORMAL, getLangSTR(DUMPER_CANCELLED)); + msgok(MSG_DIALOG::NORMAL, getLangSTR(LANG_STR::DUMPER_CANCELLED)); dump = SEL_RESET; is_dumper_running = false; } - - pthread_mutex_unlock(&disc_lock); } diff --git a/itemzflow/source/sql.cpp b/itemzflow/source/sql.cpp index 35cdccc..8d0f41b 100644 --- a/itemzflow/source/sql.cpp +++ b/itemzflow/source/sql.cpp @@ -11,6 +11,7 @@ #ifdef __ORBIS__ #include "installpkg.h" #endif +#include "feature_classes.hpp" char *err_msg = 0; sqlite3 *db = NULL; @@ -91,10 +92,13 @@ bool SQL_Exec(const char *smt, int (*cb)(void *, int, char **, char **)) int int_cb(void *in, int idc, char **argv, char **idc_2) { - - int *numb = (int *)in; - *numb = atoi(argv[0]); - + if (argv[0] != NULL) { // Check if argv[0] is not NULL + int *numb = (int *)in; + *numb = atoi(argv[0]); + } + else{ + log_error("SQL CB argv[0] is NULL"); + } return 0; } @@ -143,16 +147,13 @@ bool GameSave_Info(save_entry_t *item, int off) std::string tmp; int err = 0; sqlite3_stmt *stmt; - // lock mutex - pthread_mutex_lock(&disc_lock); - if(title_id.empty()){ + + if(title_id.get().empty()){ log_error("title_id is empty"); - pthread_mutex_unlock(&disc_lock); return false; } - item->title_id = title_id; - pthread_mutex_unlock(&disc_lock); + item->title_id = title_id.get(); if ((err = sceUserServiceGetForegroundUser(&item->userid)) != ITEMZCORE_SUCCESS) { @@ -262,6 +263,8 @@ bool AppDBVisiable(std::string tid, APP_DB_VIS opt, int write_value) u32 userId = -1; int ret, vis = -1; + log_info("[SQL] tid %s write_value %i", tid.c_str(), write_value); + if (!SQL_Load_DB(APP_DB)) return false; @@ -279,7 +282,10 @@ bool AppDBVisiable(std::string tid, APP_DB_VIS opt, int write_value) if (sqlite3_exec(db, tmp.c_str(), int_cb, &vis, &err_msg) != SQLITE_OK) { log_error("[APPDB] Failed to fetch data: %s", sqlite3_errmsg(db)); - sqlite3_free(err_msg); + + if(err_msg) + sqlite3_free(err_msg); + goto error; } @@ -291,7 +297,7 @@ bool AppDBVisiable(std::string tid, APP_DB_VIS opt, int write_value) return true; } - return vis; + return vis == 1; error: sqlite3_close(db); @@ -513,7 +519,7 @@ bool insert_app_info(std::string titleId, std::string key, std::string value = s } -bool Fix_Game_In_DB(std::vector &app_info, int index, bool is_ext_hdd) { +bool Fix_Game_In_DB(ThreadSafeVector &app_info, int index, bool is_ext_hdd) { std::string tmp; std::string tmp2; int ret = -1; @@ -549,7 +555,7 @@ bool Fix_Game_In_DB(std::vector &app_info, int index, bool is_ext_hdd) { int ver = (ps4_fw_version() >> 16); - tmp = fmt::format("INSERT OR IGNORE INTO tbl_appbrowse_0{0:d}(titleId, contentId, titleName, metaDataPath, lastAccessTime, contentStatus, onDisc, parentalLevel, visible, sortPriority, pathInfo, lastAccessIndex, dispLocation, canRemove, category, contentType, pathInfo2, presentBoxStatus, entitlement, thumbnailUrl, lastUpdateTime, playableDate, contentSize, installDate, platform, uiCategory, skuId, disableLiveDetail, linkType, linkUri, serviceIdAddCont1, serviceIdAddCont2, serviceIdAddCont3, serviceIdAddCont4, serviceIdAddCont5, serviceIdAddCont6, serviceIdAddCont7, folderType, folderInfo, parentFolderId, positionInFolder, activeDate, entitlementTitleName, hddLocation, externalHddAppStatus, entitlementIdKamaji, mTime {1:}) VALUES('{2:}', \"{3:}\", '{4:}', '{5:}', '2022-02-06 14:03:08.359', 0, 0, '{6:}', 1, 100, 0, 0, 5, 1, '{7:}', 0, 0, 0, 0, NULL, NULL, NULL, '{8:d}', '2018-07-27 15:06:46.802', 0, '{9:}', NULL, 0, 0, NULL, '{10:}', '{11:}', '{12:}','{13:}', '{14:}', '{15:}', '{16:}', 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, '2022-05-06 18:02:21.702'{17:})", userId, (ver > 0x555) ? ",freePsPlusContent, entitlementActiveFlag, sizeOtherHdd, entitlementHidden, preorderPlaceholderFlag, gatingEntitlementJson" : "", item.id, item.extra_sfo_data["CONTENT_ID"], item.name, item.is_ext_hdd ? "/user/appmeta/external/" + item.id : "/user/appmeta/" + item.id, item.extra_sfo_data["PARENTAL_LEVEL"], item.extra_sfo_data["CATEGORY"], file_size(item.package.c_str()), (item.extra_sfo_data["CATEGORY"] == "gde") ? "app" : "game", item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_1"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_2"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_3"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_4"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_5"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_6"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_7"], (ver > 0x555) ? ", 0,0, 0, 0, 0, NULL" : ""); + tmp = fmt::format("INSERT OR IGNORE INTO tbl_appbrowse_0{0:d}(titleId, contentId, titleName, metaDataPath, lastAccessTime, contentStatus, onDisc, parentalLevel, visible, sortPriority, pathInfo, lastAccessIndex, dispLocation, canRemove, category, contentType, pathInfo2, presentBoxStatus, entitlement, thumbnailUrl, lastUpdateTime, playableDate, contentSize, installDate, platform, uiCategory, skuId, disableLiveDetail, linkType, linkUri, serviceIdAddCont1, serviceIdAddCont2, serviceIdAddCont3, serviceIdAddCont4, serviceIdAddCont5, serviceIdAddCont6, serviceIdAddCont7, folderType, folderInfo, parentFolderId, positionInFolder, activeDate, entitlementTitleName, hddLocation, externalHddAppStatus, entitlementIdKamaji, mTime {1:}) VALUES('{2:}', \"{3:}\", '{4:}', '{5:}', '2022-02-06 14:03:08.359', 0, 0, '{6:}', 1, 100, 0, 0, 5, 1, '{7:}', 0, 0, 0, 0, NULL, NULL, NULL, '{8:d}', '2018-07-27 15:06:46.802', 0, '{9:}', NULL, 0, 0, NULL, '{10:}', '{11:}', '{12:}','{13:}', '{14:}', '{15:}', '{16:}', 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, '2022-05-06 18:02:21.702'{17:})", userId, (ver > 0x555) ? ",freePsPlusContent, entitlementActiveFlag, sizeOtherHdd, entitlementHidden, preorderPlaceholderFlag, gatingEntitlementJson" : "", item.info.id, item.extra_data.extra_sfo_data["CONTENT_ID"], item.info.name, item.flags.is_ext_hdd ? "/user/appmeta/external/" + item.info.id : "/user/appmeta/" + item.info.id, item.extra_data.extra_sfo_data["PARENTAL_LEVEL"], item.extra_data.extra_sfo_data["CATEGORY"], file_size(item.info.package.c_str()), (item.extra_data.extra_sfo_data["CATEGORY"] == "gde") ? "app" : "game", item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_1"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_2"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_3"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_4"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_5"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_6"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_7"], (ver > 0x555) ? ", 0,0, 0, 0, 0, NULL" : ""); if (sqlite3_exec(db, tmp.c_str(), NULL, NULL, & err_msg) != SQLITE_OK || err_msg) { log_error("[AppDB][PASS1] Failed: %s", sqlite3_errmsg(db)); sqlite3_free(err_msg); @@ -559,7 +565,7 @@ bool Fix_Game_In_DB(std::vector &app_info, int index, bool is_ext_hdd) { //return ::ispunct(ch); //}, ' '); - tmp = fmt::format("INSERT OR IGNORE INTO tbl_appbrowse_0{0:d}(titleId, contentId, titleName, metaDataPath, lastAccessTime, contentStatus, onDisc, parentalLevel, visible, sortPriority, pathInfo, lastAccessIndex, dispLocation, canRemove, category, contentType, pathInfo2, presentBoxStatus, entitlement, thumbnailUrl, lastUpdateTime, playableDate, contentSize, installDate, platform, uiCategory, skuId, disableLiveDetail, linkType, linkUri, serviceIdAddCont1, serviceIdAddCont2, serviceIdAddCont3, serviceIdAddCont4, serviceIdAddCont5, serviceIdAddCont6, serviceIdAddCont7, folderType, folderInfo, parentFolderId, positionInFolder, activeDate, entitlementTitleName, hddLocation, externalHddAppStatus, entitlementIdKamaji, mTime {1:}) VALUES('{2:}', \"{3:}\", '{4:}', '{5:}', '2022-02-06 14:03:08.359', 0, 0, '{6:}', 1, 100, 0, 0, 5, 1, '{7:}', 0, 0, 0, 0, NULL, NULL, NULL, '{8:d}', '2018-07-27 15:06:46.802', 0, '{9:}', NULL, 0, 0, NULL, '{10:}', '{11:}', '{12:}','{13:}', '{14:}', '{15:}', '{16:}', 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, '2022-05-06 18:02:21.702'{17:})", userId, (ver > 0x555) ? ",freePsPlusContent, entitlementActiveFlag, sizeOtherHdd, entitlementHidden, preorderPlaceholderFlag, gatingEntitlementJson" : "", item.id, item.extra_sfo_data["CONTENT_ID"], sanitizeString(item.name), item.is_ext_hdd ? "/user/appmeta/external/" + item.id : "/user/appmeta/" + item.id, item.extra_sfo_data["PARENTAL_LEVEL"], item.extra_sfo_data["CATEGORY"], file_size(item.package.c_str()), (item.extra_sfo_data["CATEGORY"] == "gde") ? "app" : "game", item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_1"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_2"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_3"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_4"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_5"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_6"], item.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_7"], (ver > 0x555) ? ", 0,0, 0, 0, 0, NULL" : ""); + tmp = fmt::format("INSERT OR IGNORE INTO tbl_appbrowse_0{0:d}(titleId, contentId, titleName, metaDataPath, lastAccessTime, contentStatus, onDisc, parentalLevel, visible, sortPriority, pathInfo, lastAccessIndex, dispLocation, canRemove, category, contentType, pathInfo2, presentBoxStatus, entitlement, thumbnailUrl, lastUpdateTime, playableDate, contentSize, installDate, platform, uiCategory, skuId, disableLiveDetail, linkType, linkUri, serviceIdAddCont1, serviceIdAddCont2, serviceIdAddCont3, serviceIdAddCont4, serviceIdAddCont5, serviceIdAddCont6, serviceIdAddCont7, folderType, folderInfo, parentFolderId, positionInFolder, activeDate, entitlementTitleName, hddLocation, externalHddAppStatus, entitlementIdKamaji, mTime {1:}) VALUES('{2:}', \"{3:}\", '{4:}', '{5:}', '2022-02-06 14:03:08.359', 0, 0, '{6:}', 1, 100, 0, 0, 5, 1, '{7:}', 0, 0, 0, 0, NULL, NULL, NULL, '{8:d}', '2018-07-27 15:06:46.802', 0, '{9:}', NULL, 0, 0, NULL, '{10:}', '{11:}', '{12:}','{13:}', '{14:}', '{15:}', '{16:}', 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, '2022-05-06 18:02:21.702'{17:})", userId, (ver > 0x555) ? ",freePsPlusContent, entitlementActiveFlag, sizeOtherHdd, entitlementHidden, preorderPlaceholderFlag, gatingEntitlementJson" : "", item.info.id, item.extra_data.extra_sfo_data["CONTENT_ID"], sanitizeString(item.info.name), item.flags.is_ext_hdd ? "/user/appmeta/external/" + item.info.id : "/user/appmeta/" + item.info.id, item.extra_data.extra_sfo_data["PARENTAL_LEVEL"], item.extra_data.extra_sfo_data["CATEGORY"], file_size(item.info.package.c_str()), (item.extra_data.extra_sfo_data["CATEGORY"] == "gde") ? "app" : "game", item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_1"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_2"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_3"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_4"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_5"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_6"], item.extra_data.extra_sfo_data["SERVICE_ID_ADDCONT_ADD_7"], (ver > 0x555) ? ", 0,0, 0, 0, 0, NULL" : ""); if (sqlite3_exec(db, tmp.c_str(), NULL, NULL, & err_msg) != SQLITE_OK || err_msg) { log_error("[AppDB][PASS2][FATAL] Failed: %s", sqlite3_errmsg(db)); sqlite3_free(err_msg); @@ -583,80 +589,80 @@ bool Fix_Game_In_DB(std::vector &app_info, int index, bool is_ext_hdd) { } //print out extra sfo data - if (!insert_app_info(item.id, "TITLE", item.name)) { - insert_app_info(item.id, "TITLE", sanitizeString(item.name)); + if (!insert_app_info(item.info.id, "TITLE", item.info.name)) { + insert_app_info(item.info.id, "TITLE", sanitizeString(item.info.name)); } - insert_app_info(item.id, "TITLE_ID", item.id); - insert_app_info(item.id, "_metadata_path", item.is_ext_hdd ? "/user/appmeta/external/" + item.id : "/user/appmeta/" + item.id); - insert_app_info(item.id, "_org_path", item.is_ext_hdd ? "/mnt/ext0/user/app/" + item.id : "/user/app/" + item.id); - insert_app_info(item.id, "APP_VER", item.version); - insert_app_info(item.id, "ATTRIBUTE", item.extra_sfo_data["ATTRIBUTE"], false); - insert_app_info(item.id, "CATEGORY", item.extra_sfo_data["CATEGORY"]); - insert_app_info(item.id, "CONTENT_ID", item.extra_sfo_data["CONTENT_ID"]); - insert_app_info(item.id, "VERSION", item.extra_sfo_data["VERSION"]); - insert_app_info(item.id, "#_access_index", "1", false); - insert_app_info(item.id, "#_last_access_time", "2022-01-21 15:04:39.822"); - insert_app_info(item.id, "#_contents_status", "0", false); - insert_app_info(item.id, "#_mtime", "2022-01-21 15:04:39.822"); - insert_app_info(item.id, "_contents_location", "0", false); - insert_app_info(item.id, "#_update_index", "0", false); - insert_app_info(item.id, "#exit_type", "0", false); - insert_app_info(item.id, "DISP_LOCATION_1", "0", false); - insert_app_info(item.id, "DISP_LOCATION_2", "0", false); - insert_app_info(item.id, "DOWNLOAD_DATA_SIZE", item.extra_sfo_data["DOWNLOAD_DATA_SIZE"], false); - insert_app_info(item.id, "FORMAT", "obs"); - insert_app_info(item.id, "PARENTAL_LEVEL", item.extra_sfo_data["PARENTAL_LEVEL"], false); - insert_app_info(item.id, "SYSTEM_VER", item.extra_sfo_data["SYSTEM_VER"], false); + insert_app_info(item.info.id, "TITLE_ID", item.info.id); + insert_app_info(item.info.id, "_metadata_path", item.flags.is_ext_hdd ? "/user/appmeta/external/" + item.info.id : "/user/appmeta/" + item.info.id); + insert_app_info(item.info.id, "_org_path", item.flags.is_ext_hdd ? "/mnt/ext0/user/app/" + item.info.id : "/user/app/" + item.info.id); + insert_app_info(item.info.id, "APP_VER", item.info.version); + insert_app_info(item.info.id, "ATTRIBUTE", item.extra_data.extra_sfo_data["ATTRIBUTE"], false); + insert_app_info(item.info.id, "CATEGORY", item.extra_data.extra_sfo_data["CATEGORY"]); + insert_app_info(item.info.id, "CONTENT_ID", item.extra_data.extra_sfo_data["CONTENT_ID"]); + insert_app_info(item.info.id, "VERSION", item.extra_data.extra_sfo_data["VERSION"]); + insert_app_info(item.info.id, "#_access_index", "1", false); + insert_app_info(item.info.id, "#_last_access_time", "2022-01-21 15:04:39.822"); + insert_app_info(item.info.id, "#_contents_status", "0", false); + insert_app_info(item.info.id, "#_mtime", "2022-01-21 15:04:39.822"); + insert_app_info(item.info.id, "_contents_location", "0", false); + insert_app_info(item.info.id, "#_update_index", "0", false); + insert_app_info(item.info.id, "#exit_type", "0", false); + insert_app_info(item.info.id, "DISP_LOCATION_1", "0", false); + insert_app_info(item.info.id, "DISP_LOCATION_2", "0", false); + insert_app_info(item.info.id, "DOWNLOAD_DATA_SIZE", item.extra_data.extra_sfo_data["DOWNLOAD_DATA_SIZE"], false); + insert_app_info(item.info.id, "FORMAT", "obs"); + insert_app_info(item.info.id, "PARENTAL_LEVEL", item.extra_data.extra_sfo_data["PARENTAL_LEVEL"], false); + insert_app_info(item.info.id, "SYSTEM_VER", item.extra_data.extra_sfo_data["SYSTEM_VER"], false); - if (!item.extra_sfo_data["INSTALL_DIR_SAVEDATA"].empty()) - insert_app_info(item.id, "INSTALL_DIR_SAVEDATA", item.extra_sfo_data["INSTALL_DIR_SAVEDATA"]); + if (!item.extra_data.extra_sfo_data["INSTALL_DIR_SAVEDATA"].empty()) + insert_app_info(item.info.id, "INSTALL_DIR_SAVEDATA", item.extra_data.extra_sfo_data["INSTALL_DIR_SAVEDATA"]); for (int i = 1; i <= 4; i++) { - insert_app_info(item.id, fmt::format("USER_DEFINED_PARAM_{0:d}", i), item.extra_sfo_data[fmt::format("USER_DEFINED_PARAM_{0:d}", i)], false); + insert_app_info(item.info.id, fmt::format("USER_DEFINED_PARAM_{0:d}", i), item.extra_data.extra_sfo_data[fmt::format("USER_DEFINED_PARAM_{0:d}", i)], false); } for (int i = 1; i <= 7; i++) { std::string SERV_ID = fmt::format("SERVICE_ID_ADDCONT_APPINFO_ADD_{0:d}", i); std::string SERV_ID2 = fmt::format("SERVICE_ID_ADDCONT_ADD_{0:d}", i); - if (!item.extra_sfo_data[SERV_ID].empty()) - insert_app_info(item.id, SERV_ID2, item.extra_sfo_data[SERV_ID]); + if (!item.extra_data.extra_sfo_data[SERV_ID].empty()) + insert_app_info(item.info.id, SERV_ID2, item.extra_data.extra_sfo_data[SERV_ID]); else - insert_app_info(item.id, SERV_ID2, "0", false); + insert_app_info(item.info.id, SERV_ID2, "0", false); } for (int i = 0; i < 30; i++) { std::string title = fmt::format("TITLE_{0:}{1:d}", (i < 10) ? "0" : "", i); - if (!item.extra_sfo_data[title].empty()) { - if (!insert_app_info(item.id, title, item.extra_sfo_data[title])) { + if (!item.extra_data.extra_sfo_data[title].empty()) { + if (!insert_app_info(item.info.id, title, item.extra_data.extra_sfo_data[title])) { - //std::replace_if(item.extra_sfo_data[title].begin(), item.extra_sfo_data[title].end(), [](auto ch) { + //std::replace_if(item.extra_data.extra_sfo_data[title].begin(), item.extra_data.extra_sfo_data[title].end(), [](auto ch) { // return ::ispunct(ch); //}, ' '); - insert_app_info(item.id, title, sanitizeString(item.extra_sfo_data[title])); + insert_app_info(item.info.id, title, sanitizeString(item.extra_data.extra_sfo_data[title])); } } } - insert_app_info(item.id, "_contents_ext_type", "0", false); - insert_app_info(item.id, "_current_slot", "0", false); - insert_app_info(item.id, "_disable_live_detail", "0", false); - insert_app_info(item.id, "_external_hdd_app_status", "0", false); - insert_app_info(item.id, "_hdd_location", "0", false); - insert_app_info(item.id, "_path_info", "0", false); - insert_app_info(item.id, "_path_info_2", "0", false); - insert_app_info(item.id, "_size_other_hdd", "0", false); - insert_app_info(item.id, "_sort_priority", "100", false); - insert_app_info(item.id, "_uninstallable", "1", false); - insert_app_info(item.id, "_view_category", "0", false); - insert_app_info(item.id, "_working_status", "0", false); - insert_app_info(item.id, "ATTRIBUTE2", item.extra_sfo_data["ATTRIBUTE2"], false); - insert_app_info(item.id, "ATTRIBUTE_INTERNAL", item.extra_sfo_data["ATTRIBUTE_INTERNAL"], false); - insert_app_info(item.id, "PT_PARAM", item.extra_sfo_data["PT_PARAM"], false); - insert_app_info(item.id, "APP_TYPE", item.extra_sfo_data["APP_TYPE"], false); - insert_app_info(item.id, "REMOTE_PLAY_KEY_ASSIGN", item.extra_sfo_data["REMOTE_PLAY_KEY_ASSIGN"], false); - insert_app_info(item.id, "#_promote_time", "2022-01-21 15:04:39.822"); - insert_app_info(item.id, "#_booted", "1", false); - insert_app_info(item.id, "#_size", std::to_string(file_size(item.package.c_str())), false); + insert_app_info(item.info.id, "_contents_ext_type", "0", false); + insert_app_info(item.info.id, "_current_slot", "0", false); + insert_app_info(item.info.id, "_disable_live_detail", "0", false); + insert_app_info(item.info.id, "_external_hdd_app_status", "0", false); + insert_app_info(item.info.id, "_hdd_location", "0", false); + insert_app_info(item.info.id, "_path_info", "0", false); + insert_app_info(item.info.id, "_path_info_2", "0", false); + insert_app_info(item.info.id, "_size_other_hdd", "0", false); + insert_app_info(item.info.id, "_sort_priority", "100", false); + insert_app_info(item.info.id, "_uninstallable", "1", false); + insert_app_info(item.info.id, "_view_category", "0", false); + insert_app_info(item.info.id, "_working_status", "0", false); + insert_app_info(item.info.id, "ATTRIBUTE2", item.extra_data.extra_sfo_data["ATTRIBUTE2"], false); + insert_app_info(item.info.id, "ATTRIBUTE_INTERNAL", item.extra_data.extra_sfo_data["ATTRIBUTE_INTERNAL"], false); + insert_app_info(item.info.id, "PT_PARAM", item.extra_data.extra_sfo_data["PT_PARAM"], false); + insert_app_info(item.info.id, "APP_TYPE", item.extra_data.extra_sfo_data["APP_TYPE"], false); + insert_app_info(item.info.id, "REMOTE_PLAY_KEY_ASSIGN", item.extra_data.extra_sfo_data["REMOTE_PLAY_KEY_ASSIGN"], false); + insert_app_info(item.info.id, "#_promote_time", "2022-01-21 15:04:39.822"); + insert_app_info(item.info.id, "#_booted", "1", false); + insert_app_info(item.info.id, "#_size", std::to_string(file_size(item.info.package.c_str())), false); sqlite3_close(db), db = NULL; return true; -} \ No newline at end of file +} diff --git a/itemzflow/source/utils.cpp b/itemzflow/source/utils.cpp index 99780bc..00bb58d 100644 --- a/itemzflow/source/utils.cpp +++ b/itemzflow/source/utils.cpp @@ -19,6 +19,7 @@ extern "C"{ #include "dialog.h" #include "zip/zip.h" } +#include #include "installpkg.h" #include #include @@ -29,6 +30,10 @@ extern "C"{ #include #include #include + + + + /*================== MISC GOLBAL VAR ============*/ static uint32_t sdkVersion = -1; static std::string checkedsize; @@ -43,14 +48,16 @@ size_t _orbisFile_lastopenFile_size; ItemzSettings set, * get; -extern char ipc_msg[]; +extern std::string ipc_msg; extern std::atomic_int inserted_disc; extern std::atomic_bool is_disc_inserted; extern struct retry_t* cf_tex; extern int total_pages; -extern std::vector all_apps; // Installed_Apps +extern ThreadSafeVector all_apps; // Installed_Apps bool sceAppInst_done = false; +using namespace multi_select_options; + bool (*sceStoreApiLaunchStore)(const char* query) = NULL; update_ret (*sceStoreApiCheckUpdate)(const char* tid) = NULL; /* =========================================== */ @@ -59,7 +66,7 @@ update_ret (*sceStoreApiCheckUpdate)(const char* tid) = NULL; #include #include #include -std::vector mp3_playlist; +ThreadSafeVector mp3_playlist; current_song_t current_song; static uint64_t off2 = 0; @@ -85,7 +92,7 @@ snd[1152 * 2]; // small fixed buffer to store a single audio frame of decod /*===============================================*/ /*============= USB VARS ========================*/ -extern pthread_mutex_t usb_lock; +std::mutex usb_lock; std::atomic_int usb_number = ATOMIC_VAR_INIT(false); /*=============================================*/ @@ -109,6 +116,69 @@ const char *if_d_zip[] = { }; bool reboot_app = false; #include "shaders.h" + + +double CalcFreeGigs(const char* path) { + struct statfs st; + if (statfs(path, &st) != 0) { + return 0; + } + return (double)st.f_bfree * st.f_bsize / (1024 * 1024 * 1024); +} + +#if __cplusplus +extern "C" { +#endif +int64_t sys_dynlib_load_prx(char* prxPath, int* moduleID) +{ + return (int64_t)syscall4(594, prxPath, 0, moduleID, 0); +} + +int64_t sys_dynlib_unload_prx(int64_t prxID) +{ + return (int64_t)syscall1(595, (void*)prxID); +} + + +int64_t sys_dynlib_dlsym(int64_t moduleHandle, const char* functionName, void* destFuncOffset) +{ + return (int64_t)syscall3(591, (void*)moduleHandle, (void*)functionName, destFuncOffset); +} + +unsigned char* orbisFileGetFileContent(const char* filename) +{ + _orbisFile_lastopenFile_size = -1; + + FILE* file = fopen(filename, "rb"); + if (!file) + { + log_error("Unable to open file \"%s\".", filename); return NULL; + } + + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fseek(file, 0, SEEK_SET); + + unsigned char* buffer = (unsigned char*)malloc((size + 1) * sizeof(char)); + fread(buffer, sizeof(char), size, file); + buffer[size] = 0; + _orbisFile_lastopenFile_size = size; + fclose(file); + + return buffer; +} + +int sceSystemServiceKillApp(uint32_t appid, int opt,int method, int reason); +int sceShellUIUtilInitialize(); +int sceShellUIUtilLaunchByUri(const char* uri, SceShellUIUtilLaunchByUriParam *Param); +int sceAppInstUtilAppUnInstallAddcont(const char* title_id, const char* content_id); +int sceAppInstUtilAppUnInstallPat(const char* tid); +int malloc_stats_fast(LibcMallocManagedSize *ManagedSize); + +#ifdef __cplusplus +} +#endif + /*=====================================================*/ /* * Creates all the directories in the provided path. (can include a filename) @@ -190,7 +260,7 @@ static int theme_info(void* user, const char* section, const char* name, void __stack_chk_fail(void) { log_info("Stack smashing detected."); - msgok(FATAL, "Stack Smashing has been Detected"); + msgok(MSG_DIALOG::FATAL, "Stack Smashing has been Detected"); } bool IS_ERROR(uint32_t ret) @@ -227,58 +297,6 @@ size_t CalcAppsize(const char* filename) { return st.st_size; } -double CalcFreeGigs(const char* path) { - struct statfs st; - if (statfs(path, &st) != 0) { - return 0; - } - return (double)st.f_bfree * st.f_bsize / (1024 * 1024 * 1024); -} - -#if __cplusplus -extern "C" { -#endif -int64_t sys_dynlib_load_prx(char* prxPath, int* moduleID) -{ - return (int64_t)syscall4(594, prxPath, 0, moduleID, 0); -} - -int64_t sys_dynlib_unload_prx(int64_t prxID) -{ - return (int64_t)syscall1(595, (void*)prxID); -} - - -int64_t sys_dynlib_dlsym(int64_t moduleHandle, const char* functionName, void* destFuncOffset) -{ - return (int64_t)syscall3(591, (void*)moduleHandle, (void*)functionName, destFuncOffset); -} - -unsigned char* orbisFileGetFileContent(const char* filename) -{ - _orbisFile_lastopenFile_size = -1; - - FILE* file = fopen(filename, "rb"); - if (!file) - { - log_error("Unable to open file \"%s\".", filename); return NULL; - } - - fseek(file, 0, SEEK_END); - size_t size = ftell(file); - fseek(file, 0, SEEK_SET); - - unsigned char* buffer = (unsigned char*)malloc((size + 1) * sizeof(char)); - fread(buffer, sizeof(char), size, file); - buffer[size] = 0; - _orbisFile_lastopenFile_size = size; - fclose(file); - - return buffer; -} - -int sceSystemServiceKillApp(uint32_t appid, int opt,int method, int reason); - int ItemzLocalKillApp(uint32_t appid) { log_info("ItemzLocalKillApp(%x, -1, 0, 0)", appid); @@ -288,8 +306,6 @@ int ItemzLocalKillApp(uint32_t appid) { return ITEMZCORE_EINVAL; } -int sceShellUIUtilInitialize(); -int sceShellUIUtilLaunchByUri(const char* uri, SceShellUIUtilLaunchByUriParam *Param); int ItemzLaunchByUri(const char* uri) { int libcmi = -1; @@ -321,10 +337,10 @@ bool copyFile(std::string source, std::string dest, bool show_progress) if (out > 0) { if(show_progress) - progstart((char*)"Starting..."); + progstart("Starting..."); size_t bytes ,total = 0; - size_t len = CalcAppsize((char*)source.c_str()); + size_t len = CalcAppsize(source.c_str()); std::string size = calculateSize(len); std::vector buffer(MB(10)); @@ -337,7 +353,7 @@ bool copyFile(std::string source, std::string dest, bool show_progress) uint32_t g_progress = (uint32_t)(((float)total / len) * 100.f); int status = sceMsgDialogUpdateStatus(); if (ORBIS_COMMON_DIALOG_STATUS_RUNNING == status) { - buf = fmt::format("{0:}...\n Size: {1:}", getLangSTR(COPYING_FILE), size); + buf = fmt::format("{0:}...\n Size: {1:}", getLangSTR(LANG_STR::COPYING_FILE), size); sceMsgDialogProgressBarSetValue(0, g_progress); sceMsgDialogProgressBarSetMsg(0, buf.c_str()); } @@ -382,11 +398,14 @@ void ProgSetMessagewText(int prog, const char* fmt, ...) sceMsgDialogProgressBarSetMsg(0, buff); } /// timing -unsigned int get_time_ms(void) +float get_time_ms(std::chrono::time_point &last_time) { - struct timeval tv; - gettimeofday(&tv, NULL); - return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + auto now = std::chrono::system_clock::now(); + auto duration = now - last_time; + auto millis = std::chrono::duration_cast(duration).count(); + last_time = now; + + return (float)(millis / 1000.f); } bool touch_file(const char* destfile) @@ -409,7 +428,9 @@ unsigned char _bittest64(uint64_t *Base, uint64_t Offset) :"Ir" (Offset)); return (old != 0); } - +// both real and _func functions are the same +unsigned int sceShellCoreUtilIsUsbMassStorageMounted(unsigned int usb_index); +//interchangable unsigned int sceShellCoreUtilIsUsbMassStorageMounted_func(unsigned int usb_index) { uint64_t res; @@ -425,8 +446,8 @@ unsigned int sceShellCoreUtilIsUsbMassStorageMounted_func(unsigned int usb_index unsigned int usbpath() { + // std::lock_guard lock(disc_lock); unsigned int usb_index = -1; - pthread_mutex_lock(&usb_lock); for (int i = 0; i < 8; i++){ if (sceShellCoreUtilIsUsbMassStorageMounted_func((unsigned int)i)){ usb_index = i; @@ -434,8 +455,6 @@ unsigned int usbpath() break; } } - pthread_mutex_unlock(&usb_lock); - return usb_index; } @@ -542,8 +561,6 @@ bool is_apputil_init() return true; } -int sceAppInstUtilAppUnInstallPat(const char* tid); - bool app_inst_util_uninstall_patch(const char* title_id, int* error) { int ret; @@ -629,8 +646,6 @@ err: return false; } -int sceAppInstUtilAppUnInstallAddcont(const char* title_id, const char* content_id); - bool app_inst_util_uninstall_ac(const char* content_id, const char* title_id, int* error) { *error = 0; @@ -773,7 +788,7 @@ bool LoadOptions(ItemzSettings *set) set->setting_bools[Daemon_on_start] = true; set->Dump_opt = SEL_DUMP_ALL; set->sort_cat = NO_CATEGORY_FILTER; - set->sort_by = NA_SORT; + set->sort_by = multi_select_options::NA_SORT; set->setting_bools[using_sb] = true; set->setting_bools[INTERNAL_UPDATE] = false; set->setting_bools[Show_install_prog] = true; @@ -927,12 +942,12 @@ no_theme: log_debug(" This PKG has no lang files... trying embedded"); if(!load_embdded_eng()) - msgok(FATAL, "Failed to load Backup Lang...\nThe App is unable to continue"); + msgok(MSG_DIALOG::FATAL, "Failed to load Backup Lang...\nThe App is unable to continue"); else log_debug(" Loaded embdded ini lang file"); } else - log_debug(" Loaded the backup, %s failed to load", Language_GetName(lang)); + log_debug(" Loaded the backup, %s failed to load", Language_GetName(lang).c_str()); } if (!Fnt_setting_enabled) { @@ -981,7 +996,7 @@ no_theme: log_info( "set->using_theme : %i", set->setting_bools[using_theme]); log_info( "set->Dump_opt : %i", set->Dump_opt); log_info( "set->Install_prog.: %s", set->setting_bools[Show_install_prog] ? "ON" : "OFF"); - log_info( "set->Lang : %s : %i", Language_GetName(lang), set->lang); + log_info( "set->Lang : %s : %i", Language_GetName(lang).c_str(), set->lang); return no_error; } @@ -1016,7 +1031,7 @@ bool SaveOptions(ItemzSettings *set) log_info("set->using_theme : %i", set->setting_bools[using_theme]); log_info("set->RESTIRCTED : %i", set->setting_bools[INTERNAL_UPDATE]); log_info("set->Install_prog.: %s", set->setting_bools[Show_install_prog] ? "ON" : "OFF"); - log_info("set->Lang : %s : %i", Language_GetName(set->lang), set->lang); + log_info("set->Lang : %s : %i", Language_GetName(set->lang).c_str(), set->lang); log_info("=========================================================="); /* Load values */ @@ -1092,7 +1107,7 @@ bool Keyboard(const char* Title, const char* initialTextBuffer, char* out_buffer if (result.endstatus == ORBIS_IME_DIALOG_END_STATUS_USER_CANCELED){ log_info("User Cancelled this Keyboard Session"); - ani_notify(NOTIFI_WARNING, getLangSTR(OP_CANNED), getLangSTR(OP_CANNED_1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OP_CANNED), getLangSTR(LANG_STR::OP_CANNED_1)); } else if (result.endstatus == ORBIS_IME_DIALOG_END_STATUS_OK) { @@ -1151,49 +1166,6 @@ bool rmtree(const char path[]) { return true; } -bool copy_dir(const char* sourcedir, const char* destdir) { - - log_info("s: %s d: %s", sourcedir, destdir); - DIR* dir = opendir(sourcedir); - - struct dirent* dp; - struct stat info; - char src_path[1024]; - char dst_path[1024]; - - if (!dir) { - return false; - } - mkdir(destdir, 0777); - while ((dp = readdir(dir)) != NULL) { - if(strstr(dp->d_name, "save.ini") != NULL) - log_info("Save.ini file skipped..."); - else if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") - || strstr(dp->d_name, "nobackup") != NULL) { - continue; - } - else { - sprintf(src_path, "%s/%s", sourcedir, dp->d_name); - sprintf(dst_path, "%s/%s", destdir, dp->d_name); - - if (!stat(src_path, &info)) { - if (S_ISDIR(info.st_mode)) { - copy_dir(src_path, dst_path); - } - else if (S_ISREG(info.st_mode)) { - if (!copyFile(src_path, dst_path, false)) - return false; - } - } - } - } - closedir(dir); - - return true; -} - -int malloc_stats_fast(LibcMallocManagedSize *ManagedSize); - void print_memory() { size_t sz = 0; @@ -1208,7 +1180,7 @@ void print_memory() log_info("[VRAM] VRAM_Available: %s, AmountUsed: %.2f%%",calculateSize(sz).c_str(), dfp_fmem); } -void Kill_BigApp(GameStatus &status){ +void Kill_BigApp(std::atomic &status){ int ret = -1; int BigAppid = sceSystemServiceGetAppIdOfBigApp(); @@ -1220,19 +1192,19 @@ void Kill_BigApp(GameStatus &status){ log_info("[CLOSE] BigApp ID: 0x%X", BigAppid); if ((ret = ItemzLocalKillApp(BigAppid)) == ITEMZCORE_SUCCESS) { - ani_notify(NOTIFI_INFO, getLangSTR(APP_CLOSED), ""); + ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::APP_CLOSED), ""); log_info("[CLOSE] App Closed"); } else { - ani_notify(NOTIFI_WARNING, getLangSTR(FAILED_TO_CLOSE), fmt::format( "{0:}: {1:#x}", getLangSTR(ERROR_CODE), ret)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_TO_CLOSE), fmt::format( "{0:}: {1:#x}", getLangSTR(LANG_STR::ERROR_CODE), ret)); log_error("[CLOSE] Failed to close APP: 0x%X", ret); } status = NO_APP_OPENED; } -int progstart(char* format, ...) +int progstart(const char* format, ...) { @@ -1489,7 +1461,7 @@ void* CheckForUpdateThread(void*) update_ret ret = check_update_from_url(ITEMZFLOW_TID); if (ret == IF_UPDATE_FOUND){ log_info(" Update is available, the server reported a different Hash"); - ani_notify(NOTIFI_INFO, getLangSTR(OPT_UPDATE), ""); + ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::OPT_UPDATE), ""); sleep(5 * 60); } else if(ret == IF_NO_UPDATE) { @@ -1510,9 +1482,7 @@ void launch_update_thread() pthread_create(&id, NULL, CheckForUpdateThread, NULL);// pthread_create(&id, NULL, timer_thr, NULL); } -#if __cplusplus -} -#endif + bool Launch_Store_URI() { sceStoreApiLaunchStore = (bool (*)(const char*))prx_func_loader(asset_path("../Media/store_api.prx"), "sceStoreApiLaunchStore"); @@ -1529,6 +1499,18 @@ bool Launch_Store_URI() return false; } +void ProgressUpdate(uint32_t prog, std::string fmt) +{ + + int status = sceMsgDialogUpdateStatus(); + if (ORBIS_COMMON_DIALOG_STATUS_RUNNING == status) { + sceMsgDialogProgressBarSetValue(0, prog); + sceMsgDialogProgressBarSetMsg(0, fmt.c_str()); + } +} + + + mp3_status_t MP3_Player(std::string path) { TagLib::FileRef songf; @@ -1573,7 +1555,7 @@ mp3_status_t MP3_Player(std::string path) { log_info("Reached end of playlist, resetting index"); current_song.current_file = 1; } - path = path + "/" + mp3_playlist[current_song.current_file].id; + path = path + "/" + mp3_playlist[current_song.current_file].info.id; if (path.substr(path.size() - 3, 3) != "mp3") { log_error("Not a valid MP3 file: %s", path.c_str()); goto err; @@ -1598,7 +1580,7 @@ mp3_status_t MP3_Player(std::string path) { } else { - current_song.song_title = songf.tag()->title().isEmpty() ? mp3_playlist[current_song.current_file].id : songf.tag()->title().toCString(true); + current_song.song_title = songf.tag()->title().isEmpty() ? mp3_playlist[current_song.current_file].info.id : songf.tag()->title().toCString(true); current_song.song_artist = songf.tag()->artist().isEmpty() ? "Unknown Artist" : songf.tag()->artist().toCString(true); } @@ -1641,7 +1623,7 @@ mp3_status_t MP3_Player(std::string path) { err: if (2 >= retry_mp3_count) { - ani_notify(NOTIFI_WARNING, getLangSTR(FAILED_MP3), fmt::format("({0:d}/2)", retry_mp3_count+1)); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_MP3), fmt::format("({0:d}/2)", retry_mp3_count+1)); retry_mp3_count++; } dataSize = 0; @@ -1673,97 +1655,96 @@ void Stop_Music(){ } bool do_update(std::string url) { + IPC_Client& ipc = IPC_Client::getInstance(); - log_debug("[UPDATE] Starting Update..."); - log_debug("[UPDATE] Updating..."); + log_debug("[UPDATE] Starting Update..."); + log_debug("[UPDATE] Updating..."); - if (get->setting_bools[INTERNAL_UPDATE] && !is_connected_app) { - log_debug("[UPDATE] Daemon Not connected to app, exiting update mode ... "); - return false; - } - - if (get->setting_bools[INTERNAL_UPDATE]) { - std::string tmp; - long ret = -1; - char ipc_msg[100]; - - tmp = fmt::format("{}/update/if_update.pkg", url.c_str()); - if ((ret = dl_from_url(tmp.c_str(), APP_PATH("../if_update.pkg"))) < 0 && if_exists(APP_PATH("../if_update.pkg"))) { - log_error("[UPDATE] Failed to download if_update.pkg ret: %i", ret); - return false; + if (get->setting_bools[INTERNAL_UPDATE] && !is_connected_app) { + log_debug("[UPDATE] Daemon Not connected to app, exiting update mode ... "); + return false; } - sprintf( & ipc_msg[1], "%i", getpid()); - log_info("Sending PID: %s", & ipc_msg[1]); - int error = IPCSendCommand(INSTALL_IF_UPDATE, (uint8_t * ) & ipc_msg[0]); - if (error != NO_ERROR) { - sceMsgDialogTerminate(); - log_error("[UPDATE] IPC INSTALL UPDATE ERROR: %i", error); - return false; - } + if (get->setting_bools[INTERNAL_UPDATE]) { + std::string tmp; + long ret = -1; + tmp = fmt::format("{}/update/if_update.pkg", url.c_str()); + if ((ret = dl_from_url(tmp.c_str(), APP_PATH("../if_update.pkg"))) < 0 && if_exists(APP_PATH("../if_update.pkg"))) { + log_error("[UPDATE] Failed to download if_update.pkg ret: %i", ret); + return false; + } - log_debug("[UPDATE] Copied, Removing Temp Files"); - log_debug("[UPDATE] Complete..."); + ipc_msg = std::to_string(getpid()); + + IPC_Ret error = ipc.IPCSendCommand(IPC_Commands::INSTALL_IF_UPDATE, ipc_msg); + if (error != IPC_Ret::NO_ERROR) { + sceMsgDialogTerminate(); + log_error("[UPDATE] IPC INSTALL UPDATE ERROR: %i", error); + return false; + } - raise(SIGQUIT); - } - else { - if (!Launch_Store_URI()) { - log_error("[UPDATE] Failed to launch store uri"); - return false; + log_debug("[UPDATE] Copied, Removing Temp Files"); + log_debug("[UPDATE] Complete..."); + + raise(SIGQUIT); + } else { + if (!Launch_Store_URI()) { + log_error("[UPDATE] Failed to launch store uri"); + return false; + } } - } - return true; + return true; } -void check_for_update_file(){ - - loadmsg(getLangSTR(CHECH_UPDATE_IN_PROGRESS)); +void check_for_update_file() { + IPC_Client& ipc = IPC_Client::getInstance(); + + loadmsg(getLangSTR(LANG_STR::CHECH_UPDATE_IN_PROGRESS)); bool found = false; log_debug("[STARTUP][UPDATE] Checking for update file ..."); - if(if_exists("/user/update_tmp")){ - log_debug("[IF_UPDATE] Found Update temp folder, moving files back.."); //if updated via store + if (if_exists("/user/update_tmp")) { + log_debug("[IF_UPDATE] Found Update temp folder, moving files back.."); //if updated via store copy_dir("/user/update_tmp/covers", "/user/app/ITEM00001/covers"); - copy_dir("/user/update_tmp/custom_app_names", "/user/app/ITEM00001/custom_app_names"); - copyFile("/user/update_tmp/settings.ini", "/user/app/ITEM00001/settings.ini", false); - rmtree("/user/update_tmp"); + copy_dir("/user/update_tmp/custom_app_names", "/user/app/ITEM00001/custom_app_names"); + copyFile("/user/update_tmp/settings.ini", "/user/app/ITEM00001/settings.ini", false); + rmtree("/user/update_tmp"); + unlink("/user/update_tmp"); log_debug("[IF_UPDATE] Moved files back, removing temp folder.."); - } - else - log_info("No Update temp folder found"); - - if(!is_connected_app){ + } else { + log_info("No Update temp folder found"); + } + + if (!is_connected_app) { log_debug("[STARTUP][UPDATE] Daemon Not connected to app, exiting update mode ... "); sceMsgDialogTerminate(); return; } - if(if_exists(APP_PATH("../if_update.pkg"))){ + + if (if_exists(APP_PATH("../if_update.pkg"))) { log_debug("[STARTUP][UPDATE][HDD] if_update.pkg exists, starting update ..."); found = true; - } - else if(usbpath() != -1 && if_exists(fmt::format("/mnt/usb{0:d}/if_update.pkg", usbpath()).c_str())){ + } else if (usbpath() != -1 && if_exists(fmt::format("/mnt/usb{}/if_update.pkg", usbpath()).c_str())) { log_debug("[STARTUP][UPDATE][USB] if_update.pkg exists, starting update ..."); - copyFile(fmt::format("/mnt/usb{0:d}/if_update.pkg", usbpath()), APP_PATH("../if_update.pkg"), false); + copyFile(fmt::format("/mnt/usb{}/if_update.pkg", usbpath()), APP_PATH("../if_update.pkg"), false); found = true; - } - else { + } else { log_debug("[STARTUP][UPDATE] if_update.pkg does not exist, exiting update mode ..."); sceMsgDialogTerminate(); return; } - if(found){ + + if (found) { log_debug("[STARTUP][UPDATE] Update Found, Sending IPC Command for Update Installing ..."); - sprintf(&ipc_msg[1], "%i", getpid()); - log_info("[STARTUP] Sending PID: %s", &ipc_msg[1]); - int error = IPCSendCommand(INSTALL_IF_UPDATE, (uint8_t*)&ipc_msg[0]); - if (error != NO_ERROR) { + ipc_msg = std::to_string(getpid()); + log_info("[STARTUP] Sending PID: %s", ipc_msg.c_str()); + IPC_Ret error = ipc.IPCSendCommand(IPC_Commands::INSTALL_IF_UPDATE, ipc_msg); + if (error != IPC_Ret::NO_ERROR) { log_error("[STARTUP][UPDATE] IPC INSTALL UPDATE ERROR: %i", error); sceMsgDialogTerminate(); return; } - } - else{ + } else { log_debug("[STARTUP][UPDATE] if_update.pkg does not exist, exiting update mode ..."); sceMsgDialogTerminate(); return; @@ -1813,24 +1794,24 @@ bool get_ip_address(std::string &ip) return true; } -void msgok(enum MSG_DIALOG level, std::string in) +void msgok(MSG_DIALOG::MSG_DIALOG_TYPE level, std::string in) { sceMsgDialogTerminate(); std::string out; switch (level) { - case NORMAL: + case MSG_DIALOG::NORMAL: out = in; break; - case FATAL: + case MSG_DIALOG::FATAL: log_fatal(in.c_str()); - out = fmt::format("{0:} {1:}\n\n {2:}", getLangSTR(FATAL_ERROR), in, getLangSTR(PRESS_OK_CLOSE)); + out = fmt::format("{0:} {1:}\n\n {2:}", getLangSTR(LANG_STR::FATAL_ERROR), in, getLangSTR(LANG_STR::PRESS_OK_CLOSE)); break; - case WARNING: + case MSG_DIALOG::WARNING: log_warn( in.c_str()); - out = fmt::format("{0:} {1:}", getLangSTR(WARNING2), in); + out = fmt::format("{0:} {1:}", getLangSTR(LANG_STR::WARNING2), in); break; } @@ -1863,7 +1844,7 @@ void msgok(enum MSG_DIALOG level, std::string in) } } - if (level == FATAL) { + if (level == MSG_DIALOG::FATAL) { log_fatal(in.c_str()); raise(SIGQUIT); } @@ -1895,6 +1876,38 @@ void loadmsg(std::string in) return; } +void scan_for_disc() { + //check if theres a disc + //std::lock_guard < std::mutex > lock(disc_lock); + char stack_tid[16]; + int ret = -1; + + memset( & stack_tid[0], 0, 16); + if ((ret = sceAppInstUtilAppGetInsertedDiscTitleId( & stack_tid[0])) >= 0 && !is_disc_inserted.load()) { + if (!all_apps.empty() && all_apps.size() > 1) { + for (int i = 0; i < all_apps[0].count.token_c + 1; i++) { + // skip if its an FPKG + if (all_apps[i].flags.is_fpkg) + continue; + + // log_info("Disc inserted: %s : %i %s", stack_tid, i , all_apps[i].id.c_str()); + if (all_apps[i].info.id.find( & stack_tid[0]) != std::string::npos) { + //Store the texture ID of the inserted disc + inserted_disc = i; + //inserted_disc: 1 CUSA00900 1 18 + log_info("[UTIL] inserted_disc: %i %s %i %s %i", inserted_disc.load(), stack_tid, i, all_apps[i].info.id.c_str(), all_apps.size()); + is_disc_inserted = true; + break; + } + } + } + } else if (ret == SCE_APP_INSTALLER_ERROR_DISC_NOT_INSERTED) { + // log_info("[UTIL] disc not inserted %x", ret); + inserted_disc = -999; + is_disc_inserted = false; + } +} + bool install_IF_Theme(std::string theme_path){ theme t; @@ -2055,34 +2068,34 @@ uint32_t Launch_App(std::string TITLE_ID, bool silent, int index) { fmt::print("[ERROR] Switch {0:#x}", sys_res); switch (sys_res) { case SCE_LNC_ERROR_APP_NOT_FOUND: { - //msgok(WARNING, getLangSTR(APP_NOT_FOUND)); - if(Confirmation_Msg(fmt::format("{0:}\n\n{1:}\n{2:}", getLangSTR(IF_LAUNCH_TROUBLESHOOTER), getLangSTR(LAUNCH_FAILED_BC_NOT_FOUND), getLangSTR(CONFIRMATION_2))) == YES){ - if(Fix_Game_In_DB(all_apps, index, all_apps[index].is_fpkg)) - ani_notify(NOTIFI_GAME, getLangSTR(GAME_INSERT_SUCCESSFUL), getLangSTR(REPAIR_ATTEMPTED1)); + //msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::APP_NOT_FOUND)); + if(Confirmation_Msg(fmt::format("{0:}\n\n{1:}\n{2:}", getLangSTR(LANG_STR::IF_LAUNCH_TROUBLESHOOTER), getLangSTR(LANG_STR::LAUNCH_FAILED_BC_NOT_FOUND), getLangSTR(LANG_STR::CONFIRMATION_2))) == YES){ + if(Fix_Game_In_DB(all_apps, index, all_apps[index].flags.is_fpkg)) + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::GAME_INSERT_SUCCESSFUL), getLangSTR(LANG_STR::REPAIR_ATTEMPTED1)); else - msgok(WARNING, getLangSTR(GAME_INSERT_FAILED)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::GAME_INSERT_FAILED)); } break; } case SCE_LNC_UTIL_ERROR_ALREADY_RUNNING: { - msgok(WARNING, getLangSTR(APP_OPENED)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::APP_OPENED)); break; } case SCE_LNC_UTIL_ERROR_APPHOME_EBOOTBIN_NOT_FOUND: { - msgok(WARNING, getLangSTR(MISSING_EBOOT)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::MISSING_EBOOT)); break; } case SCE_LNC_UTIL_ERROR_APPHOME_PARAMSFO_NOT_FOUND: { - msgok(WARNING, getLangSTR(MISSING_SFO)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::MISSING_SFO)); break; } case SCE_LNC_UTIL_ERROR_NO_SFOKEY_IN_APP_INFO: { - //msgok(WARNING, getLangSTR(CORRUPT_SFO)); - if(Confirmation_Msg(fmt::format("{0:}\n\n{1:}\n{2:}", getLangSTR(IF_LAUNCH_TROUBLESHOOTER), getLangSTR(FIX_BROKEN_SFO_DB), getLangSTR(CONFIRMATION_3))) == YES){ - if(Fix_Game_In_DB(all_apps, index, all_apps[index].is_fpkg)) - ani_notify(NOTIFI_GAME, getLangSTR(REPAIR_ATTEMPTED), getLangSTR(REPAIR_ATTEMPTED1)); + //msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::CORRUPT_SFO)); + if(Confirmation_Msg(fmt::format("{0:}\n\n{1:}\n{2:}", getLangSTR(LANG_STR::IF_LAUNCH_TROUBLESHOOTER), getLangSTR(LANG_STR::FIX_BROKEN_SFO_DB), getLangSTR(LANG_STR::CONFIRMATION_3))) == YES){ + if(Fix_Game_In_DB(all_apps, index, all_apps[index].flags.is_fpkg)) + ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::REPAIR_ATTEMPTED), getLangSTR(LANG_STR::REPAIR_ATTEMPTED1)); else - msgok(WARNING, getLangSTR(GAME_INSERT_FAILED)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::GAME_INSERT_FAILED)); } break; } @@ -2095,23 +2108,23 @@ uint32_t Launch_App(std::string TITLE_ID, bool silent, int index) { break; } case SCE_LNC_UTIL_ERROR_SETUP_FS_SANDBOX: { - msgok(WARNING, getLangSTR(APP_UNL)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::APP_UNL)); break; } case SCE_LNC_UTIL_ERROR_INVALID_TITLE_ID: { - msgok(WARNING, getLangSTR(ID_NOT_VAILD)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::ID_NOT_VAILD)); break; } case PROCCESS_STARTER_OP_NOT_SUPPORTED: { if(TITLE_ID == APP_HOME_HOST_TID) - msgok(WARNING, getLangSTR(LNC_TOO_MANY_ROOT_FILES)); + msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::LNC_TOO_MANY_ROOT_FILES)); break; } default: { - msgok(WARNING, fmt::format("{0:}: {1:#x}", getLangSTR(LAUNCH_ERROR),sys_res)); + msgok(MSG_DIALOG::WARNING, fmt::format("{0:}: {1:#x}", getLangSTR(LANG_STR::LAUNCH_ERROR),sys_res)); break; } } @@ -2122,7 +2135,7 @@ uint32_t Launch_App(std::string TITLE_ID, bool silent, int index) { } else { if(!silent) - msgok(WARNING, fmt::format("{0:}: {1:#x}", getLangSTR(LAUNCH_ERROR),libcmi)); + msgok(MSG_DIALOG::WARNING, fmt::format("{0:}: {1:#x}", getLangSTR(LANG_STR::LAUNCH_ERROR),libcmi)); } return sys_res; @@ -2135,15 +2148,15 @@ bool is_sfo(const char* input_file_name) { // Get SFO header offset uint32_t magic; fread(&magic, 4, 1, file); - if (magic == 1179865088) { // Param.sfo file + if (magic == 1179865088) { // Param.sfo file rewind(file); - } - else - { + } + else + { log_error("[SFO] Not a param.sfo file. %s", input_file_name); fclose(file); return false; - } + } fclose(file); return true; } @@ -2224,75 +2237,76 @@ bool dlc_pkg_details(const char* src_dest, DLC_PKG_DETAILS &details) { void rebuild_db(){ if (get->sort_cat == NO_CATEGORY_FILTER || - (get->sort_cat != NO_CATEGORY_FILTER && Confirmation_Msg(getLangSTR(CAT_REBUILD_WARNING)) == YES)) + (get->sort_cat != NO_CATEGORY_FILTER && Confirmation_Msg(getLangSTR(LANG_STR::CAT_REBUILD_WARNING)) == YES)) { - progstart((char *)getLangSTR(REBUILDING_FOR_FPKG).c_str()); + progstart((char *)getLangSTR(LANG_STR::REBUILDING_FOR_FPKG).c_str()); int failures = 0; - for (int i = 1; i <= all_apps[0].HDD_count; i++) + for (int i = 1; i <= all_apps[0].count.HDD_count; i++) { - bool is_if = ((all_apps[i].id == "ITEM00001") || (all_apps[i].id == "ITEM99999")); - if (!all_apps[i].is_fpkg || is_if) + bool is_if = ((all_apps[i].info.id == "ITEM00001") || (all_apps[i].info.id == "ITEM99999")); + if (!all_apps[i].flags.is_fpkg || is_if) { // if its not an fpkg or Itemzflow, skip - fmt::print("Skipping {}: {}", is_if ? "IF" : "Retail Game", all_apps[i].name); + fmt::print("Skipping {}: {}", is_if ? "IF" : "Retail Game", all_apps[i].info.name); continue; } - if (i > all_apps[0].HDD_count){ - log_info("Skipping external HDD app: %s ???", all_apps[i].name.c_str()); + if (i > all_apps[0].count.HDD_count){ + log_info("Skipping external HDD app: %s ???", all_apps[i].info.name.c_str()); continue; } - int prog = (uint32_t)(((float)i / all_apps[0].HDD_count) * 100.f); - ProgSetMessagewText(prog, fmt::format("{0:}\n{1:}\n{2:}\n\n {3:} {4:d}\n{5:} {6:d}", getLangSTR(REBUILDING_FOR_FPKG), all_apps[i].name, all_apps[i].id, getLangSTR(CURRENT_APP), i, getLangSTR(TOTAL_NUM_OF_APPS), all_apps[0].HDD_count).c_str()); - log_info("App Info: %s | %i", all_apps[i].name.c_str(), i); + int prog = (uint32_t)(((float)i / all_apps[0].count.HDD_count) * 100.f); + ProgSetMessagewText(prog, fmt::format("{0:}\n{1:}\n{2:}\n\n {3:} {4:d}\n{5:} {6:d}", getLangSTR(LANG_STR::REBUILDING_FOR_FPKG), all_apps[i].info.name, all_apps[i].info.id, getLangSTR(LANG_STR::CURRENT_APP), i, getLangSTR(LANG_STR::TOTAL_NUM_OF_APPS), all_apps[0].count.HDD_count).c_str()); + log_info("App Info: %s | %i", all_apps[i].info.name.c_str(), i); // function also replaces special chars for SQL smt - if (Fix_Game_In_DB(all_apps, i, all_apps[i].is_ext_hdd)) - log_info("Game Fixed: %s", all_apps[i].name.c_str()); + if (Fix_Game_In_DB(all_apps, i, all_apps[i].flags.is_ext_hdd)) + log_info("Game Fixed: %s", all_apps[i].info.name.c_str()); else { - log_error("Game NOT Fixed: %s", all_apps[i].name.c_str()); + log_error("Game NOT Fixed: %s", all_apps[i].info.name.c_str()); failures++; } } sceMsgDialogTerminate(); - loadmsg((char *)getLangSTR(REBUILDING_ADDCONT).c_str()); + loadmsg((char *)getLangSTR(LANG_STR::REBUILDING_ADDCONT).c_str()); addcont_dlc_rebuild("/system_data/priv/mms/addcont.db", false); //addcont_dlc_rebuild("/system_data/priv/mms/addcont.db", true); - msgok(NORMAL, fmt::format("{0:}: {1:d}\n{2:}: {3:d}", getLangSTR(NUMB_OF_FIXED_APPS), all_apps[0].HDD_count-failures, getLangSTR(NOT_FIXED), failures)); + msgok(MSG_DIALOG::NORMAL, fmt::format("{0:}: {1:d}\n{2:}: {3:d}", getLangSTR(LANG_STR::NUMB_OF_FIXED_APPS), all_apps[0].count.HDD_count-failures, getLangSTR(LANG_STR::NOT_FIXED), failures)); } } void rebuild_dlc_db(){ - loadmsg((char *)getLangSTR(REBUILDING_ADDCONT).c_str()); + loadmsg((char *)getLangSTR(LANG_STR::REBUILDING_ADDCONT).c_str()); addcont_dlc_rebuild("/system_data/priv/mms/addcont.db", true); if(addcont_dlc_rebuild("/system_data/priv/mms/addcont.db", false)) - ani_notify(NOTIFI_SUCCESS, getLangSTR(ADDCONT_REBUILT), ""); + ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::ADDCONT_REBUILT), ""); else - ani_notify(NOTIFI_WARNING, getLangSTR(ADDCONT_FAILED), ""); + ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::ADDCONT_FAILED), ""); sceMsgDialogTerminate(); } -void print_Apps_Array(std::vector &b) +void print_Apps_Array(ThreadSafeVector &b) { - for (int i = 0; i <= b.at(0).token_c; i++) + for (int i = 0; i <= b.at(0).count.token_c; i++) { - fmt::print("b[{0:d}].token_d[0].off ptr: {1:}", i, b.at(i).id); - fmt::print("b[{0:d}].token_d[1].off ptr: {1:}", i, b.at(i).name); + fmt::print("b[{0:d}].token_d[0].off ptr: {1:}", i, b.at(i).info.id); + fmt::print("b[{0:d}].token_d[1].off ptr: {1:}", i, b.at(i).info.name); } } -void delete_apps_array(std::vector &apps_arr) +void delete_apps_array(ThreadSafeVector &apps_arr) { if(apps_arr.empty()) return; - for (int i = 1; i <= apps_arr.at(0).token_c; i++) + for (int i = 1; i <= apps_arr.at(0).count.token_c; i++) { - if (apps_arr.at(i).texture > GL_NULL){ - glDeleteTextures(1, &apps_arr.at(i).texture); - apps_arr.at(i).texture = GL_NULL; + GLuint text = apps_arr.at(i).icon.texture.load(); + if (text > GL_NULL){ + glDeleteTextures(1, &text); + apps_arr.at(i).icon.texture = GL_NULL; } } @@ -2305,3 +2319,13 @@ std::ifstream::pos_type file_size(const char* filename) return in.tellg(); } +std::string NormalizeFWVersion(int version) { + std::string versionString = fmt::format("{:x}", version); + versionString.insert(1, "."); + std::string::size_type pos; + while ((pos = versionString.find("0000")) != std::string::npos) { + versionString.erase(pos, 4); // 4 is the length of "0000" + } + + return versionString; +} \ No newline at end of file