├── .github
├── CODEOWNERS
└── workflows
│ └── build.yml
├── .gitignore
├── CHANGELOG.md
├── CMakeLists.txt
├── CODE_OF_CONDUCT.md
├── ICON0.PNG
├── LICENSE
├── README.md
├── data
├── CIRCLE.png
├── CROSS.png
├── LANG
│ ├── ch.txt
│ ├── de.po
│ ├── es.po
│ ├── fi.po
│ ├── fr.po
│ ├── id.po
│ ├── it.po
│ ├── pl.po
│ ├── pt.po
│ ├── tr.po
│ └── zh.txt
├── SQUARE.png
├── TRIANGLE.png
├── background.png
├── fonts
│ ├── NotoSansJP-Medium.otf
│ └── OFL.txt
└── pkgi.txt
├── docs
├── README.md
├── _config.yml
├── leon-luna.jpg
└── screenshot.png
├── include
├── font-8x16.h
├── libfont.h
├── pkgi.h
├── pkgi_aes.h
├── pkgi_config.h
├── pkgi_db.h
├── pkgi_dialog.h
├── pkgi_download.h
├── pkgi_menu.h
├── pkgi_sha256.h
├── pkgi_style.h
├── pkgi_utils.h
└── ttf_render.h
├── source
├── depackager.c
├── libfont.c
├── loadpng.c
├── pkg2iso.c
├── pkgi.c
├── pkgi_aes.c
├── pkgi_config.c
├── pkgi_db.c
├── pkgi_dialog.c
├── pkgi_download.c
├── pkgi_menu.c
├── pkgi_psp.c
├── ttf_fonts.c
└── zip_util.c
└── translate.po
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # CODEOWNERS info: https://help.github.com/en/articles/about-code-owners
2 | # Owners are automatically requested for review for PRs that changes code
3 | # that they own.
4 | * @bucanero
5 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build package
2 |
3 | on: [ push, pull_request, workflow_dispatch ]
4 |
5 | jobs:
6 | build_pkg:
7 | runs-on: ubuntu-22.04
8 | container: pspdev/pspdev:latest
9 | steps:
10 |
11 | - name: Checkout
12 | uses: actions/checkout@v4
13 |
14 | - name: Install dependencies
15 | run: |
16 | apk add zip
17 |
18 | - name: Checkout dbglogger
19 | uses: actions/checkout@v4
20 | with:
21 | repository: bucanero/dbglogger
22 | path: dbglogger
23 |
24 | - name: Install dbglogger
25 | working-directory: dbglogger
26 | run: |
27 | make -f Makefile.psp install
28 |
29 | - name: Build PKGi App Package (OFW)
30 | run: |
31 | mkdir ofw && cd ofw
32 | psp-cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_PRX=ON -DENC_PRX=ON
33 | make
34 | make createzip
35 |
36 | - name: Build PKGi App Package
37 | run: |
38 | psp-cmake . -DCMAKE_BUILD_TYPE=Release
39 | make
40 | make createzip
41 |
42 | - name: Get short SHA
43 | id: slug
44 | run: |
45 | printf '%s\n' "sha8=$(printf '%s\n' ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT
46 |
47 | - name: Push package artifact
48 | uses: actions/upload-artifact@v4
49 | with:
50 | name: pkgi-psp-build_${{ steps.slug.outputs.sha8 }}
51 | path: pkgi-psp.zip
52 | if-no-files-found: error
53 |
54 | - name: Push OFW artifact
55 | uses: actions/upload-artifact@v4
56 | with:
57 | name: pkgi-ofw-build_${{ steps.slug.outputs.sha8 }}
58 | path: ofw/pkgi-psp.zip
59 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to the `pkgi-psp` project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4 |
5 | ## [Unreleased]()
6 |
7 | ## [v1.1.0](https://github.com/bucanero/pkgi-psp/releases/tag/v1.1.0) - 2022-03-03
8 |
9 | ### Added
10 |
11 | * Show battery level
12 | * .Zip file support
13 | - Download and extract `.zip` links
14 | * Local .PKG installation
15 | - Install `.pkg` files from the PSP's memory stick
16 | - Scan and list packages from `ms0:/PKG`
17 | * Add PSP-Go storage option
18 | - Edit `config.txt` to change storage location (add line `storage ms0`)
19 |
20 | ### Misc
21 |
22 | * Add OFW-compatible build
23 |
24 | ### Fixed
25 |
26 | * Fix progress bar ETA when resuming downloads
27 |
28 | ## [v1.0.0](https://github.com/bucanero/pkgi-psp/releases/tag/v1.0.0) - 2023-10-14
29 |
30 | ### Added
31 |
32 | * Package install as `ISO`/`CSO`/`Digital`
33 | * PSP Go internal storage detection (`ef0`/`ms0`)
34 | * Update database files from URLs (`Refresh` option)
35 | * Enabled `Search` option (on-screen keyboard)
36 | * Added PSX category
37 | * PKGi new version check & notification
38 |
39 | ### Fixed
40 |
41 | * Install and decrypt themes to `/PSP/THEME/`
42 |
43 | ### Misc
44 |
45 | * Improved download speed (~400Kb/s average)
46 | * Network proxy settings support
47 |
48 | ## [v0.8.0](https://github.com/bucanero/pkgi-psp/releases/tag/v0.8.0) - 2023-10-01
49 |
50 | First public release. In memory of Leon & Luna.
51 |
52 | ### Added
53 |
54 | * Download and install PKG files to the PSP
55 | * Support for loading multiple database files
56 | - Generic text database format support
57 | - Filter unsupported or missing URLs when loading a database
58 | * Content categorization and filtering
59 | * Support for `HTTP`, `HTTPS`, `FTP`, `FTPS` links with TLS v1.2
60 | * Localization support (Finnish, French, German, Indonesian, Italian, Polish, Portuguese, Spanish, Turkish)
61 | - Language detection based on PSP settings
62 | * Enter button detection (`cross`/`circle`)
63 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.2)
2 |
3 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
4 | if(NOT DEFINED ENV{PSPDEV})
5 | message(FATAL_ERROR "Please define PSPDEV to point to your SDK path!")
6 | endif()
7 | endif()
8 |
9 | project(pkgi-psp)
10 |
11 | find_package(SDL2 REQUIRED)
12 | find_package(CURL REQUIRED)
13 |
14 | option(PKGI_ENABLE_DEBUG "enables debug logging over udp multicast" OFF)
15 |
16 | if(PKGI_ENABLE_DEBUG)
17 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPKGI_ENABLE_LOGGING=1")
18 | endif()
19 |
20 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11 -G0")
21 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
22 |
23 | include_directories(
24 | include
25 | ${PSPDEV}/psp/include/freetype2
26 | )
27 |
28 | link_directories(
29 | libs
30 | )
31 |
32 | # From vita shell CMakeLists - credits TheFlow
33 | FUNCTION(ADD_RESOURCES out_var)
34 | SET(result)
35 | FOREACH(in_f ${ARGN})
36 | SET(out_f "${CMAKE_CURRENT_BINARY_DIR}/${in_f}.o")
37 | GET_FILENAME_COMPONENT(out_dir ${out_f} DIRECTORY)
38 | ADD_CUSTOM_COMMAND(OUTPUT ${out_f}
39 | COMMAND ${CMAKE_COMMAND} -E make_directory ${out_dir}
40 | COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} ${in_f}
41 | DEPENDS ${in_f}
42 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
43 | COMMENT "Building resource ${out_f}"
44 | VERBATIM
45 | )
46 | LIST(APPEND result ${out_f})
47 | ENDFOREACH()
48 | SET(${out_var} "${result}" PARENT_SCOPE)
49 | ENDFUNCTION()
50 |
51 | file(GLOB res_files RELATIVE
52 | ${CMAKE_SOURCE_DIR}
53 | data/*.png
54 | data/*.ahx
55 | )
56 | add_resources(apollo_res ${res_files})
57 |
58 | add_executable(${PROJECT_NAME}
59 | ${apollo_res}
60 | source/pkg2iso.c
61 | source/depackager.c
62 | source/loadpng.c
63 | source/libfont.c
64 | source/ttf_fonts.c
65 | source/zip_util.c
66 | source/pkgi.c
67 | source/pkgi_aes.c
68 | source/pkgi_db.c
69 | source/pkgi_download.c
70 | source/pkgi_psp.c
71 | source/pkgi_config.c
72 | source/pkgi_dialog.c
73 | source/pkgi_menu.c
74 | )
75 |
76 | target_link_libraries(${PROJECT_NAME}
77 | ${SDL2_LIBRARIES}
78 | CURL::libcurl
79 | dbglogger
80 | freetype
81 | png
82 | pspnet
83 | pspnet_apctl
84 | mini18n
85 | zip
86 | bz2
87 | z
88 | )
89 |
90 | create_pbp_file(
91 | TARGET ${PROJECT_NAME}
92 | ICON_PATH ${CMAKE_SOURCE_DIR}/ICON0.PNG
93 | BACKGROUND_PATH NULL
94 | PREVIEW_PATH NULL
95 | TITLE "PKGi PSP"
96 | VERSION 01.10
97 | )
98 |
99 | add_custom_target(createzip
100 | COMMAND @mkdir -p PKGI/LANG
101 | COMMAND @cp EBOOT.PBP PKGI/
102 | COMMAND @cp ${CMAKE_SOURCE_DIR}/data/fonts/NotoSansJP-Medium.otf PKGI/FONT.OTF
103 | COMMAND @cp ${CMAKE_SOURCE_DIR}/data/LANG/* PKGI/LANG/
104 | COMMAND @rm -fr pkgi-psp.zip
105 | COMMAND @zip -r pkgi-psp.zip PKGI/
106 | )
107 |
108 | add_custom_target(copy
109 | COMMAND @echo "Copying to $$PSPMS/PSP/GAME/PKGI/EBOOT.PBP ..."
110 | COMMAND @mkdir -p $$PSPMS/PSP/GAME/PKGI
111 | COMMAND @cp -v EBOOT.PBP $$PSPMS/PSP/GAME/PKGI/EBOOT.PBP
112 | DEPENDS ${PROJECT_NAME}
113 | )
114 |
115 | add_custom_target(send
116 | COMMAND @echo "Uploading to ftp://$$PSPIP:1337/ms0:/PSP/GAME/PKGI/EBOOT.PBP ..."
117 | COMMAND @curl -T EBOOT.PBP ftp://$$PSPIP:1337/ms0:/PSP/GAME/PKGI/EBOOT.PBP
118 | DEPENDS ${PROJECT_NAME}
119 | )
120 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | Code of Conduct
2 | ===============
3 |
4 | My goal is to provide quality open-source software that everyone can use.
5 | While I may not be able to address every request or accept every contribution
6 | to this project, I will do my best to develop and maintain it for the common
7 | good.
8 |
9 | As part of the open-source community, I expect everyone to:
10 |
11 | - Be friendly and patient.
12 | - Be respectful, even if we disagree.
13 | - Be honest.
14 | - Be accepting of all people.
15 | - Fully explain your concerns, issues, or ideas.
16 |
--------------------------------------------------------------------------------
/ICON0.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/ICON0.PNG
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Damian Parrino
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PKGi PSP
2 |
3 | [![Downloads][img_downloads]][pkgi_downloads] [![Release][img_latest]][pkgi_latest] [![License][img_license]][pkgi_license]
4 | [](https://github.com/bucanero/pkgi-psp/actions/workflows/build.yml)
5 | [](https://twitter.com/dparrino)
6 |
7 | **PKGi PSP** is a PlayStation Portable port of [pkgi-ps3](https://github.com/bucanero/pkgi-ps3).
8 |
9 | This homebrew app allows to download and install `.pkg` files directly on your PSP.
10 |
11 | 
12 |
13 | **Comments, ideas, suggestions?** You can [contact me](https://github.com/bucanero/) on [Twitter](https://twitter.com/dparrino) and on [my website](http://www.bucanero.com.ar/).
14 |
15 | # Features
16 |
17 | * **easy to use:** list available downloads, including searching, filtering, and sorting.
18 | * **standalone:** no PC required, everything happens directly on the PSP.
19 | * **automatic downloads:** just choose an item, and it will be downloaded by the app to your Memory Stick (`ms0:/PKG/`).
20 | * **resumes interrupted downloads:** you can stop a download at any time, switch applications, and come back to resume the download later.
21 | * **localization support:** Finnish, French, German, Indonesian, Italian, Polish, Portuguese, Spanish, Turkish
22 |
23 | ### Notes:
24 | * **content activation:** downloaded content requires a valid license to run. If your PSP hasn't been activated, you can use one of the following plugins:
25 | - [npdrm_free plugin](https://github.com/qwikrazor87/npdrm_free) (PSP/PS1/PCEngine games and DLCs)
26 | - [npdrm_free_mod plugin](https://github.com/lusid1/npdrm_free_mod) (PSP/PS1 games only)
27 | - [nploader_mod](https://github.com/lusid1/nploader_mod) (PSP DLCs only)
28 |
29 | # Download
30 |
31 | Get the [latest version here][pkgi_latest].
32 |
33 | ### Changelog
34 |
35 | See the [latest changes here](CHANGELOG.md).
36 |
37 | # Setup instructions
38 |
39 | You need to create a [`pkgi.txt`](#sample-db-file) file in `ms0:/PSP/GAME/PKGI/` that contains the items available for installation.
40 | The text database format is user customizable. Check [this section](#user-defined-db-format) to learn how to define your own custom DB format.
41 |
42 | ## Multiple databases
43 |
44 | You can also load additional database files:
45 |
46 | - `pkgi_games.txt`
47 | - `pkgi_dlcs.txt`
48 | - `pkgi_themes.txt`
49 | - `pkgi_psx.txt`
50 | - `pkgi_demos.txt`
51 | - `pkgi_updates.txt`
52 | - `pkgi_emulators.txt`
53 | - `pkgi_apps.txt`
54 |
55 | Items on each of these files will be auto-categorized to the file content type. **Note:** The app assumes that every database file has the same format, as defined in `dbformat.txt`.
56 |
57 | ## Online DB update
58 |
59 | You can refresh and sync an online database by adding the DB URL(s) to the `config.txt` file in `ms0:/PSP/GAME/PKGI/`.
60 |
61 | For example:
62 |
63 | ```
64 | url_games http://www.mysite.com/mylist.csv
65 | url_demos http://www.demos.com/otherlist.csv
66 | url_emulators http://www.example.com/emulators.csv
67 | ```
68 |
69 | Using this setup,
70 | - `pkgi_games.txt` will be updated with `http://www.mysite.com/mylist.csv`,
71 | - `pkgi_demos.txt` with `http://www.demos.com/otherlist.csv`,
72 | - and `pkgi_emulators.txt` with `http://www.example.com/emulators.csv`.
73 |
74 | Next time you open the app, you'll have an additional menu option  called **Refresh**. When you select it, the local databases will be syncronized with the defined URLs.
75 |
76 | # DB formats
77 |
78 | The application needs a text database that contains the items available for installation, and it must follow the [default format definition](#default-db-format), or have a [custom format definition](#user-defined-db-format) file.
79 |
80 | ## Default DB format
81 |
82 | The default database file format uses a very simple CSV format where each line means one item in the list:
83 |
84 | ```
85 | contentid,type,name,description,rap,url,size,checksum
86 | ```
87 |
88 | where:
89 |
90 | | Column | Description |
91 | |--------|-------------|
92 | | `contentid` | is the full content id of the item, for example: `UP0000-NPXX99999_00-0000112223333000`.
93 | | `type` | is a number for the item's content type. See the [table below](#content-types) for details. (set it to 0 if unknown)
94 | | `name` | is a string for the item's name.
95 | | `description` | is a string for the item's description.
96 | | `rap` | the 16 hex bytes for a RAP file, if needed by the item (`.rap` files will be created on `ms0:/PKG/RAP`). Leave empty to skip the `.rap` file.
97 | | `url` | is the HTTP/HTTPS/FTP/FTPS URL where to download the `.pkg` file.
98 | | `size` | is the size in bytes of the `.pkg` file, or 0 if unknown.
99 | | `checksum` | is a SHA256 digest of the `.pkg` file (as 32 hex bytes) to make sure the file is not tampered with. Leave empty to skip the check.
100 |
101 | **Note:** `name` and `description` cannot contain newlines or commas.
102 |
103 | ### Sample DB file
104 |
105 | An example `pkgi.txt` file following the `contentid,type,name,description,rap,url,size,checksum` format:
106 |
107 | ```
108 | U00001-NP0APOLLO_00-0000000000000000,8,Apollo Save Tool v1.3.0,Save-game manager,,https://github.com/bucanero/apollo-psp/releases/download/v1.3.0/apollo-psp.zip,6885196,DFF88635A044F319686231ACABB548CC42EA1CF3A992856572C7AF20F70D30ED
109 | U00001-CMFILEMGR_00-0000000000000000,8,CM File Manager v4.10,File manager,,https://github.com/joel16/CMFileManager-PSP/releases/download/v4.10/CMFileManager-PSP.zip,1815998,7A5FD10184546AB993A4D5F3054BCBA9E9B7A1C569EE26E366F1F5CC9DA5A554
110 | UP0001-PSPTOOL10_00-0000000000000000,8,PSP Tool v1.00,PSP Tool,,https://archive.org/download/psp-tool.-7z/PSP%20Tool.zip,5023819,
111 | UP0001-PSPFILER6_00-0000000000000000,8,PSP Filer v6.6,File manager,,https://wololo.net/download.php?f=filer6.6.zip,1295106,
112 | UP0001-NZPORTABL_00-0000000000000000,1,Nazi Zombies: Portable,Nightly 64 Mb,,https://github.com/nzp-team/nzportable/releases/download/nightly/nzportable-psp-64mb.zip,36163686,
113 | UP0001-IRISMAN00_00-VER4880000000000,8,IRISMAN 4.88.1,Backup Manager,,http://github.com/aldostools/IRISMAN/releases/download/4.88/IRISMAN_4.88.pkg,29411984,E6EF607F0002B31BFB148BE4FC9BDBACB4E53110751F0E667C701D40B5290570
114 | ```
115 |
116 | ### Content types
117 |
118 | | Type value | Content type | DB File |
119 | |------------|--------------|---------|
120 | | 0 | Unknown |
121 | | 1 | Game | `pkgi_games.txt`
122 | | 2 | DLC | `pkgi_dlcs.txt`
123 | | 3 | Theme | `pkgi_themes.txt`
124 | | 4 | PSX | `pkgi_psx.txt`
125 | | 5 | Demo | `pkgi_demos.txt`
126 | | 6 | Update | `pkgi_updates.txt`
127 | | 7 | Emulator | `pkgi_emulators.txt`
128 | | 8 | Application | `pkgi_apps.txt`
129 |
130 | ## User-defined DB format
131 |
132 | To use a custom database format, you need to create a `dbformat.txt` file, and save it on `ms0:/PSP/GAME/PKGI/`.
133 |
134 | The `dbformat.txt` definition file is a 2-line text file:
135 | * Line 1: the custom delimiter character (e.g.: `;`, `,`, `|`, etc.)
136 | * Line 2: the column names for every column in the custom database, delimited by the proper delimiter defined in line 1
137 |
138 | **Note:** For the columns to be properly recognized, use the column tag names defined in the table above.
139 |
140 | All the columns are optional. Your database might have more (or less) columns, so any unrecognized column will be skipped.
141 |
142 | ### Example
143 |
144 | Example `dbformat.txt`, for a database using semi-colon (`;`) as separator:
145 |
146 | ```
147 | ;
148 | name;TITLE ID;REGION;description;AUTHOR;TYPE;url;rap;size
149 | ```
150 |
151 | **Result:** only the `name,description,url,rap,size` fields will be used.
152 |
153 | ### Example
154 |
155 | Example `dbformat.txt`, for a database using character pipe (`|`) as separator:
156 |
157 | ```
158 | |
159 | REGION|TITLE|name|url|rap|contentid|DATE|PKG FILENAME|size|checksum
160 | ```
161 |
162 | **Result:** only the `name,url,rap,contentid,size,checksum` fields will be used.
163 |
164 | # Usage
165 |
166 | Using the application is simple and straight-forward:
167 |
168 | - Move UP/DOWN to select the item you want to download, and press .
169 | - To see the item's details, press .
170 | - To sort/filter/search press .
171 | It will open the context menu. Press  again to confirm the new settings, or press  to cancel any changes.
172 | - Press left or right trigger buttons L1/R1 to move pages up or down.
173 | - Press LEFT/RIGHT buttons to switch between categories.
174 |
175 | ### Notes
176 |
177 | - **RAP data:** if the item has `.rap` data, the file will be saved in the `ms0:/PKG/RAP/` folder.
178 |
179 |
180 | # Q&A
181 |
182 | 1. Where to get a `rap` string?
183 |
184 | You can use a tool like RIF2RAP to generate a `.rap` from your existing `.rif` files. Then you can use a tool like `hexdump` to get the hex byte string.
185 |
186 | 2. Where to get `.pkg` links?
187 |
188 | You can use [PSDLE][] to find `.pkg` URLs for the games you own. Then either use the original URL, or host the file on your own web server.
189 |
190 | 3. Where to remove interrupted/failed downloads to free up disk space?
191 |
192 | Check the `ms0:/PKG/` folder - each download will be in a separate `.pkg` file by its content ID. Simply delete the file and start again.
193 |
194 | 4. Download speed is too slow!
195 |
196 | Optimization is still pending. (Optional) Set `Power Save Settings` -> `WLAN Power save` -> `OFF` , if you want to speed up the download process.
197 |
198 | # Credits
199 |
200 | * [Bucanero](http://www.bucanero.com.ar/): Project developer
201 |
202 | ## Acknowledgements
203 |
204 | * [mmozeiko](https://github.com/mmozeiko/): [pkgi](https://github.com/mmozeiko/pkgi) (PS Vita), [pkg2zip](https://github.com/mmozeiko/pkg2zip)
205 | * [qwikrazor87](https://github.com/qwikrazor87/): [Depackager](https://github.com/bucanero/psptools/tree/master/depackager)
206 |
207 | # Building
208 |
209 | You need to have installed:
210 |
211 | - [PSP SDK](https://github.com/pspdev/)
212 | - [mbedTLS](https://github.com/pspdev/psp-packages/tree/master/mbedtls) library
213 | - [cURL](https://github.com/pspdev/psp-packages/tree/master/curl) library
214 | - [Mini18n](https://github.com/bucanero/mini18n) library
215 | - [dbglogger](https://github.com/bucanero/dbglogger) library (only required for debug logging)
216 |
217 | Run `cmake . && make` to create a release build. If you want to create a `.zip` file, run `make createzip`.
218 |
219 | ## Debugging
220 |
221 | To enable debug logging, pass `-DPKGI_ENABLE_DEBUG=ON` argument to `cmake`. The application will write debug messages to
222 |
223 | ms0:/pkgi-psp.log
224 |
225 | You can also set the `PSPIP` environment variable to your PSP's IP address, and use `make send` to upload `EBOOT.PBP` directly to the `ms0:/PSP/GAME/PKGI` folder.
226 |
227 | # License
228 |
229 | `pkgi-psp` is released under the [MIT License](LICENSE).
230 |
231 | [PSDLE]: https://repod.github.io/psdle/
232 | [socat]: http://www.dest-unreach.org/socat/
233 | [pkgi_downloads]: https://github.com/bucanero/pkgi-psp/releases
234 | [pkgi_latest]: https://github.com/bucanero/pkgi-psp/releases/latest
235 | [pkgi_license]: https://github.com/bucanero/pkgi-psp/blob/main/LICENSE
236 | [img_downloads]: https://img.shields.io/github/downloads/bucanero/pkgi-psp/total.svg?maxAge=3600
237 | [img_latest]: https://img.shields.io/github/release/bucanero/pkgi-psp.svg?maxAge=3600
238 | [img_license]: https://img.shields.io/github/license/bucanero/pkgi-psp.svg?maxAge=2592000
239 |
--------------------------------------------------------------------------------
/data/CIRCLE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/CIRCLE.png
--------------------------------------------------------------------------------
/data/CROSS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/CROSS.png
--------------------------------------------------------------------------------
/data/LANG/ch.txt:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: PKGi PSP\n"
4 | "POT-Creation-Date: 2021-10-09 19:00-0300\n"
5 | "PO-Revision-Date: 2021-10-09 19:00-0300\n"
6 | "Last-Translator: Croden1999\n"
7 | "Language-Team: \n"
8 | "Language: ch\n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=UTF-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "X-Generator: Poedit 3.0\n"
13 | "X-Poedit-Basepath: ./source\n"
14 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
15 | "X-Poedit-Flags-xgettext: --add-comments\n"
16 | "X-Poedit-SourceCharset: UTF-8\n"
17 | "X-Poedit-KeywordsList: _\n"
18 | "X-Poedit-SearchPath-0: .\n"
19 |
20 | #: pkgi.c:82
21 | msgid "Installing..."
22 | msgstr "安裝中"
23 |
24 | #: pkgi.c:82
25 | msgid "Please wait..."
26 | msgstr "請稍等..."
27 |
28 | #: pkgi.c:94
29 | msgid "Installation failed"
30 | msgstr "安裝失敗"
31 |
32 | #: pkgi.c:118
33 | msgid "Successfully installed"
34 | msgstr "下載完成"
35 |
36 | #: pkgi.c:122
37 | msgid "Task successfully queued (reboot to start)"
38 | msgstr "任務已成功排隊(重啓開始)"
39 |
40 | #: pkgi.c:163
41 | msgid "GB"
42 | msgstr ""
43 |
44 | #: pkgi.c:167 pkgi_download.c:249
45 | msgid "MB"
46 | msgstr ""
47 |
48 | #: pkgi.c:171 pkgi.c:504 pkgi_download.c:253
49 | msgid "KB"
50 | msgstr ""
51 |
52 | #: pkgi.c:175
53 | msgid "B"
54 | msgstr ""
55 |
56 | #: pkgi.c:185
57 | #, c-format
58 | msgid "pkg requires %u %s free space, but only %u %s available"
59 | msgstr "pkg需要%u %s剩餘空间, 但只有%u %s可用"
60 |
61 | #: pkgi.c:225 pkgi_menu.c:109 pkgi_menu.c:121
62 | msgid "All"
63 | msgstr "全部"
64 |
65 | #: pkgi.c:226 pkgi_menu.c:122
66 | msgid "Games"
67 | msgstr "游戲"
68 |
69 | #: pkgi.c:227 pkgi_menu.c:123
70 | msgid "DLCs"
71 | msgstr "追加内容"
72 |
73 | #: pkgi.c:228 pkgi_menu.c:124
74 | msgid "Themes"
75 | msgstr "主題"
76 |
77 | #: pkgi.c:229 pkgi_menu.c:125
78 | msgid "Avatars"
79 | msgstr "頭像"
80 |
81 | #: pkgi.c:230 pkgi_menu.c:126
82 | msgid "Demos"
83 | msgstr "試玩"
84 |
85 | #: pkgi.c:231 pkgi_menu.c:118 pkgi_menu.c:127
86 | msgid "Updates"
87 | msgstr "更新"
88 |
89 | #: pkgi.c:232 pkgi_menu.c:128
90 | msgid "Emulators"
91 | msgstr "模擬器"
92 |
93 | #: pkgi.c:233 pkgi_menu.c:129
94 | msgid "Apps"
95 | msgstr "應用程序"
96 |
97 | #: pkgi.c:234 pkgi_menu.c:130
98 | msgid "Tools"
99 | msgstr "工具"
100 |
101 | #: pkgi.c:235
102 | msgid "Unknown"
103 | msgstr "未知"
104 |
105 | #: pkgi.c:253
106 | msgid "Exit to XMB?"
107 | msgstr "退出到XMB嗎?"
108 |
109 | #: pkgi.c:436
110 | msgid "No items!"
111 | msgstr "無內容!"
112 |
113 | #: pkgi.c:462
114 | msgid "Item already installed, download again?"
115 | msgstr "內容已安裝,是否重新下载?"
116 |
117 | #: pkgi.c:471 pkgi_download.c:372 pkgi_download.c:652
118 | msgid "Downloading..."
119 | msgstr "下載中..."
120 |
121 | #: pkgi.c:471 pkgi.c:674
122 | msgid "Preparing..."
123 | msgstr "準備中..."
124 |
125 | #: pkgi.c:504 pkgi.c:508
126 | msgid "Refreshing"
127 | msgstr "刷新中"
128 |
129 | #: pkgi.c:563 pkgi.c:567
130 | msgid "Count"
131 | msgstr "總數"
132 |
133 | #: pkgi.c:575
134 | msgid "Free"
135 | msgstr "剩餘"
136 |
137 | #: pkgi.c:585
138 | msgid "Select"
139 | msgstr "選擇"
140 |
141 | #: pkgi.c:585
142 | msgid "Close"
143 | msgstr "關閉"
144 |
145 | #: pkgi.c:585
146 | msgid "Cancel"
147 | msgstr "取消"
148 |
149 | #: pkgi.c:589 pkgi_download.c:136
150 | msgid "Download"
151 | msgstr "下載"
152 |
153 | #: pkgi.c:589
154 | msgid "Menu"
155 | msgstr "菜單"
156 |
157 | #: pkgi.c:589
158 | msgid "Details"
159 | msgstr "詳細"
160 |
161 | #: pkgi.c:589
162 | msgid "Exit"
163 | msgstr "退出"
164 |
165 | #: pkgi.c:678
166 | msgid "Successfully downloaded PKGi PSP update"
167 | msgstr "下載PKGi PSP更新成功"
168 |
169 | #: pkgi.c:1051
170 | msgid "Search"
171 | msgstr "搜索"
172 |
173 | #: pkgi_db.c:145 pkgi_db.c:153
174 | msgid "failed to download list from"
175 | msgstr "下載列表失敗"
176 |
177 | #: pkgi_db.c:159
178 | msgid "list is too large... check for newer pkgi version!"
179 | msgstr "列表過大...檢查是不是新的pkgi版本!"
180 |
181 | #: pkgi_db.c:187
182 | msgid "list is empty... check the DB server"
183 | msgstr "列表為空...檢查數據庫服務器"
184 |
185 | #: pkgi_db.c:390
186 | msgid "ERROR: pkgi.txt file(s) missing or bad config.txt file"
187 | msgstr "錯誤:pkgi.txt文件丟失或配置文件錯誤"
188 |
189 | #: pkgi_dialog.c:80
190 | msgid "Content"
191 | msgstr "內容"
192 |
193 | #: pkgi_dialog.c:108
194 | msgid "ERROR"
195 | msgstr "錯誤"
196 |
197 | #: pkgi_dialog.c:169
198 | msgid "Failed to download the update list"
199 | msgstr "下载更新列表失败"
200 |
201 | #: pkgi_dialog.c:174
202 | msgid "update(s) loaded"
203 | msgstr "更新加載"
204 |
205 | #: pkgi_dialog.c:319
206 | #, c-format
207 | msgid "press %s to cancel"
208 | msgstr "按%s取消"
209 |
210 | #: pkgi_dialog.c:333
211 | #, c-format
212 | msgid "press %s to close - %s to scan updates"
213 | msgstr "按%s關閉 - %s掃描更新"
214 |
215 | #: pkgi_dialog.c:365
216 | msgid "Enter"
217 | msgstr "確認"
218 |
219 | #: pkgi_dialog.c:365
220 | msgid "Back"
221 | msgstr "返回"
222 |
223 | #: pkgi_dialog.c:367
224 | #, c-format
225 | msgid "press %s to close"
226 | msgstr "按%s關閉"
227 |
228 | #: pkgi_download.c:183
229 | msgid "Install"
230 | msgstr "安裝"
231 |
232 | #: pkgi_download.c:216 pkgi_download.c:220 pkgi_download.c:226
233 | msgid "ETA"
234 | msgstr ""
235 |
236 | #: pkgi_download.c:310 pkgi_download.c:392
237 | msgid "Could not send HTTP request"
238 | msgstr "無法發送HTTP請求"
239 |
240 | #: pkgi_download.c:317 pkgi_download.c:399
241 | msgid "HTTP request failed"
242 | msgstr "HTTP請求失敗"
243 |
244 | #: pkgi_download.c:322 pkgi_download.c:404
245 | msgid "HTTP response has unknown length"
246 | msgstr "未知HTTP響應長度"
247 |
248 | #: pkgi_download.c:331
249 | msgid "Not enough free space on HDD"
250 | msgstr "硬盤空間不足"
251 |
252 | #: pkgi_download.c:340
253 | msgid "Could not create task directory on HDD."
254 | msgstr "無法在硬盤創建任務目錄。"
255 |
256 | #: pkgi_download.c:348
257 | msgid "Saving background task..."
258 | msgstr "保存後臺任務中..."
259 |
260 | #: pkgi_download.c:354
261 | msgid "Could not create PKG file to HDD."
262 | msgstr "無法創建PKG文件到硬盤裏。"
263 |
264 | #: pkgi_download.c:360
265 | msgid "Could not create task files to HDD."
266 | msgstr "無法創建任務文件到硬盘裏。"
267 |
268 | #: pkgi_download.c:425
269 | msgid "HTTP download error"
270 | msgstr "HTTP下載錯誤"
271 |
272 | #: pkgi_download.c:432
273 | msgid "HTTP connection closed"
274 | msgstr "HTTP連接關閉"
275 |
276 | #: pkgi_download.c:445
277 | msgid "failed to write to"
278 | msgstr "寫入失敗"
279 |
280 | #: pkgi_download.c:465
281 | msgid "cannot create folder"
282 | msgstr "無法創建文件夹"
283 |
284 | #: pkgi_download.c:475
285 | msgid "cannot create file"
286 | msgstr "無法創建文件"
287 |
288 | #: pkgi_download.c:490
289 | msgid "cannot resume file"
290 | msgstr "無法恢復文件"
291 |
292 | #: pkgi_download.c:562
293 | msgid "pkg integrity failed, try downloading again"
294 | msgstr "pkg完整性失敗,請嘗試重新下載"
295 |
296 | #: pkgi_download.c:573
297 | msgid "Creating RAP file"
298 | msgstr "創建RAP文件中"
299 |
300 | #: pkgi_download.c:581 pkgi_download.c:626
301 | msgid "Cannot save"
302 | msgstr "無法保存"
303 |
304 | #: pkgi_download.c:621
305 | msgid "Creating RIF file"
306 | msgstr "創建RIF文件中"
307 |
308 | #: pkgi_download.c:646
309 | msgid "Resuming..."
310 | msgstr "恢復中..."
311 |
312 | #: pkgi_download.c:652
313 | msgid "Adding background task..."
314 | msgstr "添加後臺任務中..."
315 |
316 | #: pkgi_download.c:675
317 | msgid "Downloading icon"
318 | msgstr "下載圖標中"
319 |
320 | #: pkgi_download.c:713
321 | msgid "Could not create install directory on HDD."
322 | msgstr "無法在硬盤創建安裝目錄。"
323 |
324 | #: pkgi_menu.c:102
325 | msgid "Search..."
326 | msgstr "搜索..."
327 |
328 | #: pkgi_menu.c:103
329 | msgid "Sort by:"
330 | msgstr "排序方式:"
331 |
332 | #: pkgi_menu.c:104
333 | msgid "Title"
334 | msgstr "標題"
335 |
336 | #: pkgi_menu.c:105
337 | msgid "Region"
338 | msgstr "區域"
339 |
340 | #: pkgi_menu.c:106
341 | msgid "Name"
342 | msgstr "名稱"
343 |
344 | #: pkgi_menu.c:107
345 | msgid "Size"
346 | msgstr "大小"
347 |
348 | #: pkgi_menu.c:108
349 | msgid "Content:"
350 | msgstr "內容:"
351 |
352 | #: pkgi_menu.c:110
353 | msgid "Regions:"
354 | msgstr "區域:"
355 |
356 | #: pkgi_menu.c:111
357 | msgid "Asia"
358 | msgstr "亞洲"
359 |
360 | #: pkgi_menu.c:112
361 | msgid "Europe"
362 | msgstr "歐洲"
363 |
364 | #: pkgi_menu.c:113
365 | msgid "Japan"
366 | msgstr "日本"
367 |
368 | #: pkgi_menu.c:114
369 | msgid "USA"
370 | msgstr "美國"
371 |
372 | #: pkgi_menu.c:115
373 | msgid "Options:"
374 | msgstr "選項:"
375 |
376 | #: pkgi_menu.c:116
377 | msgid "ISO"
378 | msgstr "後臺下載"
379 |
380 | #: pkgi_menu.c:117
381 | msgid "Keep PKGs"
382 | msgstr "音樂"
383 |
384 | #: pkgi_menu.c:119
385 | msgid "Refresh..."
386 | msgstr "刷新中..."
387 |
388 | #: pkgi_menu.c:326
389 | msgid "Direct DL"
390 | msgstr "直接下載"
391 |
--------------------------------------------------------------------------------
/data/LANG/de.po:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/LANG/de.po
--------------------------------------------------------------------------------
/data/LANG/es.po:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/LANG/es.po
--------------------------------------------------------------------------------
/data/LANG/fi.po:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/LANG/fi.po
--------------------------------------------------------------------------------
/data/LANG/fr.po:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/LANG/fr.po
--------------------------------------------------------------------------------
/data/LANG/id.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: PKGi PSP\n"
4 | "POT-Creation-Date: 2021-10-09 19:00-0300\n"
5 | "PO-Revision-Date: 2021-10-09 19:00-0300\n"
6 | "Last-Translator: Fajar Maulana\n"
7 | "Language-Team: \n"
8 | "Language: id\n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=UTF-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "X-Generator: Poedit 3.0\n"
13 | "X-Poedit-Basepath: ./source\n"
14 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
15 | "X-Poedit-Flags-xgettext: --add-comments\n"
16 | "X-Poedit-SourceCharset: UTF-8\n"
17 | "X-Poedit-KeywordsList: _\n"
18 | "X-Poedit-SearchPath-0: .\n"
19 |
20 | #: pkgi.c:82
21 | msgid "Installing..."
22 | msgstr "Menginstal..."
23 |
24 | #: pkgi.c:82
25 | msgid "Please wait..."
26 | msgstr "Harap tunggu"
27 |
28 | #: pkgi.c:94
29 | msgid "Installation failed"
30 | msgstr "Penginstalan gagal"
31 |
32 | #: pkgi.c:118
33 | msgid "Successfully installed"
34 | msgstr "Terunduh dengan sukses"
35 |
36 | #: pkgi.c:122
37 | msgid "Task successfully queued (reboot to start)"
38 | msgstr "Tugas berhasil ditambahkan ke antrean (reboot untuk memulai)"
39 |
40 | #: pkgi.c:163
41 | msgid "GB"
42 | msgstr "GB"
43 |
44 | #: pkgi.c:167 pkgi_download.c:249
45 | msgid "MB"
46 | msgstr "MB"
47 |
48 | #: pkgi.c:171 pkgi.c:504 pkgi_download.c:253
49 | msgid "KB"
50 | msgstr "KB"
51 |
52 | #: pkgi.c:175
53 | msgid "B"
54 | msgstr "B"
55 |
56 | #: pkgi.c:185
57 | #, c-format
58 | msgid "pkg requires %u %s free space, but only %u %s available"
59 | msgstr "pkg memerlukan ruang kosong sebesar %u %s, tetapi Anda hanya memiliki %u %s ruang yang tersisa"
60 |
61 | #: pkgi.c:225 pkgi_menu.c:109 pkgi_menu.c:121
62 | msgid "All"
63 | msgstr "Semua"
64 |
65 | #: pkgi.c:226 pkgi_menu.c:122
66 | msgid "Games"
67 | msgstr "Gim"
68 |
69 | #: pkgi.c:227 pkgi_menu.c:123
70 | msgid "DLCs"
71 | msgstr "DLC"
72 |
73 | #: pkgi.c:228 pkgi_menu.c:124
74 | msgid "Themes"
75 | msgstr "Tema"
76 |
77 | #: pkgi.c:229 pkgi_menu.c:125
78 | msgid "Avatars"
79 | msgstr "Avatar"
80 |
81 | #: pkgi.c:230 pkgi_menu.c:126
82 | msgid "Demos"
83 | msgstr "Demo"
84 |
85 | #: pkgi.c:231 pkgi_menu.c:118 pkgi_menu.c:127
86 | msgid "Updates"
87 | msgstr "Pembaruan"
88 |
89 | #: pkgi.c:232 pkgi_menu.c:128
90 | msgid "Emulators"
91 | msgstr "Emulator"
92 |
93 | #: pkgi.c:233 pkgi_menu.c:129
94 | msgid "Apps"
95 | msgstr "Aplikasi"
96 |
97 | #: pkgi.c:234 pkgi_menu.c:130
98 | msgid "Tools"
99 | msgstr "Peralatan"
100 |
101 | #: pkgi.c:235
102 | msgid "Unknown"
103 | msgstr "Tidak Dikenal"
104 |
105 | #: pkgi.c:253
106 | msgid "Exit to XMB?"
107 | msgstr "Keluar ke XMB"
108 |
109 | #: pkgi.c:436
110 | msgid "No items!"
111 | msgstr "Tidak ada item!"
112 |
113 | #: pkgi.c:462
114 | msgid "Item already installed, download again?"
115 | msgstr "Item sudah terinstal, unduh kembali?"
116 |
117 | #: pkgi.c:471 pkgi_download.c:372 pkgi_download.c:652
118 | msgid "Downloading..."
119 | msgstr "Mengunduh..."
120 |
121 | #: pkgi.c:471 pkgi.c:674
122 | msgid "Preparing..."
123 | msgstr "Mempersiapkan..."
124 |
125 | #: pkgi.c:504 pkgi.c:508
126 | msgid "Refreshing"
127 | msgstr "Menyegarkan"
128 |
129 | #: pkgi.c:563 pkgi.c:567
130 | msgid "Count"
131 | msgstr "Jumlah"
132 |
133 | #: pkgi.c:575
134 | msgid "Free"
135 | msgstr "Bebas"
136 |
137 | #: pkgi.c:585
138 | msgid "Select"
139 | msgstr "Pilih"
140 |
141 | #: pkgi.c:585
142 | msgid "Close"
143 | msgstr "Tutup"
144 |
145 | #: pkgi.c:585
146 | msgid "Cancel"
147 | msgstr "Batal"
148 |
149 | #: pkgi.c:589 pkgi_download.c:136
150 | msgid "Download"
151 | msgstr "Unduh"
152 |
153 | #: pkgi.c:589
154 | msgid "Menu"
155 | msgstr "Menu"
156 |
157 | #: pkgi.c:589
158 | msgid "Details"
159 | msgstr "Detail"
160 |
161 | #: pkgi.c:589
162 | msgid "Exit"
163 | msgstr "Keluar"
164 |
165 | #: pkgi.c:678
166 | msgid "Successfully downloaded PKGi PSP update"
167 | msgstr "Pembaruan PKGi PSP berhasil terunduh"
168 |
169 | #: pkgi.c:1051
170 | msgid "Search"
171 | msgstr "Cari"
172 |
173 | #: pkgi_db.c:145 pkgi_db.c:153
174 | msgid "failed to download list from"
175 | msgstr "gagal mengunduh daftar dari"
176 |
177 | #: pkgi_db.c:159
178 | msgid "list is too large... check for newer pkgi version!"
179 | msgstr "daftar terlalu besar... periksa versi pkgi terbaru!"
180 |
181 | #: pkgi_db.c:187
182 | msgid "list is empty... check the DB server"
183 | msgstr "daftar kosong... periksa penyedia DB"
184 |
185 | #: pkgi_db.c:390
186 | msgid "ERROR: pkgi.txt file(s) missing or bad config.txt file"
187 | msgstr "EROR: berkas pkgi.txt tidak ada atau berkas config.txt rusak"
188 |
189 | #: pkgi_dialog.c:80
190 | msgid "Content"
191 | msgstr "Konten"
192 |
193 | #: pkgi_dialog.c:108
194 | msgid "ERROR"
195 | msgstr "EROR"
196 |
197 | #: pkgi_dialog.c:169
198 | msgid "Failed to download the update list"
199 | msgstr "Gagal mengunduh daftar pembaruan"
200 |
201 | #: pkgi_dialog.c:174
202 | msgid "update(s) loaded"
203 | msgstr "pembaruan dimuat"
204 |
205 | #: pkgi_dialog.c:319
206 | #, c-format
207 | msgid "press %s to cancel"
208 | msgstr "tekan %s untuk membatalkan"
209 |
210 | #: pkgi_dialog.c:333
211 | #, c-format
212 | msgid "press %s to close - %s to scan updates"
213 | msgstr "tekan %s untuk menutup - %s untuk memindai pembaruan"
214 |
215 | #: pkgi_dialog.c:365
216 | msgid "Enter"
217 | msgstr "Masuk"
218 |
219 | #: pkgi_dialog.c:365
220 | msgid "Back"
221 | msgstr "Kembali"
222 |
223 | #: pkgi_dialog.c:367
224 | #, c-format
225 | msgid "press %s to close"
226 | msgstr "tekan %s untuk menutup"
227 |
228 | #: pkgi_download.c:183
229 | msgid "Install"
230 | msgstr "Instal"
231 |
232 | #: pkgi_download.c:216 pkgi_download.c:220 pkgi_download.c:226
233 | msgid "ETA"
234 | msgstr "Estimasi"
235 |
236 | #: pkgi_download.c:310 pkgi_download.c:392
237 | msgid "Could not send HTTP request"
238 | msgstr "Tidak dapat mengirim permintaan HTTP"
239 |
240 | #: pkgi_download.c:317 pkgi_download.c:399
241 | msgid "HTTP request failed"
242 | msgstr "Permintaan HTTP gagal"
243 |
244 | #: pkgi_download.c:322 pkgi_download.c:404
245 | msgid "HTTP response has unknown length"
246 | msgstr "Panjang respons HTTP tidak diketahui"
247 |
248 | #: pkgi_download.c:331
249 | msgid "Not enough free space on HDD"
250 | msgstr "Sisa ruang HDD tidak cukup"
251 |
252 | #: pkgi_download.c:340
253 | msgid "Could not create task directory on HDD."
254 | msgstr "Tidak dapat membuat direktori tugas di HDD."
255 |
256 | #: pkgi_download.c:348
257 | msgid "Saving background task..."
258 | msgstr "Menyimpan tugas latar belakang..."
259 |
260 | #: pkgi_download.c:354
261 | msgid "Could not create PKG file to HDD."
262 | msgstr "Tidak dapat menciptakan berkas PKG di HDD."
263 |
264 | #: pkgi_download.c:360
265 | msgid "Could not create task files to HDD."
266 | msgstr "Tidak dapat menciptakan berkas tugas di HDD."
267 |
268 | #: pkgi_download.c:425
269 | msgid "HTTP download error"
270 | msgstr "Unduhan HTTP eror"
271 |
272 | #: pkgi_download.c:432
273 | msgid "Koneksi HTTP tertutup"
274 | msgstr "HTTP connection closed"
275 |
276 | #: pkgi_download.c:445
277 | msgid "gagal menulis ke"
278 | msgstr "gagal menulis ke"
279 |
280 | #: pkgi_download.c:465
281 | msgid "cannot creat folder"
282 | msgstr "tidak dapat menciptakan folder"
283 |
284 | #: pkgi_download.c:475
285 | msgid "cannot create file"
286 | msgstr "tidak dapat menciptakan berkas"
287 |
288 | #: pkgi_download.c:490
289 | msgid "cannot resume file"
290 | msgstr "tidak dapat melanjutkan berkas"
291 |
292 | #: pkgi_download.c:562
293 | msgid "pkg integrity failed, try downloading again"
294 | msgstr "integritas pkg gagal, coba unduh kembali"
295 |
296 | #: pkgi_download.c:573
297 | msgid "Creating RAP file"
298 | msgstr "Menciptakan berkas RAP"
299 |
300 | #: pkgi_download.c:581 pkgi_download.c:626
301 | msgid "Cannot save"
302 | msgstr ""
303 |
304 | #: pkgi_download.c:621
305 | msgid "Creating RIF file"
306 | msgstr "Menciptakan berkas RIF"
307 |
308 | #: pkgi_download.c:646
309 | msgid "Resuming..."
310 | msgstr "Melanjutkan"
311 |
312 | #: pkgi_download.c:652
313 | msgid "Adding background task..."
314 | msgstr "Menambahkan tugas latar belakang..."
315 |
316 | #: pkgi_download.c:675
317 | msgid "Downloading icon"
318 | msgstr "Mengunduh ikon"
319 |
320 | #: pkgi_download.c:713
321 | msgid "Could not create install directory on HDD."
322 | msgstr "Tidak dapat menciptakan direktori pemasangan di HDD."
323 |
324 | #: pkgi_menu.c:102
325 | msgid "Search..."
326 | msgstr "Cari..."
327 |
328 | #: pkgi_menu.c:103
329 | msgid "Sort by:"
330 | msgstr "Sortir menurut:"
331 |
332 | #: pkgi_menu.c:104
333 | msgid "Title"
334 | msgstr "Judul"
335 |
336 | #: pkgi_menu.c:105
337 | msgid "Region"
338 | msgstr "Region"
339 |
340 | #: pkgi_menu.c:106
341 | msgid "Name"
342 | msgstr "Nama"
343 |
344 | #: pkgi_menu.c:107
345 | msgid "Size"
346 | msgstr "Ukuran"
347 |
348 | #: pkgi_menu.c:108
349 | msgid "Content:"
350 | msgstr "Konten"
351 |
352 | #: pkgi_menu.c:110
353 | msgid "Regions:"
354 | msgstr "Region:"
355 |
356 | #: pkgi_menu.c:111
357 | msgid "Asia"
358 | msgstr "Asia"
359 |
360 | #: pkgi_menu.c:112
361 | msgid "Europe"
362 | msgstr "Eropa"
363 |
364 | #: pkgi_menu.c:113
365 | msgid "Japan"
366 | msgstr "Jepang"
367 |
368 | #: pkgi_menu.c:114
369 | msgid "USA"
370 | msgstr "USA"
371 |
372 | #: pkgi_menu.c:115
373 | msgid "Options:"
374 | msgstr "Pilihan:"
375 |
376 | #: pkgi_menu.c:116
377 | msgid "ISO"
378 | msgstr ""
379 |
380 | #: pkgi_menu.c:117
381 | msgid "Keep PKGs"
382 | msgstr ""
383 |
384 | #: pkgi_menu.c:119
385 | msgid "Refresh..."
386 | msgstr "Segarkan..."
387 |
388 | #msgid "---translated by---"
389 | #msgstr "Jeremy Belpois (Fajar Maulana)"
--------------------------------------------------------------------------------
/data/LANG/it.po:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/LANG/it.po
--------------------------------------------------------------------------------
/data/LANG/pl.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: PKGi PSP\n"
4 | "POT-Creation-Date: 2021-10-09 17:56-0300\n"
5 | "PO-Revision-Date: 2023-02-05 20:08-0300\n"
6 | "Last-Translator: DEX357\n"
7 | "Language-Team: \n"
8 | "Language: pl\n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=UTF-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "X-Generator: Poedit 3.0\n"
13 | "X-Poedit-Basepath: ./source\n"
14 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
15 | "X-Poedit-Flags-xgettext: --add-comments\n"
16 | "X-Poedit-SourceCharset: UTF-8\n"
17 | "X-Poedit-KeywordsList: _\n"
18 | "X-Poedit-SearchPath-0: .\n"
19 |
20 | #: pkgi.c:82
21 | msgid "Installing..."
22 | msgstr "Instalowanie..."
23 |
24 | #: pkgi.c:82
25 | msgid "Please wait..."
26 | msgstr "Prosze czekac..."
27 |
28 | #: pkgi.c:94
29 | msgid "Installation failed"
30 | msgstr "Instalacja nie powiodla sie"
31 |
32 | #: pkgi.c:118
33 | msgid "Successfully installed"
34 | msgstr "Pobrano pomyslnie"
35 |
36 | #: pkgi.c:122
37 | msgid "Task successfully queued (reboot to start)"
38 | msgstr "Zadanie umieszczone w kolejce. (Zrestartuj, aby rozpoczac)"
39 |
40 | #: pkgi.c:163
41 | msgid "GB"
42 | msgstr "GB"
43 |
44 | #: pkgi.c:167 pkgi_download.c:249
45 | msgid "MB"
46 | msgstr "MB"
47 |
48 | #: pkgi.c:171 pkgi.c:504 pkgi_download.c:253
49 | msgid "KB"
50 | msgstr "KB"
51 |
52 | #: pkgi.c:175
53 | msgid "B"
54 | msgstr "B"
55 |
56 | #: pkgi.c:185
57 | #, c-format
58 | msgid "pkg requires %u %s free space, but only %u %s available"
59 | msgstr "pkg wymaga %u %s wolnego miejsca, ale dostepne jest tylko %u %s"
60 |
61 | #: pkgi.c:225 pkgi_menu.c:109 pkgi_menu.c:121
62 | msgid "All"
63 | msgstr "Wszystko"
64 |
65 | #: pkgi.c:226 pkgi_menu.c:122
66 | msgid "Games"
67 | msgstr "Gry"
68 |
69 | #: pkgi.c:227 pkgi_menu.c:123
70 | msgid "DLCs"
71 | msgstr "DLC"
72 |
73 | #: pkgi.c:228 pkgi_menu.c:124
74 | msgid "Themes"
75 | msgstr "Motywy"
76 |
77 | #: pkgi.c:229 pkgi_menu.c:125
78 | msgid "Avatars"
79 | msgstr "Awatary"
80 |
81 | #: pkgi.c:230 pkgi_menu.c:126
82 | msgid "Demos"
83 | msgstr "Dema"
84 |
85 | #: pkgi.c:231 pkgi_menu.c:118 pkgi_menu.c:127
86 | msgid "Updates"
87 | msgstr "Aktualizacje"
88 |
89 | #: pkgi.c:232 pkgi_menu.c:128
90 | msgid "Emulators"
91 | msgstr "Emulatory"
92 |
93 | #: pkgi.c:233 pkgi_menu.c:129
94 | msgid "Apps"
95 | msgstr "Aplikacje"
96 |
97 | #: pkgi.c:234 pkgi_menu.c:130
98 | msgid "Tools"
99 | msgstr "Narzedzia"
100 |
101 | #: pkgi.c:235
102 | msgid "Unknown"
103 | msgstr "Nieznany"
104 |
105 | #: pkgi.c:253
106 | msgid "Exit to XMB?"
107 | msgstr "Wyjsc do XMB?"
108 |
109 | #: pkgi.c:436
110 | msgid "No items!"
111 | msgstr "Brak danych!"
112 |
113 | #: pkgi.c:462
114 | msgid "Item already installed, download again?"
115 | msgstr "Element juz zainstalowany, pobrac ponownie?"
116 |
117 | #: pkgi.c:471 pkgi_download.c:372 pkgi_download.c:652
118 | msgid "Downloading..."
119 | msgstr "Pobieranie..."
120 |
121 | #: pkgi.c:471 pkgi.c:674
122 | msgid "Preparing..."
123 | msgstr "Przygotowywanie..."
124 |
125 | #: pkgi.c:504 pkgi.c:508
126 | msgid "Refreshing"
127 | msgstr "Odswiezanie"
128 |
129 | #: pkgi.c:563 pkgi.c:567
130 | msgid "Count"
131 | msgstr "Ilosc"
132 |
133 | #: pkgi.c:575
134 | msgid "Free"
135 | msgstr "Wolne"
136 |
137 | #: pkgi.c:585
138 | msgid "Select"
139 | msgstr "Wybierz"
140 |
141 | #: pkgi.c:585
142 | msgid "Close"
143 | msgstr "Zamknij"
144 |
145 | #: pkgi.c:585
146 | msgid "Cancel"
147 | msgstr "Anuluj"
148 |
149 | #: pkgi.c:589 pkgi_download.c:136
150 | msgid "Download"
151 | msgstr "Pobierz"
152 |
153 | #: pkgi.c:589
154 | msgid "Menu"
155 | msgstr "Menu"
156 |
157 | #: pkgi.c:589
158 | msgid "Details"
159 | msgstr "Szczegoly"
160 |
161 | #: pkgi.c:589
162 | msgid "Exit"
163 | msgstr "Wyjdz"
164 |
165 | #: pkgi.c:678
166 | msgid "Successfully downloaded PKGi PSP update"
167 | msgstr "Pomyslnie pobrano aktualizacje PKGi PSP"
168 |
169 | #: pkgi.c:1051
170 | msgid "Search"
171 | msgstr "Szukaj"
172 |
173 | #: pkgi_db.c:145 pkgi_db.c:153
174 | msgid "failed to download list from"
175 | msgstr "nie udalo sie pobrac listy z"
176 |
177 | #: pkgi_db.c:159
178 | msgid "list is too large... check for newer pkgi version!"
179 | msgstr "lista jest za duza... sprawdz nowsza wersje pkgi!"
180 |
181 | #: pkgi_db.c:187
182 | msgid "list is empty... check the DB server"
183 | msgstr "lista jest pusta... sprawdz serwer DB"
184 |
185 | #: pkgi_db.c:390
186 | msgid "ERROR: pkgi.txt file(s) missing or bad config.txt file"
187 | msgstr "BLAD: brak pliku(ow) pkgi.txt lub zly plik config.txt"
188 |
189 | #: pkgi_dialog.c:80
190 | msgid "Content"
191 | msgstr "Zawartosc"
192 |
193 | #: pkgi_dialog.c:108
194 | msgid "ERROR"
195 | msgstr "BLAD"
196 |
197 | #: pkgi_dialog.c:169
198 | msgid "Failed to download the update list"
199 | msgstr "Nie udalo sie pobrac listy aktualizacji"
200 |
201 | #: pkgi_dialog.c:174
202 | msgid "update(s) loaded"
203 | msgstr "zaladowano aktualizacje"
204 |
205 | #: pkgi_dialog.c:319
206 | #, c-format
207 | msgid "press %s to cancel"
208 | msgstr "nacisnij %s aby anulowac"
209 |
210 | #: pkgi_dialog.c:333
211 | #, c-format
212 | msgid "press %s to close - %s to scan updates"
213 | msgstr "nacisnij %s, aby zamknac - %s aby przeskanowac aktualizacje"
214 |
215 | #: pkgi_dialog.c:365
216 | msgid "Enter"
217 | msgstr "Wejdz"
218 |
219 | #: pkgi_dialog.c:365
220 | msgid "Back"
221 | msgstr "Cofnij"
222 |
223 | #: pkgi_dialog.c:367
224 | #, c-format
225 | msgid "press %s to close"
226 | msgstr "nacisnij %s aby zamknac"
227 |
228 | #. 00000069 - Display title
229 | #: pkgi_download.c:183
230 | msgid "Install"
231 | msgstr "Zainstaluj"
232 |
233 | #: pkgi_download.c:216 pkgi_download.c:220 pkgi_download.c:226
234 | msgid "ETA"
235 | msgstr "ETA"
236 |
237 | #: pkgi_download.c:310 pkgi_download.c:392
238 | msgid "Could not send HTTP request"
239 | msgstr "Nie mozna wyslac zadania HTTP"
240 |
241 | #: pkgi_download.c:317 pkgi_download.c:399
242 | msgid "HTTP request failed"
243 | msgstr "Zadanie HTTP nie powiodlo sie"
244 |
245 | #: pkgi_download.c:322 pkgi_download.c:404
246 | msgid "HTTP response has unknown length"
247 | msgstr "Odpowiedz HTTP ma nieznana dlugosc"
248 |
249 | #: pkgi_download.c:331
250 | msgid "Not enough free space on HDD"
251 | msgstr "Nie wystarczajaca ilosc miejsca na dysku twardym"
252 |
253 | #: pkgi_download.c:340
254 | msgid "Could not create task directory on HDD."
255 | msgstr "Nie mozna utworzyc katalogu zadan na dysku twardym."
256 |
257 | #: pkgi_download.c:348
258 | msgid "Saving background task..."
259 | msgstr "Zapisywanie w tle..."
260 |
261 | #: pkgi_download.c:354
262 | msgid "Could not create PKG file to HDD."
263 | msgstr "Nie mozna utworzyc pliku PKG na HDD."
264 |
265 | #: pkgi_download.c:360
266 | msgid "Could not create task files to HDD."
267 | msgstr "Nie mozna utworzyc plikow zadan na HDD."
268 |
269 | #: pkgi_download.c:425
270 | msgid "HTTP download error"
271 | msgstr "Blad pobierania HTTP"
272 |
273 | #: pkgi_download.c:432
274 | msgid "HTTP connection closed"
275 | msgstr "Polaczenie HTTP zamkniete"
276 |
277 | #: pkgi_download.c:445
278 | msgid "failed to write to"
279 | msgstr "zapisywanie nie powiodlo sie"
280 |
281 | #: pkgi_download.c:465
282 | msgid "cannot create folder"
283 | msgstr "nie mozna utworzyc folderu"
284 |
285 | #: pkgi_download.c:475
286 | msgid "cannot create file"
287 | msgstr "nie mozna stworzyc pliku"
288 |
289 | #: pkgi_download.c:490
290 | msgid "cannot resume file"
291 | msgstr "nie mozna wznowic pliku"
292 |
293 | #: pkgi_download.c:562
294 | msgid "pkg integrity failed, try downloading again"
295 | msgstr "Integralnosc pakietu nie powiodla sie, sprobuj pobrac ponownie"
296 |
297 | #: pkgi_download.c:573
298 | msgid "Creating RAP file"
299 | msgstr "Tworzenie pliku RAP"
300 |
301 | #: pkgi_download.c:581 pkgi_download.c:626
302 | msgid "Cannot save"
303 | msgstr "Nie mozna zapisac"
304 |
305 | #: pkgi_download.c:621
306 | msgid "Creating RIF file"
307 | msgstr "Tworzenie pliku RIF"
308 |
309 | #: pkgi_download.c:646
310 | msgid "Resuming..."
311 | msgstr "Wznawianie..."
312 |
313 | #: pkgi_download.c:652
314 | msgid "Adding background task..."
315 | msgstr "Dodawanie zadania w tle..."
316 |
317 | #: pkgi_download.c:675
318 | msgid "Downloading icon"
319 | msgstr "Pobieranie ikon"
320 |
321 | #: pkgi_download.c:713
322 | msgid "Could not create install directory on HDD."
323 | msgstr "Nie mozna utworzyc katalogu instalacyjnego na dysku twardym."
324 |
325 | #: pkgi_menu.c:102
326 | msgid "Search..."
327 | msgstr "Szukaj..."
328 |
329 | #: pkgi_menu.c:103
330 | msgid "Sort by:"
331 | msgstr "Sortuj wedlug:"
332 |
333 | #: pkgi_menu.c:104
334 | msgid "Title"
335 | msgstr "Tytul"
336 |
337 | #: pkgi_menu.c:105
338 | msgid "Region"
339 | msgstr "Region"
340 |
341 | #: pkgi_menu.c:106
342 | msgid "Name"
343 | msgstr "Nazwa"
344 |
345 | #: pkgi_menu.c:107
346 | msgid "Size"
347 | msgstr "Rozmiar"
348 |
349 | #: pkgi_menu.c:108
350 | msgid "Content:"
351 | msgstr "Zawartosc:"
352 |
353 | #: pkgi_menu.c:110
354 | msgid "Regions:"
355 | msgstr "Regiony:"
356 |
357 | #: pkgi_menu.c:111
358 | msgid "Asia"
359 | msgstr "Azja"
360 |
361 | #: pkgi_menu.c:112
362 | msgid "Europe"
363 | msgstr "Europa"
364 |
365 | #: pkgi_menu.c:113
366 | msgid "Japan"
367 | msgstr "Japonia"
368 |
369 | #: pkgi_menu.c:114
370 | msgid "USA"
371 | msgstr "USA"
372 |
373 | #: pkgi_menu.c:115
374 | msgid "Options:"
375 | msgstr "Opcje:"
376 |
377 | #: pkgi_menu.c:116
378 | msgid "ISO"
379 | msgstr ""
380 |
381 | #: pkgi_menu.c:117
382 | msgid "Keep PKGs"
383 | msgstr ""
384 |
385 | #: pkgi_menu.c:119
386 | msgid "Refresh..."
387 | msgstr "Odswiez..."
388 |
389 | #msgid "---translated by---"
390 | #msgstr "DEX357, PoliPyc, olokos"
391 |
--------------------------------------------------------------------------------
/data/LANG/pt.po:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/LANG/pt.po
--------------------------------------------------------------------------------
/data/LANG/tr.po:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/LANG/tr.po
--------------------------------------------------------------------------------
/data/LANG/zh.txt:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: PKGi PSP\n"
4 | "POT-Creation-Date: 2021-10-09 19:00-0300\n"
5 | "PO-Revision-Date: 2021-10-09 19:00-0300\n"
6 | "Last-Translator: Croden1999\n"
7 | "Language-Team: \n"
8 | "Language: zh\n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=UTF-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "X-Generator: Poedit 3.0\n"
13 | "X-Poedit-Basepath: ./source\n"
14 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
15 | "X-Poedit-Flags-xgettext: --add-comments\n"
16 | "X-Poedit-SourceCharset: UTF-8\n"
17 | "X-Poedit-KeywordsList: _\n"
18 | "X-Poedit-SearchPath-0: .\n"
19 |
20 | #: pkgi.c:82
21 | msgid "Installing..."
22 | msgstr "安装中"
23 |
24 | #: pkgi.c:82
25 | msgid "Please wait..."
26 | msgstr "请稍等..."
27 |
28 | #: pkgi.c:94
29 | msgid "Installation failed"
30 | msgstr "安装失败"
31 |
32 | #: pkgi.c:118
33 | msgid "Successfully installed"
34 | msgstr "下载完成"
35 |
36 | #: pkgi.c:122
37 | msgid "Task successfully queued (reboot to start)"
38 | msgstr "任务已成功排队(重启开始)"
39 |
40 | #: pkgi.c:163
41 | msgid "GB"
42 | msgstr ""
43 |
44 | #: pkgi.c:167 pkgi_download.c:249
45 | msgid "MB"
46 | msgstr ""
47 |
48 | #: pkgi.c:171 pkgi.c:504 pkgi_download.c:253
49 | msgid "KB"
50 | msgstr ""
51 |
52 | #: pkgi.c:175
53 | msgid "B"
54 | msgstr ""
55 |
56 | #: pkgi.c:185
57 | #, c-format
58 | msgid "pkg requires %u %s free space, but only %u %s available"
59 | msgstr "pkg需要%u %s剩余空间, 但只有%u %s可用"
60 |
61 | #: pkgi.c:225 pkgi_menu.c:109 pkgi_menu.c:121
62 | msgid "All"
63 | msgstr "全部"
64 |
65 | #: pkgi.c:226 pkgi_menu.c:122
66 | msgid "Games"
67 | msgstr "游戏"
68 |
69 | #: pkgi.c:227 pkgi_menu.c:123
70 | msgid "DLCs"
71 | msgstr "追加内容"
72 |
73 | #: pkgi.c:228 pkgi_menu.c:124
74 | msgid "Themes"
75 | msgstr "主题"
76 |
77 | #: pkgi.c:229 pkgi_menu.c:125
78 | msgid "Avatars"
79 | msgstr "头像"
80 |
81 | #: pkgi.c:230 pkgi_menu.c:126
82 | msgid "Demos"
83 | msgstr "试玩"
84 |
85 | #: pkgi.c:231 pkgi_menu.c:118 pkgi_menu.c:127
86 | msgid "Updates"
87 | msgstr "更新"
88 |
89 | #: pkgi.c:232 pkgi_menu.c:128
90 | msgid "Emulators"
91 | msgstr "模拟器"
92 |
93 | #: pkgi.c:233 pkgi_menu.c:129
94 | msgid "Apps"
95 | msgstr "应用程序"
96 |
97 | #: pkgi.c:234 pkgi_menu.c:130
98 | msgid "Tools"
99 | msgstr "工具"
100 |
101 | #: pkgi.c:235
102 | msgid "Unknown"
103 | msgstr "未知"
104 |
105 | #: pkgi.c:253
106 | msgid "Exit to XMB?"
107 | msgstr "退出到XMB吗?"
108 |
109 | #: pkgi.c:436
110 | msgid "No items!"
111 | msgstr "没有内容!"
112 |
113 | #: pkgi.c:462
114 | msgid "Item already installed, download again?"
115 | msgstr "内容已安装,是否重新下载?"
116 |
117 | #: pkgi.c:471 pkgi_download.c:372 pkgi_download.c:652
118 | msgid "Downloading..."
119 | msgstr "下载中..."
120 |
121 | #: pkgi.c:471 pkgi.c:674
122 | msgid "Preparing..."
123 | msgstr "准备中..."
124 |
125 | #: pkgi.c:504 pkgi.c:508
126 | msgid "Refreshing"
127 | msgstr "刷新中"
128 |
129 | #: pkgi.c:563 pkgi.c:567
130 | msgid "Count"
131 | msgstr "总数"
132 |
133 | #: pkgi.c:575
134 | msgid "Free"
135 | msgstr "剩余"
136 |
137 | #: pkgi.c:585
138 | msgid "Select"
139 | msgstr "选择"
140 |
141 | #: pkgi.c:585
142 | msgid "Close"
143 | msgstr "关闭"
144 |
145 | #: pkgi.c:585
146 | msgid "Cancel"
147 | msgstr "取消"
148 |
149 | #: pkgi.c:589 pkgi_download.c:136
150 | msgid "Download"
151 | msgstr "下载"
152 |
153 | #: pkgi.c:589
154 | msgid "Menu"
155 | msgstr "菜单"
156 |
157 | #: pkgi.c:589
158 | msgid "Details"
159 | msgstr "详细"
160 |
161 | #: pkgi.c:589
162 | msgid "Exit"
163 | msgstr "退出"
164 |
165 | #: pkgi.c:678
166 | msgid "Successfully downloaded PKGi PSP update"
167 | msgstr "下载PKGi PSP更新成功"
168 |
169 | #: pkgi.c:1051
170 | msgid "Search"
171 | msgstr "搜索"
172 |
173 | #: pkgi_db.c:145 pkgi_db.c:153
174 | msgid "failed to download list from"
175 | msgstr "下载列表失败"
176 |
177 | #: pkgi_db.c:159
178 | msgid "list is too large... check for newer pkgi version!"
179 | msgstr "列表过大...检查是不是新的pkgi版本!"
180 |
181 | #: pkgi_db.c:187
182 | msgid "list is empty... check the DB server"
183 | msgstr "列表为空...检查数据库服务器"
184 |
185 | #: pkgi_db.c:390
186 | msgid "ERROR: pkgi.txt file(s) missing or bad config.txt file"
187 | msgstr "错误:pkgi.txt文件丢失或配置文件错误"
188 |
189 | #: pkgi_dialog.c:80
190 | msgid "Content"
191 | msgstr "内容"
192 |
193 | #: pkgi_dialog.c:108
194 | msgid "ERROR"
195 | msgstr "错误"
196 |
197 | #: pkgi_dialog.c:169
198 | msgid "Failed to download the update list"
199 | msgstr "下载更新列表失败"
200 |
201 | #: pkgi_dialog.c:174
202 | msgid "update(s) loaded"
203 | msgstr "更新加载"
204 |
205 | #: pkgi_dialog.c:319
206 | #, c-format
207 | msgid "press %s to cancel"
208 | msgstr "按%s取消"
209 |
210 | #: pkgi_dialog.c:333
211 | #, c-format
212 | msgid "press %s to close - %s to scan updates"
213 | msgstr "按%s关闭 - %s扫描更新"
214 |
215 | #: pkgi_dialog.c:365
216 | msgid "Enter"
217 | msgstr "确认"
218 |
219 | #: pkgi_dialog.c:365
220 | msgid "Back"
221 | msgstr "返回"
222 |
223 | #: pkgi_dialog.c:367
224 | #, c-format
225 | msgid "press %s to close"
226 | msgstr "按%s关闭"
227 |
228 | #: pkgi_download.c:183
229 | msgid "Install"
230 | msgstr "安装"
231 |
232 | #: pkgi_download.c:216 pkgi_download.c:220 pkgi_download.c:226
233 | msgid "ETA"
234 | msgstr ""
235 |
236 | #: pkgi_download.c:310 pkgi_download.c:392
237 | msgid "Could not send HTTP request"
238 | msgstr "无法发送HTTP请求"
239 |
240 | #: pkgi_download.c:317 pkgi_download.c:399
241 | msgid "HTTP request failed"
242 | msgstr "HTTP请求失败"
243 |
244 | #: pkgi_download.c:322 pkgi_download.c:404
245 | msgid "HTTP response has unknown length"
246 | msgstr "未知HTTP响应长度"
247 |
248 | #: pkgi_download.c:331
249 | msgid "Not enough free space on HDD"
250 | msgstr "硬盘空间不足"
251 |
252 | #: pkgi_download.c:340
253 | msgid "Could not create task directory on HDD."
254 | msgstr "无法在硬盘创建任务目录。"
255 |
256 | #: pkgi_download.c:348
257 | msgid "Saving background task..."
258 | msgstr "保存后台任务中..."
259 |
260 | #: pkgi_download.c:354
261 | msgid "Could not create PKG file to HDD."
262 | msgstr "无法创建PKG文件到硬盘。"
263 |
264 | #: pkgi_download.c:360
265 | msgid "Could not create task files to HDD."
266 | msgstr "无法创建任务文件到硬盘。"
267 |
268 | #: pkgi_download.c:425
269 | msgid "HTTP download error"
270 | msgstr "HTTP下载错误"
271 |
272 | #: pkgi_download.c:432
273 | msgid "HTTP connection closed"
274 | msgstr "HTTP连接关闭"
275 |
276 | #: pkgi_download.c:445
277 | msgid "failed to write to"
278 | msgstr "写入失败"
279 |
280 | #: pkgi_download.c:465
281 | msgid "cannot create folder"
282 | msgstr "无法创建文件夹"
283 |
284 | #: pkgi_download.c:475
285 | msgid "cannot create file"
286 | msgstr "无法创建文件"
287 |
288 | #: pkgi_download.c:490
289 | msgid "cannot resume file"
290 | msgstr "无法恢复文件"
291 |
292 | #: pkgi_download.c:562
293 | msgid "pkg integrity failed, try downloading again"
294 | msgstr "pkg完整性失败,请尝试重新下载"
295 |
296 | #: pkgi_download.c:573
297 | msgid "Creating RAP file"
298 | msgstr "创建RAP文件中"
299 |
300 | #: pkgi_download.c:581 pkgi_download.c:626
301 | msgid "Cannot save"
302 | msgstr "无法保存"
303 |
304 | #: pkgi_download.c:621
305 | msgid "Creating RIF file"
306 | msgstr "创建RIF文件中"
307 |
308 | #: pkgi_download.c:646
309 | msgid "Resuming..."
310 | msgstr "恢复中..."
311 |
312 | #: pkgi_download.c:652
313 | msgid "Adding background task..."
314 | msgstr "添加后台任务中..."
315 |
316 | #: pkgi_download.c:675
317 | msgid "Downloading icon"
318 | msgstr "下载图标中"
319 |
320 | #: pkgi_download.c:713
321 | msgid "Could not create install directory on HDD."
322 | msgstr "无法在硬盘创建安装目录。"
323 |
324 | #: pkgi_menu.c:102
325 | msgid "Search..."
326 | msgstr "搜索..."
327 |
328 | #: pkgi_menu.c:103
329 | msgid "Sort by:"
330 | msgstr "排序方式:"
331 |
332 | #: pkgi_menu.c:104
333 | msgid "Title"
334 | msgstr "标题"
335 |
336 | #: pkgi_menu.c:105
337 | msgid "Region"
338 | msgstr "区域"
339 |
340 | #: pkgi_menu.c:106
341 | msgid "Name"
342 | msgstr "名称"
343 |
344 | #: pkgi_menu.c:107
345 | msgid "Size"
346 | msgstr "大小"
347 |
348 | #: pkgi_menu.c:108
349 | msgid "Content:"
350 | msgstr "内容:"
351 |
352 | #: pkgi_menu.c:110
353 | msgid "Regions:"
354 | msgstr "区域:"
355 |
356 | #: pkgi_menu.c:111
357 | msgid "Asia"
358 | msgstr "亚洲"
359 |
360 | #: pkgi_menu.c:112
361 | msgid "Europe"
362 | msgstr "欧洲"
363 |
364 | #: pkgi_menu.c:113
365 | msgid "Japan"
366 | msgstr "日本"
367 |
368 | #: pkgi_menu.c:114
369 | msgid "USA"
370 | msgstr "美国"
371 |
372 | #: pkgi_menu.c:115
373 | msgid "Options:"
374 | msgstr "选项:"
375 |
376 | #: pkgi_menu.c:116
377 | msgid "ISO"
378 | msgstr "后台下载"
379 |
380 | #: pkgi_menu.c:117
381 | msgid "Keep PKGs"
382 | msgstr "音乐"
383 |
384 | #: pkgi_menu.c:119
385 | msgid "Refresh..."
386 | msgstr "刷新中..."
387 |
388 | #: pkgi_menu.c:326
389 | msgid "Direct DL"
390 | msgstr "直接下载"
391 |
--------------------------------------------------------------------------------
/data/SQUARE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/SQUARE.png
--------------------------------------------------------------------------------
/data/TRIANGLE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/TRIANGLE.png
--------------------------------------------------------------------------------
/data/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/background.png
--------------------------------------------------------------------------------
/data/fonts/NotoSansJP-Medium.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/data/fonts/NotoSansJP-Medium.otf
--------------------------------------------------------------------------------
/data/fonts/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright 2012 Google Inc. All Rights Reserved.
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/data/pkgi.txt:
--------------------------------------------------------------------------------
1 | U00001-NP0APOLLO_00-0000000000000000,8,Apollo Save Tool v1.3.0,Save-game manager,,https://github.com/bucanero/apollo-psp/releases/download/v1.3.0/apollo-psp.zip,6885196,DFF88635A044F319686231ACABB548CC42EA1CF3A992856572C7AF20F70D30ED
2 | U00001-CMFILEMGR_00-0000000000000000,8,CM File Manager v4.10,File manager,,https://github.com/joel16/CMFileManager-PSP/releases/download/v4.10/CMFileManager-PSP.zip,1815998,
3 | U00001-PSPTOOL10_00-0000000000000000,8,PSP Tool v1.00,PSP Tool,,https://archive.org/download/psp-tool.-7z/PSP%20Tool.zip,5023819,
4 | U00001-PSPFILER6_00-0000000000000000,8,PSP Filer v6.6,File manager,,https://wololo.net/download.php?f=filer6.6.zip,1295106,
5 | U00001-NZPORTABL_00-0000000000000000,1,Nazi Zombies: Portable,Nightly 64 Mb,,https://github.com/nzp-team/nzportable/releases/download/nightly/nzportable-psp-64mb.zip,36163686,
6 | U00001-WOLFENS3D_00-0000000000000000,1,Wolfenstein 3D,v6.0 shareware,,https://archive.org/download/wolf-3-d-v-6.0.7z/Wolf3D_V6.0.zip,8043837,
7 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # PKGi PSP
2 |
3 | [![Downloads][img_downloads]][pkgi_downloads] [![Release][img_latest]][pkgi_latest] [![License][img_license]][pkgi_license]
4 | [](https://github.com/bucanero/pkgi-psp/actions/workflows/build.yml)
5 | [](https://twitter.com/dparrino)
6 |
7 | **PKGi PSP** is a PlayStation Portable port of [pkgi-ps3](https://github.com/bucanero/pkgi-ps3).
8 |
9 | This homebrew app allows to download and install `.pkg` files directly on your PSP.
10 |
11 | 
12 |
13 | **Comments, ideas, suggestions?** You can [contact me](https://github.com/bucanero/) on [Twitter](https://twitter.com/dparrino) and on [my website](http://www.bucanero.com.ar/).
14 |
15 | # Features
16 |
17 | * **easy to use:** list available downloads, including searching, filtering, and sorting.
18 | * **standalone:** no PC required, everything happens directly on the PSP.
19 | * **automatic downloads:** just choose an item, and it will be downloaded by the app to your Memory Stick (`ms0:/PKG/`).
20 | * **resumes interrupted downloads:** you can stop a download at any time, switch applications, and come back to resume the download later.
21 | * **localization support:** Finnish, French, German, Indonesian, Italian, Polish, Portuguese, Spanish, Turkish
22 |
23 | ### Notes:
24 | * **content activation:** downloaded content requires a valid license to run. If your PSP hasn't been activated, you can use one of the following plugins:
25 | - [npdrm_free plugin](https://github.com/qwikrazor87/npdrm_free) (PSP/PS1/PCEngine games and DLCs)
26 | - [npdrm_free_mod plugin](https://github.com/lusid1/npdrm_free_mod) (PSP/PS1 games only)
27 | - [nploader_mod](https://github.com/lusid1/nploader_mod) (PSP DLCs only)
28 |
29 | # Download
30 |
31 | Get the [latest version here][pkgi_latest].
32 |
33 | ### Changelog
34 |
35 | See the [latest changes here](CHANGELOG.md).
36 |
37 | # Setup instructions
38 |
39 | You need to create a [`pkgi.txt`](#sample-db-file) file in `ms0:/PSP/GAME/PKGI/` that contains the items available for installation.
40 | The text database format is user customizable. Check [this section](#user-defined-db-format) to learn how to define your own custom DB format.
41 |
42 | ## Multiple databases
43 |
44 | You can also load additional database files:
45 |
46 | - `pkgi_games.txt`
47 | - `pkgi_dlcs.txt`
48 | - `pkgi_themes.txt`
49 | - `pkgi_psx.txt`
50 | - `pkgi_demos.txt`
51 | - `pkgi_updates.txt`
52 | - `pkgi_emulators.txt`
53 | - `pkgi_apps.txt`
54 |
55 | Items on each of these files will be auto-categorized to the file content type. **Note:** The app assumes that every database file has the same format, as defined in `dbformat.txt`.
56 |
57 | ## Online DB update
58 |
59 | You can refresh and sync an online database by adding the DB URL(s) to the `config.txt` file in `ms0:/PSP/GAME/PKGI/`.
60 |
61 | For example:
62 |
63 | ```
64 | url_games http://www.mysite.com/mylist.csv
65 | url_demos http://www.demos.com/otherlist.csv
66 | url_emulators http://www.example.com/emulators.csv
67 | ```
68 |
69 | Using this setup,
70 | - `pkgi_games.txt` will be updated with `http://www.mysite.com/mylist.csv`,
71 | - `pkgi_demos.txt` with `http://www.demos.com/otherlist.csv`,
72 | - and `pkgi_emulators.txt` with `http://www.example.com/emulators.csv`.
73 |
74 | Next time you open the app, you'll have an additional menu option  called **Refresh**. When you select it, the local databases will be syncronized with the defined URLs.
75 |
76 | # DB formats
77 |
78 | The application needs a text database that contains the items available for installation, and it must follow the [default format definition](#default-db-format), or have a [custom format definition](#user-defined-db-format) file.
79 |
80 | ## Default DB format
81 |
82 | The default database file format uses a very simple CSV format where each line means one item in the list:
83 |
84 | ```
85 | contentid,type,name,description,rap,url,size,checksum
86 | ```
87 |
88 | where:
89 |
90 | | Column | Description |
91 | |--------|-------------|
92 | | `contentid` | is the full content id of the item, for example: `UP0000-NPXX99999_00-0000112223333000`.
93 | | `type` | is a number for the item's content type. See the [table below](#content-types) for details. (set it to 0 if unknown)
94 | | `name` | is a string for the item's name.
95 | | `description` | is a string for the item's description.
96 | | `rap` | the 16 hex bytes for a RAP file, if needed by the item (`.rap` files will be created on `ms0:/PKG/RAP`). Leave empty to skip the `.rap` file.
97 | | `url` | is the HTTP/HTTPS/FTP/FTPS URL where to download the `.pkg` file.
98 | | `size` | is the size in bytes of the `.pkg` file, or 0 if unknown.
99 | | `checksum` | is a SHA256 digest of the `.pkg` file (as 32 hex bytes) to make sure the file is not tampered with. Leave empty to skip the check.
100 |
101 | **Note:** `name` and `description` cannot contain newlines or commas.
102 |
103 | ### Sample DB file
104 |
105 | An example `pkgi.txt` file following the `contentid,type,name,description,rap,url,size,checksum` format:
106 |
107 | ```
108 | U00001-NP0APOLLO_00-0000000000000000,8,Apollo Save Tool v1.3.0,Save-game manager,,https://github.com/bucanero/apollo-psp/releases/download/v1.3.0/apollo-psp.zip,6885196,DFF88635A044F319686231ACABB548CC42EA1CF3A992856572C7AF20F70D30ED
109 | U00001-CMFILEMGR_00-0000000000000000,8,CM File Manager v4.10,File manager,,https://github.com/joel16/CMFileManager-PSP/releases/download/v4.10/CMFileManager-PSP.zip,1815998,7A5FD10184546AB993A4D5F3054BCBA9E9B7A1C569EE26E366F1F5CC9DA5A554
110 | UP0001-PSPTOOL10_00-0000000000000000,8,PSP Tool v1.00,PSP Tool,,https://archive.org/download/psp-tool.-7z/PSP%20Tool.zip,5023819,
111 | UP0001-PSPFILER6_00-0000000000000000,8,PSP Filer v6.6,File manager,,https://wololo.net/download.php?f=filer6.6.zip,1295106,
112 | UP0001-NZPORTABL_00-0000000000000000,1,Nazi Zombies: Portable,Nightly 64 Mb,,https://github.com/nzp-team/nzportable/releases/download/nightly/nzportable-psp-64mb.zip,36163686,
113 | UP0001-IRISMAN00_00-VER4880000000000,8,IRISMAN 4.88.1,Backup Manager,,http://github.com/aldostools/IRISMAN/releases/download/4.88/IRISMAN_4.88.pkg,29411984,E6EF607F0002B31BFB148BE4FC9BDBACB4E53110751F0E667C701D40B5290570
114 | ```
115 |
116 | ### Content types
117 |
118 | | Type value | Content type | DB File |
119 | |------------|--------------|---------|
120 | | 0 | Unknown |
121 | | 1 | Game | `pkgi_games.txt`
122 | | 2 | DLC | `pkgi_dlcs.txt`
123 | | 3 | Theme | `pkgi_themes.txt`
124 | | 4 | PSX | `pkgi_psx.txt`
125 | | 5 | Demo | `pkgi_demos.txt`
126 | | 6 | Update | `pkgi_updates.txt`
127 | | 7 | Emulator | `pkgi_emulators.txt`
128 | | 8 | Application | `pkgi_apps.txt`
129 |
130 | ## User-defined DB format
131 |
132 | To use a custom database format, you need to create a `dbformat.txt` file, and save it on `ms0:/PSP/GAME/PKGI/`.
133 |
134 | The `dbformat.txt` definition file is a 2-line text file:
135 | * Line 1: the custom delimiter character (e.g.: `;`, `,`, `|`, etc.)
136 | * Line 2: the column names for every column in the custom database, delimited by the proper delimiter defined in line 1
137 |
138 | **Note:** For the columns to be properly recognized, use the column tag names defined in the table above.
139 |
140 | All the columns are optional. Your database might have more (or less) columns, so any unrecognized column will be skipped.
141 |
142 | ### Example
143 |
144 | Example `dbformat.txt`, for a database using semi-colon (`;`) as separator:
145 |
146 | ```
147 | ;
148 | name;TITLE ID;REGION;description;AUTHOR;TYPE;url;rap;size
149 | ```
150 |
151 | **Result:** only the `name,description,url,rap,size` fields will be used.
152 |
153 | ### Example
154 |
155 | Example `dbformat.txt`, for a database using character pipe (`|`) as separator:
156 |
157 | ```
158 | |
159 | REGION|TITLE|name|url|rap|contentid|DATE|PKG FILENAME|size|checksum
160 | ```
161 |
162 | **Result:** only the `name,url,rap,contentid,size,checksum` fields will be used.
163 |
164 | # Usage
165 |
166 | Using the application is simple and straight-forward:
167 |
168 | - Move UP/DOWN to select the item you want to download, and press .
169 | - To see the item's details, press .
170 | - To sort/filter/search press .
171 | It will open the context menu. Press  again to confirm the new settings, or press  to cancel any changes.
172 | - Press left or right trigger buttons L1/R1 to move pages up or down.
173 | - Press LEFT/RIGHT buttons to switch between categories.
174 |
175 | ### Notes
176 |
177 | - **RAP data:** if the item has `.rap` data, the file will be saved in the `ms0:/PKG/RAP/` folder.
178 |
179 |
180 | # Q&A
181 |
182 | 1. Where to get a `rap` string?
183 |
184 | You can use a tool like RIF2RAP to generate a `.rap` from your existing `.rif` files. Then you can use a tool like `hexdump` to get the hex byte string.
185 |
186 | 2. Where to get `.pkg` links?
187 |
188 | You can use [PSDLE][] to find `.pkg` URLs for the games you own. Then either use the original URL, or host the file on your own web server.
189 |
190 | 3. Where to remove interrupted/failed downloads to free up disk space?
191 |
192 | Check the `ms0:/PKG/` folder - each download will be in a separate `.pkg` file by its content ID. Simply delete the file and start again.
193 |
194 | 4. Download speed is too slow!
195 |
196 | Optimization is still pending. (Optional) Set `Power Save Settings` -> `WLAN Power save` -> `OFF` , if you want to speed up the download process.
197 |
198 | # Credits
199 |
200 | * [Bucanero](http://www.bucanero.com.ar/): Project developer
201 |
202 | ## Acknowledgements
203 |
204 | * [mmozeiko](https://github.com/mmozeiko/): [pkgi](https://github.com/mmozeiko/pkgi) (PS Vita), [pkg2zip](https://github.com/mmozeiko/pkg2zip)
205 | * [qwikrazor87](https://github.com/qwikrazor87/): [Depackager](https://github.com/bucanero/psptools/tree/master/depackager)
206 |
207 | # Building
208 |
209 | You need to have installed:
210 |
211 | - [PSP SDK](https://github.com/pspdev/)
212 | - [mbedTLS](https://github.com/pspdev/psp-packages/tree/master/mbedtls) library
213 | - [cURL](https://github.com/pspdev/psp-packages/tree/master/curl) library
214 | - [Mini18n](https://github.com/bucanero/mini18n) library
215 | - [dbglogger](https://github.com/bucanero/dbglogger) library (only required for debug logging)
216 |
217 | Run `cmake . && make` to create a release build. If you want to create a `.zip` file, run `make createzip`.
218 |
219 | ## Debugging
220 |
221 | To enable debug logging, pass `-DPKGI_ENABLE_DEBUG=ON` argument to `cmake`. The application will write debug messages to
222 |
223 | ms0:/pkgi-psp.log
224 |
225 | You can also set the `PSPIP` environment variable to your PSP's IP address, and use `make send` to upload `EBOOT.PBP` directly to the `ms0:/PSP/GAME/PKGI` folder.
226 |
227 | # License
228 |
229 | `pkgi-psp` is released under the [MIT License](LICENSE).
230 |
231 | [PSDLE]: https://repod.github.io/psdle/
232 | [socat]: http://www.dest-unreach.org/socat/
233 | [pkgi_downloads]: https://github.com/bucanero/pkgi-psp/releases
234 | [pkgi_latest]: https://github.com/bucanero/pkgi-psp/releases/latest
235 | [pkgi_license]: https://github.com/bucanero/pkgi-psp/blob/main/LICENSE
236 | [img_downloads]: https://img.shields.io/github/downloads/bucanero/pkgi-psp/total.svg?maxAge=3600
237 | [img_latest]: https://img.shields.io/github/release/bucanero/pkgi-psp.svg?maxAge=3600
238 | [img_license]: https://img.shields.io/github/license/bucanero/pkgi-psp.svg?maxAge=2592000
239 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-slate
2 | show_downloads: true
3 |
--------------------------------------------------------------------------------
/docs/leon-luna.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/docs/leon-luna.jpg
--------------------------------------------------------------------------------
/docs/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bucanero/pkgi-psp/9f440ce6af0d0f9fe97eed1c0b6220399cb06792/docs/screenshot.png
--------------------------------------------------------------------------------
/include/libfont.h:
--------------------------------------------------------------------------------
1 | /*
2 | TINY3D - font library / (c) 2010 Hermes
3 |
4 | */
5 |
6 | #ifndef LIBFONT_H
7 | #define LIBFONT_H
8 |
9 | //#include "menu.h"
10 | void DrawTexture(pkgi_texture tex, int x, int y, int z, int w, int h, uint32_t rgba);
11 |
12 | /* NOTE: LIBFONT is thinkin to work with Tiny3D 2D mode: you need to call tiny3d_Project2D() before to work with draw functions */
13 |
14 | // max number of special characters
15 |
16 | #define MAX_SPECIAL_CHARS 0x20
17 |
18 | // initialize all datas. After you call it you don't have any font to use
19 |
20 | void ResetFont();
21 |
22 | // used as byte_order in AddFontFromBitmapArray()
23 |
24 | #define BIT0_FIRST_PIXEL 0
25 | #define BIT7_FIRST_PIXEL 1
26 |
27 | /* add one font from one bitmap array. You can define the font range with first_char and last_char (in my sample i use two fonts that starts
28 | at chr 32 and finish iat chr 255 and other font starts at 0 and finish at 254. fonts can have one depth of 1, 2, 4, 8 bits (used as pixel intensity)
29 | to create smooth fonts and you can select the byte order (some fonts can use bit 7 as first pixel or bit 0) .
30 |
31 | w and h must be 8, 16, 32, .....
32 |
33 | It receive one RSX texture pointer and return the texture pointer increased and aligned to 16 bytes think to use this pointer to build the next
34 | RSX texture.
35 | */
36 |
37 | uint8_t * AddFontFromBitmapArray(uint8_t *font, uint8_t *texture, uint8_t first_char, uint8_t last_char, int w, int h, int bits_per_pixel, int byte_order);
38 |
39 | /*
40 | add one bitmap font creating it from True Type Fonts. You can define the font range with first_char and last_char in the range 0 to 255
41 | w and h must be 8, 16, 32, .....
42 |
43 | The callback is used to create the font externally (it don't need to use freetype library directly)
44 |
45 | It receive one RSX texture pointer and return the texture pointer increased and aligned to 16 bytes think to use this pointer to build the next
46 | RSX texture.
47 | */
48 |
49 | uint8_t * AddFontFromTTF(uint8_t *texture, uint8_t first_char, uint8_t last_char, int w, int h,
50 | void (* ttf_callback) (uint8_t chr, uint8_t * bitmap, short *w, short *h, short *y_correction));
51 |
52 | /* function to select the current font to use (the first is 0. if you select an undefined font, it uses font 0) */
53 |
54 | void SetCurrentFont(int nfont);
55 |
56 | // font are resizable: the minimun is 8 x 8 but you can use as you want. Remember the font quality depends of the original size
57 |
58 | void SetFontSize(int sx, int sy);
59 |
60 | // select the color and the background color for the font. if you use 0 for bkcolor background is not drawing
61 |
62 | void SetFontColor(uint32_t color, uint32_t bkcolor);
63 |
64 | #define FONT_ALIGN_LEFT 0
65 | #define FONT_ALIGN_SCREEN_CENTER 1
66 | #define FONT_ALIGN_RIGHT 2
67 | #define FONT_ALIGN_CENTER 3
68 |
69 | // Set the DrawString alignment (0=left,1=center,2=right). don't use '\n' or unsupported characters here
70 |
71 | void SetFontAlign(int mode);
72 |
73 | // compatibility with old name
74 | #define SetFontAutocenter SetFontAutoCenter
75 |
76 | // enable the auto new line if width is different to 0. When one word exceed the width specified, it skip to the next line
77 |
78 | void SetFontAutoNewLine(int width);
79 |
80 | // Z used to draw the font. Usually is 0.0f
81 |
82 | void SetFontZ(float z);
83 |
84 | // last X used
85 |
86 | float GetFontX();
87 |
88 | // last Y used
89 |
90 | float GetFontY();
91 |
92 | // set mono spacing (if 0 uses normal space)
93 |
94 | void SetMonoSpace(int space);
95 |
96 | // set spacing
97 |
98 | void SetExtraSpace(int space);
99 |
100 | // register special character into font
101 |
102 | void RegisterSpecialCharacter(char value, short fy, float scale, pkgi_texture image);
103 |
104 | // get width of string based on font
105 |
106 | int WidthFromStr(const char * str);
107 |
108 | // function to draw one character
109 |
110 | void DrawChar(float x, float y, float z, uint8_t chr);
111 |
112 | // function to draw one string monospaced. It return X incremented
113 |
114 | float DrawStringMono(float x, float y, const char *str);
115 |
116 | // function to draw one string. It return X incremented
117 |
118 | float DrawString(float x, float y, const char *str);
119 |
120 | // function to draw with fomat string similar to printf. It return X incremented
121 |
122 | float DrawFormatString(float x, float y, char *format, ...);
123 | float DrawFormatStringMono(float x, float y, char *format, ...);
124 |
125 | #endif
--------------------------------------------------------------------------------
/include/pkgi.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include "pkgi_dialog.h"
6 |
7 | #define PKGI_UPDATE_URL "https://api.github.com/repos/bucanero/pkgi-psp/releases/latest"
8 | #define PKGI_VERSION "1.1.0"
9 |
10 | #define PKGI_BUTTON_SELECT 0x000001
11 | #define PKGI_BUTTON_START 0x000008
12 | #define PKGI_BUTTON_UP 0x000010
13 | #define PKGI_BUTTON_RIGHT 0x000020
14 | #define PKGI_BUTTON_DOWN 0x000040
15 | #define PKGI_BUTTON_LEFT 0x000080
16 |
17 | #define PKGI_BUTTON_LT 0x000100 // L1
18 | #define PKGI_BUTTON_RT 0x000200 // R1
19 |
20 | #define PKGI_BUTTON_X 0x004000 // cross
21 | #define PKGI_BUTTON_O 0x002000 // circle
22 | #define PKGI_BUTTON_T 0x001000 // triangle
23 | #define PKGI_BUTTON_S 0x008000 // square
24 |
25 | #define PKGI_UNUSED(x) (void)(x)
26 |
27 | #define PKGI_APP_FOLDER "."
28 | #define PKGI_RAP_FOLDER "/PKG/RAP"
29 | #define PKGI_TMP_FOLDER "/PKG"
30 | #define PKGI_INSTALL_FOLDER "/PSP/GAME"
31 |
32 |
33 | #define PKGI_COUNTOF(arr) (sizeof(arr)/sizeof(0[arr]))
34 |
35 | #ifdef PKGI_ENABLE_LOGGING
36 | #include
37 | #define LOG dbglogger_log
38 | #else
39 | #define LOG(...)
40 | #endif
41 |
42 | int pkgi_snprintf(char* buffer, uint32_t size, const char* msg, ...);
43 | void pkgi_vsnprintf(char* buffer, uint32_t size, const char* msg, va_list args);
44 | char* pkgi_strstr(const char* str, const char* sub);
45 | int pkgi_stricontains(const char* str, const char* sub);
46 | int pkgi_stricmp(const char* a, const char* b);
47 | void pkgi_strncpy(char* dst, uint32_t size, const char* src);
48 | char* pkgi_strrchr(const char* str, char ch);
49 | uint32_t pkgi_strlen(const char *str);
50 | int64_t pkgi_strtoll(const char* str);
51 | void pkgi_memcpy(void* dst, const void* src, uint32_t size);
52 | void pkgi_memmove(void* dst, const void* src, uint32_t size);
53 | int pkgi_memequ(const void* a, const void* b, uint32_t size);
54 | void* pkgi_malloc(uint32_t size);
55 | void pkgi_free(void* ptr);
56 |
57 | int pkgi_ok_button(void);
58 | int pkgi_cancel_button(void);
59 |
60 | void pkgi_start(void);
61 | int pkgi_update(pkgi_input* input);
62 | void pkgi_swap(void);
63 | void pkgi_end(void);
64 |
65 | int pkgi_get_battery_charge(int* status);
66 | const char* pkgi_get_storage_device(void);
67 | int pkgi_is_psp_go(uint8_t dev);
68 |
69 | uint64_t pkgi_get_free_space(void);
70 | const char* pkgi_get_config_folder(void);
71 | const char* pkgi_get_temp_folder(void);
72 | const char* pkgi_get_app_folder(void);
73 | int pkgi_is_incomplete(const char* titleid);
74 | int pkgi_is_installed(const char* titleid);
75 | int pkgi_install(int iso_mode, int remove_pkg);
76 |
77 | uint32_t pkgi_time_msec(void);
78 |
79 | typedef void pkgi_thread_entry(void);
80 | void pkgi_start_thread(const char* name, pkgi_thread_entry* start);
81 | void pkgi_thread_exit(void);
82 | void pkgi_sleep(uint32_t msec);
83 |
84 | int pkgi_load(const char* name, void* data, uint32_t max);
85 | int pkgi_save(const char* name, const void* data, uint32_t size);
86 |
87 | void pkgi_lock_process(void);
88 | void pkgi_unlock_process(void);
89 |
90 | int pkgi_dialog_lock(void);
91 | int pkgi_dialog_unlock(void);
92 |
93 | void pkgi_dialog_input_text(const char* title, const char* text);
94 | int pkgi_dialog_input_update(void);
95 | void pkgi_dialog_input_get_text(char* text, uint32_t size);
96 |
97 | int pkgi_check_free_space(uint64_t http_length);
98 |
99 | typedef struct pkgi_http pkgi_http;
100 |
101 | int pkgi_validate_url(const char* url);
102 | pkgi_http* pkgi_http_get(const char* url, const char* content, uint64_t offset);
103 | int pkgi_http_response_length(pkgi_http* http, int64_t* length);
104 | int pkgi_http_read(pkgi_http* http, void* write_func, void* xferinfo_func);
105 | void pkgi_http_close(pkgi_http* http);
106 |
107 | int pkgi_mkdirs(const char* path);
108 | void pkgi_rm(const char* file);
109 | int64_t pkgi_get_size(const char* path);
110 |
111 | // creates file (if it exists, truncates size to 0)
112 | void* pkgi_create(const char* path);
113 | // open existing file in read mode, fails if file does not exist
114 | void* pkgi_open(const char* path);
115 | // open file for writing, next write will append data to end of it
116 | void* pkgi_append(const char* path);
117 |
118 | void pkgi_close(void* f);
119 |
120 | int pkgi_read(void* f, void* buffer, uint32_t size);
121 | int pkgi_write(void* f, const void* buffer, uint32_t size);
122 |
123 | // UI stuff
124 | struct pkgi_texture_s
125 | {
126 | void *texture;
127 | unsigned short width;
128 | unsigned short height;
129 | };
130 |
131 | typedef struct pkgi_texture_s* pkgi_texture;
132 |
133 | #define pkgi_load_image_buffer(name, type) \
134 | ({ extern const uint8_t _binary_data_##name##_##type##_start; \
135 | extern const uint8_t _binary_data_##name##_##type##_size; \
136 | pkgi_load_##type##_raw((void*) &_binary_data_##name##_##type##_start , (int) &_binary_data_##name##_##type##_size); \
137 | })
138 |
139 | pkgi_texture loadPngTexture(const char* path, const void* buffer);
140 | pkgi_texture pkgi_load_png_raw(const void* data, uint32_t size);
141 | pkgi_texture pkgi_load_png_file(const char* filename);
142 | void pkgi_draw_background(pkgi_texture texture);
143 | void pkgi_draw_texture(pkgi_texture texture, int x, int y);
144 | void pkgi_draw_texture_z(pkgi_texture texture, int x, int y, int z, float scale);
145 | void pkgi_free_texture(pkgi_texture texture);
146 |
147 | void pkgi_clip_set(int x, int y, int w, int h);
148 | void pkgi_clip_remove(void);
149 | void pkgi_draw_rect(int x, int y, int w, int h, uint32_t color);
150 | void pkgi_draw_rect_z(int x, int y, int z, int w, int h, uint32_t color);
151 | void pkgi_draw_fill_rect(int x, int y, int w, int h, uint32_t color);
152 | void pkgi_draw_fill_rect_z(int x, int y, int z, int w, int h, uint32_t color);
153 | void pkgi_draw_text(int x, int y, uint32_t color, const char* text);
154 | void pkgi_draw_text_z(int x, int y, int z, uint32_t color, const char* text);
155 | void pkgi_draw_text_ttf(int x, int y, int z, uint32_t color, const char* text);
156 | int pkgi_text_width(const char* text);
157 | int pkgi_text_width_ttf(const char* text);
158 | int pkgi_text_height(const char* text);
159 |
--------------------------------------------------------------------------------
/include/pkgi_aes.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "pkgi_utils.h"
4 | #include
5 |
6 |
7 | void aes128_init(mbedtls_aes_context* ctx, const uint8_t* key);
8 | void aes128_init_dec(mbedtls_aes_context* ctx, const uint8_t* key);
9 |
10 | void aes128_ecb_encrypt(mbedtls_aes_context* ctx, const uint8_t* input, uint8_t* output);
11 | void aes128_ecb_decrypt(mbedtls_aes_context* ctx, const uint8_t* input, uint8_t* output);
12 |
13 | void aes128_ctr_xor(mbedtls_aes_context* ctx, const uint8_t* iv, uint64_t block, uint8_t* buffer, size_t size);
14 |
15 | void aes128_cmac(const uint8_t* key, const uint8_t* buffer, uint32_t size, uint8_t* mac);
16 |
17 | void aes128_psp_decrypt(mbedtls_aes_context* ctx, const uint8_t* iv, uint32_t index, uint8_t* buffer, uint32_t size);
18 |
--------------------------------------------------------------------------------
/include/pkgi_config.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "pkgi_db.h"
4 |
5 | void pkgi_load_config(Config* config, char* update_url, uint32_t update_len);
6 | void pkgi_save_config(const Config* config, const char* update_url, uint32_t update_len);
7 |
8 | const char* pkgi_content_tag(ContentType content);
9 | const char* pkgi_get_user_language(void);
10 |
--------------------------------------------------------------------------------
/include/pkgi_db.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 |
6 | typedef enum {
7 | PresenceUnknown,
8 | PresenceIncomplete,
9 | PresenceInstalled,
10 | PresenceMissing,
11 | } DbPresence;
12 |
13 | typedef enum {
14 | SortByTitle,
15 | SortByRegion,
16 | SortByName,
17 | SortBySize,
18 | } DbSort;
19 |
20 | typedef enum {
21 | SortAscending,
22 | SortDescending,
23 | } DbSortOrder;
24 |
25 | typedef enum {
26 | DbFilterRegionASA = 0x01,
27 | DbFilterRegionEUR = 0x02,
28 | DbFilterRegionJPN = 0x04,
29 | DbFilterRegionUSA = 0x08,
30 |
31 | // TODO: implement these two
32 | DbFilterInstalled = 0x10,
33 | DbFilterMissing = 0x20,
34 |
35 | DbFilterContentGame = 0x000100,
36 | DbFilterContentDLC = 0x000200,
37 | DbFilterContentTheme = 0x000400,
38 | DbFilterContentPSX = 0x000800,
39 | DbFilterContentDemo = 0x001000,
40 | DbFilterContentUpdate = 0x002000,
41 | DbFilterContentEmulator = 0x004000,
42 | DbFilterContentApp = 0x008000,
43 | DbFilterContentLocal = 0x010000,
44 |
45 | DbFilterAllRegions = DbFilterRegionUSA | DbFilterRegionEUR | DbFilterRegionJPN | DbFilterRegionASA,
46 | DbFilterAllContent = DbFilterContentGame | DbFilterContentDLC | DbFilterContentTheme | DbFilterContentPSX |
47 | DbFilterContentDemo | DbFilterContentUpdate | DbFilterContentEmulator | DbFilterContentApp | DbFilterContentLocal,
48 | DbFilterAll = DbFilterAllRegions | DbFilterAllContent | DbFilterInstalled | DbFilterMissing,
49 | } DbFilter;
50 |
51 | typedef enum {
52 | ContentUnknown,
53 | ContentGame,
54 | ContentDLC,
55 | ContentTheme,
56 | ContentPSX,
57 | ContentDemo,
58 | ContentUpdate,
59 | ContentEmulator,
60 | ContentApp,
61 | ContentLocal,
62 | MAX_CONTENT_TYPES
63 | } ContentType;
64 |
65 | typedef struct {
66 | DbPresence presence;
67 | const char* content;
68 | ContentType type;
69 | const char* name;
70 | const char* description;
71 | const uint8_t* rap;
72 | const char* url;
73 | const uint8_t* digest;
74 | int64_t size;
75 | } DbItem;
76 |
77 | typedef enum {
78 | RegionASA,
79 | RegionEUR,
80 | RegionJPN,
81 | RegionUSA,
82 | RegionUnknown,
83 | } GameRegion;
84 |
85 | typedef struct Config {
86 | DbSort sort;
87 | DbSortOrder order;
88 | uint32_t filter;
89 | uint8_t content;
90 | uint8_t version_check;
91 | uint8_t install_mode_iso;
92 | uint8_t keep_pkg;
93 | uint8_t allow_refresh;
94 | uint8_t storage;
95 | char language[3];
96 | } Config;
97 |
98 |
99 | int pkgi_db_reload(char* error, uint32_t error_size);
100 | int pkgi_db_update(const char* update_url, uint32_t update_len, char* error, uint32_t error_size);
101 | void pkgi_db_get_update_status(uint32_t* updated, uint32_t* total);
102 | int pkgi_db_load_xml_updates(const char* content_id, const char* name);
103 |
104 | void pkgi_db_configure(const char* search, const Config* config);
105 |
106 | uint32_t pkgi_db_count(void);
107 | uint32_t pkgi_db_total(void);
108 | DbItem* pkgi_db_get(uint32_t index);
109 |
110 | GameRegion pkgi_get_region(const char* content);
111 | ContentType pkgi_get_content_type(uint32_t content);
112 |
--------------------------------------------------------------------------------
/include/pkgi_dialog.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "pkgi_db.h"
5 |
6 | #define MDIALOG_OK 0
7 | #define MDIALOG_YESNO 1
8 |
9 | typedef struct pkgi_input {
10 | uint64_t delta; // microseconds from previous frame
11 | uint32_t pressed; // button pressed in last frame
12 | uint32_t down; // button is currently down
13 | uint32_t active; // button is pressed in last frame, or held down for a long time (10 frames)
14 | } pkgi_input;
15 |
16 | typedef void (*pkgi_dialog_callback_t)(int);
17 |
18 | void pkgi_dialog_init(void);
19 |
20 | int pkgi_dialog_is_open(void);
21 | int pkgi_dialog_is_cancelled(void);
22 | void pkgi_dialog_allow_close(int allow);
23 | void pkgi_dialog_message(const char* title, const char* text);
24 | void pkgi_dialog_error(const char* text);
25 | void pkgi_dialog_details(DbItem* item, const char* type);
26 | void pkgi_dialog_ok_cancel(const char* title, const char* text, pkgi_dialog_callback_t callback);
27 |
28 | void pkgi_dialog_start_progress(const char* title, const char* text, float progress);
29 | void pkgi_dialog_set_progress_title(const char* title);
30 | void pkgi_dialog_update_progress(const char* text, const char* extra, const char* eta, float progress);
31 |
32 | void pkgi_dialog_close(void);
33 |
34 | void pkgi_do_dialog(pkgi_input* input);
35 |
36 | int pkgi_msg_dialog(int tdialog, const char * str);
37 |
--------------------------------------------------------------------------------
/include/pkgi_download.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "pkgi_db.h"
5 |
6 | #define PKGI_RAP_SIZE 16
7 |
8 | int pkgi_download(const DbItem* item);
9 | char * pkgi_http_download_buffer(const char* url, uint32_t* buf_size);
10 |
11 | void progress_screen_refresh(void);
12 | void update_install_progress(const char *filename, int64_t progress);
13 | int install_psp_pkg(const char *file);
14 | int convert_psp_pkg_iso(const char* pkg_arg, int cso);
15 | int extract_zip(const char* zip_file);
16 |
--------------------------------------------------------------------------------
/include/pkgi_menu.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "pkgi_db.h"
4 | #include "pkgi_dialog.h"
5 |
6 |
7 | typedef enum {
8 | MenuResultSearch,
9 | MenuResultSearchClear,
10 | MenuResultAccept,
11 | MenuResultCancel,
12 | MenuResultRefresh,
13 | } MenuResult;
14 |
15 | int pkgi_menu_is_open(void);
16 | void pkgi_menu_get(Config* config);
17 | MenuResult pkgi_menu_result(void);
18 |
19 | void pkgi_menu_start(int search_clear, const Config* config);
20 |
21 | int pkgi_do_menu(pkgi_input* input);
22 |
--------------------------------------------------------------------------------
/include/pkgi_sha256.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "pkgi_utils.h"
4 | #include
5 |
6 | #define SHA256_DIGEST_SIZE 32
7 |
--------------------------------------------------------------------------------
/include/pkgi_style.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define PKGI_SCREEN_WIDTH 480
4 | #define PKGI_SCREEN_HEIGHT 272
5 |
6 | #define PKGI_COLOR(R, G, B) (((R)<<24) | ((G)<<16) | ((B)<<8))
7 | #define RGBA_COLOR(C, ALPHA) ((C) | ALPHA)
8 |
9 | #define RGBA_R(c) (uint8_t)((c & 0xFF000000) >> 24)
10 | #define RGBA_G(c) (uint8_t)((c & 0x00FF0000) >> 16)
11 | #define RGBA_B(c) (uint8_t)((c & 0x0000FF00) >> 8)
12 | #define RGBA_A(c) (uint8_t) (c & 0x000000FF)
13 |
14 | #define PKGI_UTF8_O "\xfa" // "\xe2\x97\x8b" // 0x25cb
15 | #define PKGI_UTF8_X "\xfb" // "\xe2\x95\xb3" // 0x2573
16 | #define PKGI_UTF8_T "\xfc" // "\xe2\x96\xb3" // 0x25b3
17 | #define PKGI_UTF8_S "\xfd" // "\xe2\x96\xa1" // 0x25a1
18 |
19 |
20 | #define PKGI_UTF8_INSTALLED "\x04"//"\xe2\x97\x8f" // 0x25cf
21 | #define PKGI_UTF8_PARTIAL "\x09"//"\xe2\x97\x8b" // 0x25cb
22 |
23 | #define PKGI_UTF8_B "B"
24 | #define PKGI_UTF8_KB "Kb" // "\xe3\x8e\x85" // 0x3385
25 | #define PKGI_UTF8_MB "Mb" // "\xe3\x8e\x86" // 0x3386
26 | #define PKGI_UTF8_GB "Gb" // "\xe3\x8e\x87" // 0x3387
27 |
28 | #define PKGI_UTF8_CLEAR "\xaf" // 0x00d7
29 |
30 | #define PKGI_UTF8_SORT_ASC "\x1e" //"\xe2\x96\xb2" // 0x25b2
31 | #define PKGI_UTF8_SORT_DESC "\x1f" //"\xe2\x96\xbc" // 0x25bc
32 |
33 | #define PKGI_UTF8_CHECK_ON "\x04"//"\xe2\x97\x8f" // 0x25cf
34 | #define PKGI_UTF8_CHECK_OFF "\x09"//"\xe2\x97\x8b" // 0x25cb
35 |
36 | #define PKGI_COLOR_DIALOG_BACKGROUND PKGI_COLOR(48, 48, 48)
37 | #define PKGI_COLOR_MENU_BORDER PKGI_COLOR(80, 80, 255)
38 | #define PKGI_COLOR_MENU_BACKGROUND PKGI_COLOR(48, 48, 48)
39 | #define PKGI_COLOR_TEXT_MENU PKGI_COLOR(255, 255, 255)
40 | #define PKGI_COLOR_TEXT_MENU_SELECTED PKGI_COLOR(0, 255, 0)
41 | #define PKGI_COLOR_TEXT PKGI_COLOR(255, 255, 255)
42 | #define PKGI_COLOR_TEXT_HEAD PKGI_COLOR(255, 255, 255)
43 | #define PKGI_COLOR_TEXT_TAIL PKGI_COLOR(255, 255, 255)
44 | #define PKGI_COLOR_TEXT_DIALOG PKGI_COLOR(255, 255, 255)
45 | #define PKGI_COLOR_TEXT_ERROR PKGI_COLOR(255, 50, 50)
46 | #define PKGI_COLOR_TEXT_SHADOW PKGI_COLOR(0, 0, 0)
47 | #define PKGI_COLOR_HLINE PKGI_COLOR(200, 200, 200)
48 | #define PKGI_COLOR_SCROLL_BAR PKGI_COLOR(255, 255, 255)
49 | #define PKGI_COLOR_BATTERY_LOW PKGI_COLOR(255, 50, 50)
50 | #define PKGI_COLOR_BATTERY_CHARGING PKGI_COLOR(50, 255, 50)
51 | #define PKGI_COLOR_SELECTED_BACKGROUND PKGI_COLOR(60, 60, 60)
52 | #define PKGI_COLOR_PROGRESS_BACKGROUND PKGI_COLOR(128, 128, 128)
53 | #define PKGI_COLOR_PROGRESS_BAR PKGI_COLOR(128, 255, 0)
54 |
55 | #define PKGI_ANIMATION_SPEED 4000 // px/second
56 |
57 | #define PKGI_FONT_8x16 0
58 | #define PKGI_FONT_Z 1000
59 | #define PKGI_FONT_WIDTH 8
60 | #define PKGI_FONT_HEIGHT 16
61 | #define PKGI_FONT_SHADOW 2
62 |
63 | #define PKGI_MAIN_COLUMN_PADDING 10
64 | #define PKGI_MAIN_HLINE_EXTRA 5
65 | #define PKGI_MAIN_ROW_PADDING 1
66 | #define PKGI_MAIN_HLINE_HEIGHT 2
67 | #define PKGI_MAIN_TEXT_PADDING 5
68 | #define PKGI_MAIN_SCROLL_WIDTH 2
69 | #define PKGI_MAIN_SCROLL_PADDING 2
70 | #define PKGI_MAIN_SCROLL_MIN_HEIGHT 50
71 | #define PKGI_MAIN_HMARGIN 8
72 | #define PKGI_MAIN_VMARGIN 4
73 |
74 | #define PKGI_DIALOG_TEXT_Z 800
75 | #define PKGI_DIALOG_HMARGIN 50
76 | #define PKGI_DIALOG_VMARGIN 50
77 | #define PKGI_DIALOG_PADDING 30
78 | #define PKGI_DIALOG_WIDTH (PKGI_SCREEN_WIDTH - 2*PKGI_DIALOG_HMARGIN)
79 | #define PKGI_DIALOG_HEIGHT (PKGI_SCREEN_HEIGHT - 2*PKGI_DIALOG_VMARGIN)
80 |
81 | #define PKGI_DIALOG_PROCESS_BAR_HEIGHT 10
82 | #define PKGI_DIALOG_PROCESS_BAR_PADDING 10
83 | #define PKGI_DIALOG_PROCESS_BAR_CHUNK 200
84 |
85 | #define PKGI_MENU_Z 900
86 | #define PKGI_MENU_TEXT_Z 800
87 | #define PKGI_MENU_WIDTH 150
88 | #define PKGI_MENU_HEIGHT 260
89 | #define PKGI_MENU_LEFT_PADDING 20
90 | #define PKGI_MENU_TOP_PADDING 20
91 |
--------------------------------------------------------------------------------
/include/pkgi_utils.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #ifdef _MSC_VER
6 | #define GCC_ALIGN(n)
7 | #else
8 | #define GCC_ALIGN(n) __attribute__((aligned(n)))
9 | #endif
10 |
11 | static inline uint8_t byte32(uint32_t x, int n)
12 | {
13 | return (uint8_t)(x >> (8 * n));
14 | }
15 |
16 | static inline uint32_t min32(uint32_t a, uint32_t b)
17 | {
18 | return a < b ? a : b;
19 | }
20 |
21 | static inline uint64_t min64(uint64_t a, uint64_t b)
22 | {
23 | return a < b ? a : b;
24 | }
25 |
26 | static inline uint32_t max32(uint32_t a, uint32_t b)
27 | {
28 | return a > b ? a : b;
29 | }
30 |
31 | static inline uint64_t max64(uint64_t a, uint64_t b)
32 | {
33 | return a > b ? a : b;
34 | }
35 |
36 | static inline uint32_t ror32(uint32_t x, int n)
37 | {
38 | return (x >> n) | (x << (32 - n));
39 | }
40 |
41 | static inline uint16_t get16le(const uint8_t* bytes)
42 | {
43 | return (bytes[0]) | (bytes[1] << 8);
44 | }
45 |
46 | static inline uint32_t get32le(const uint8_t* bytes)
47 | {
48 | return (bytes[0]) | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
49 | }
50 |
51 | static inline uint64_t get64le(const uint8_t* bytes)
52 | {
53 | return (uint64_t)bytes[0]
54 | | ((uint64_t)bytes[1] << 8)
55 | | ((uint64_t)bytes[2] << 16)
56 | | ((uint64_t)bytes[3] << 24)
57 | | ((uint64_t)bytes[4] << 32)
58 | | ((uint64_t)bytes[5] << 40)
59 | | ((uint64_t)bytes[6] << 48)
60 | | ((uint64_t)bytes[7] << 56);
61 | }
62 |
63 | static inline uint16_t get16be(const uint8_t* bytes)
64 | {
65 | return (bytes[1]) | (bytes[0] << 8);
66 | }
67 |
68 | static inline uint32_t get32be(const uint8_t* bytes)
69 | {
70 | return (bytes[3]) | (bytes[2] << 8) | (bytes[1] << 16) | (bytes[0] << 24);
71 | }
72 |
73 | static inline uint64_t get64be(const uint8_t* bytes)
74 | {
75 | return (uint64_t)bytes[7]
76 | | ((uint64_t)bytes[6] << 8)
77 | | ((uint64_t)bytes[5] << 16)
78 | | ((uint64_t)bytes[4] << 24)
79 | | ((uint64_t)bytes[3] << 32)
80 | | ((uint64_t)bytes[2] << 40)
81 | | ((uint64_t)bytes[1] << 48)
82 | | ((uint64_t)bytes[0] << 56);
83 | }
84 |
85 | static inline void set16le(uint8_t* bytes, uint16_t x)
86 | {
87 | bytes[0] = (uint8_t)x;
88 | bytes[1] = (uint8_t)(x >> 8);
89 | }
90 |
91 | static inline void set32le(uint8_t* bytes, uint32_t x)
92 | {
93 | bytes[0] = (uint8_t)x;
94 | bytes[1] = (uint8_t)(x >> 8);
95 | bytes[2] = (uint8_t)(x >> 16);
96 | bytes[3] = (uint8_t)(x >> 24);
97 | }
98 |
99 | static inline void set64le(uint8_t* bytes, uint64_t x)
100 | {
101 | bytes[0] = (uint8_t)x;
102 | bytes[1] = (uint8_t)(x >> 8);
103 | bytes[2] = (uint8_t)(x >> 16);
104 | bytes[3] = (uint8_t)(x >> 24);
105 | bytes[4] = (uint8_t)(x >> 32);
106 | bytes[5] = (uint8_t)(x >> 40);
107 | bytes[6] = (uint8_t)(x >> 48);
108 | bytes[7] = (uint8_t)(x >> 56);
109 | }
110 |
111 | static inline void set16be(uint8_t* bytes, uint16_t x)
112 | {
113 | bytes[0] = (uint8_t)(x >> 8);
114 | bytes[1] = (uint8_t)x;
115 | }
116 |
117 | static inline void set32be(uint8_t* bytes, uint32_t x)
118 | {
119 | bytes[0] = (uint8_t)(x >> 24);
120 | bytes[1] = (uint8_t)(x >> 16);
121 | bytes[2] = (uint8_t)(x >> 8);
122 | bytes[3] = (uint8_t)x;
123 | }
124 |
125 | static inline void set64be(uint8_t* bytes, uint64_t x)
126 | {
127 | bytes[0] = (uint8_t)(x >> 56);
128 | bytes[1] = (uint8_t)(x >> 48);
129 | bytes[2] = (uint8_t)(x >> 40);
130 | bytes[3] = (uint8_t)(x >> 32);
131 | bytes[4] = (uint8_t)(x >> 24);
132 | bytes[5] = (uint8_t)(x >> 16);
133 | bytes[6] = (uint8_t)(x >> 8);
134 | bytes[7] = (uint8_t)x;
135 | }
136 |
--------------------------------------------------------------------------------
/include/ttf_render.h:
--------------------------------------------------------------------------------
1 | #ifndef TTF_RENDER_H
2 | #define TTF_RENDER_H
3 |
4 |
5 | int TTFLoadFont(int set, const char * path, void * from_memory, int size_from_memory);
6 | void TTFUnloadFont();
7 |
8 | void TTF_to_Bitmap(uint8_t chr, uint8_t * bitmap, short *w, short *h, short *y_correction);
9 |
10 | int Render_String_UTF8(uint16_t * bitmap, int w, int h, uint8_t *string, int sw, int sh);
11 |
12 |
13 | // initialize and create textures slots for ttf
14 | uint16_t * init_ttf_table(uint8_t *texture);
15 |
16 | // do one time per frame to reset the character use flag
17 | void reset_ttf_frame(void);
18 |
19 | // define window mode and size
20 |
21 | #define WIN_AUTO_LF 1
22 | #define WIN_SKIP_LF 2
23 | #define WIN_DOUBLE_LF 4
24 |
25 | void set_ttf_window(int x, int y, int width, int height, uint32_t mode);
26 |
27 | extern float Y_ttf;
28 | extern float Z_ttf;
29 |
30 | // display UTF8 string int posx/posy position. With color 0 don't display and refresh/calculate the width.
31 | // color is the character color and sw/sh the width/height of the characters
32 |
33 | int display_ttf_string(int posx, int posy, const char *string, uint32_t color, uint32_t bkcolor, int sw, int sh, int (*DrawIcon)(int, int, char));
34 | int width_ttf_string(const char *string, int sw, int sh);
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/source/depackager.c:
--------------------------------------------------------------------------------
1 | /*
2 | * based on depackager by qwikrazor87
3 | */
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "pkgi.h"
12 | #include "pkgi_download.h"
13 |
14 | #define DEPACKAGER_VER 3
15 |
16 | /*
17 | typedef struct {
18 | u32 magic;
19 | u16 pkg_revision;
20 | u16 pkg_type;
21 | u32 pkg_metadata_offset;
22 | u32 pkg_metadata_count;
23 | u32 pkg_metadata_size;
24 | u32 item_count;
25 | u64 total_size;
26 | u64 data_offset;
27 | u64 data_size;
28 | u8 contentid[0x30];
29 | u8 digest[0x10];
30 | u8 pkg_data_riv[0x10];
31 | u8 pkg_header_digest[0x40];
32 | } PKG_HEADER;
33 |
34 | typedef struct {
35 | u32 magic; // 0x7F657874 (".ext")
36 | u32 unknown_1; // Maybe version. Always 1.
37 | u32 ext_hdr_size; // Extended header size. ex: 0x40
38 | u32 ext_data_size; // ex: 0x180
39 | u32 main_and_ext_headers_hmac_offset; // ex: 0x100
40 | u32 metadata_header_hmac_offset; // ex: 0x360, 0x390, 0x490
41 | u64 tail_offset; // Tail size seems to be always 0x1A0
42 | u32 padding1;
43 | u32 pkg_key_id; // Id of the AES key used for decryption. PSP = 0x1, PS Vita = 0xC0000002, PSM = 0xC0000004
44 | u32 full_header_hmac_offset; // ex: none (old pkg): 0, 0x930
45 | u8 padding2[0x14];
46 | } PKG_EXT_HEADER;
47 | */
48 |
49 |
50 | static u8 static_public_key[16];
51 |
52 | static const u8 PSPAESKey[16] __attribute__((aligned(16))) = {
53 | 0x07, 0xF2, 0xC6, 0x82, 0x90, 0xB5, 0x0D, 0x2C, 0x33, 0x81, 0x8D, 0x70, 0x9B, 0x60, 0xE6, 0x2B
54 | };
55 |
56 |
57 | static void AES128_ECB_encrypt(uint8_t* input, const uint8_t* key, uint8_t *output)
58 | {
59 | mbedtls_aes_context ctx;
60 |
61 | mbedtls_aes_init(&ctx);
62 | mbedtls_aes_setkey_enc(&ctx, key, 128);
63 | mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output);
64 | }
65 |
66 | static void AES128_ECB_decrypt(uint8_t* input, const uint8_t* key, uint8_t *output)
67 | {
68 | mbedtls_aes_context ctx;
69 |
70 | mbedtls_aes_init(&ctx);
71 | mbedtls_aes_setkey_dec(&ctx, key, 128);
72 | mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, input, output);
73 | }
74 |
75 | static u32 toU32(char *_buf)
76 | {
77 | u8 *buf = (u8 *)_buf;
78 | u32 b1 = buf[0] << 24;
79 | u32 b2 = buf[1] << 16;
80 | u32 b3 = buf[2] << 8;
81 | u32 size = b1 | b2 | b3 | buf[3];
82 |
83 | return size;
84 | }
85 |
86 | static int is_pkg_supported(const char *file)
87 | {
88 | char buf[16];
89 |
90 | SceUID fd = sceIoOpen(file, PSP_O_RDONLY, 0777);
91 | if (fd >= 0) {
92 | sceIoRead(fd, buf, 4);
93 | sceIoClose(fd);
94 |
95 | return memcmp(buf, "\x7FPKG", 4) == 0;
96 | } else
97 | return 0;
98 | }
99 |
100 | static int is_pkg_type_supported(const char *file)
101 | {
102 | char buf[16];
103 |
104 | SceUID fd = sceIoOpen(file, PSP_O_RDONLY, 0777);
105 | if (fd >= 0) {
106 | sceIoRead(fd, buf, 8);
107 | sceIoClose(fd);
108 |
109 | return memcmp(buf, "\x7FPKG\x80\x00\x00\x02", 8) == 0;
110 | } else
111 | return 0;
112 | }
113 |
114 | static void view_pkg_info(const char *file)
115 | {
116 | char buf[256];
117 |
118 | LOG("PKG info viewer");
119 |
120 | SceUID fd = sceIoOpen(file, PSP_O_RDONLY, 0777);
121 | sceIoRead(fd, buf, sizeof(buf));
122 | sceIoClose(fd);
123 |
124 | u8 version = (u8)buf[4];
125 |
126 | LOG("PKG type: %s", buf[7] == 1 ? "PS3 (currently unsupported)" : buf[7] == 2 ? "PSP" : "unknown");
127 | LOG("PKG version: %s", (version == 0x80) ? "retail" : (version == 0x90) ? "debug (currently unsupported)" : "unknown");
128 | LOG("Content ID: %s", buf + 0x30);
129 | LOG("PKG file count: %d", toU32(buf + 0x14));
130 | LOG("PKG size: %d bytes", toU32(buf + 0x1C));
131 | LOG("Encrypted size: %d bytes", toU32(buf + 0x2C));
132 | }
133 |
134 | static void xor128(u8 *dst, u8 *xor1, u8 *xor2)
135 | {
136 | int i;
137 |
138 | for (i = 0; i < 16; i++)
139 | dst[i] = xor1[i] ^ xor2[i];
140 | }
141 |
142 | static void iter128(u8 *buf)
143 | {
144 | int i;
145 |
146 | for (i = 15; i >= 0; i--) {
147 | buf[i]++;
148 |
149 | if (buf[i])
150 | break;
151 | }
152 | }
153 |
154 | static void setiter128(u8 *dst, int size)
155 | {
156 | memcpy(dst, static_public_key, 16);
157 |
158 | int i;
159 |
160 | for (i = 0; i < size; i++)
161 | iter128(dst);
162 | }
163 |
164 | int install_psp_pkg(const char *file)
165 | {
166 | char *tmpBuf;
167 | char gameid[10];
168 | char pkgBuf[256];
169 | u8 public_key[16], xor_key[16];
170 | SceOff progress = 0;
171 |
172 | LOG("PSP depackager v%d", DEPACKAGER_VER);
173 | LOG("PSP PKG installer");
174 |
175 | if(is_pkg_supported(file))
176 | view_pkg_info(file);
177 |
178 | if (!is_pkg_type_supported(file)) {
179 | LOG("Unsupported PKG type detected.");
180 | return 0;
181 | }
182 |
183 | SceUID fd = sceIoOpen(file, PSP_O_RDONLY, 0777);
184 | SceSize fdsize = sceIoLseek(fd, 0, PSP_SEEK_END);
185 | sceIoLseek(fd, 0, PSP_SEEK_SET);
186 | sceIoRead(fd, pkgBuf, 256);
187 |
188 | if (toU32(pkgBuf + 0x1C) != fdsize) {
189 | sceIoClose(fd);
190 | LOG("Corrupt PKG detected");
191 | LOG("detected size: %d bytes", fdsize);
192 | LOG("expected size: %d bytes", toU32(pkgBuf + 0x1C));
193 | return 0;
194 | }
195 |
196 | sceIoLseek(fd, toU32(pkgBuf + 8) + 0x14, PSP_SEEK_SET);
197 | sceIoRead(fd, gameid, 4);
198 |
199 | if (toU32(gameid) == 9) {
200 | sceIoClose(fd);
201 | LOG("Theme PKG detected");
202 | return (convert_psp_pkg_iso(file, 0));
203 | }
204 |
205 | //1 MiB buffer
206 | tmpBuf = (char *)malloc(1024 * 1024);
207 | if (!tmpBuf) {
208 | LOG("Error allocating memory: 0x%08X", 1024 * 1024);
209 | return 0;
210 | }
211 |
212 | sceIoLseek(fd, toU32(pkgBuf + 8) + 0x48, PSP_SEEK_SET);
213 | sceIoRead(fd, gameid, 9);
214 | gameid[9] = 0;
215 |
216 | memcpy(public_key, pkgBuf + 0x70, 16);
217 | memcpy(static_public_key, pkgBuf + 0x70, 16);
218 |
219 | u32 enc_start = toU32(pkgBuf + 0x24);
220 | u32 files = toU32(pkgBuf + 0x14);
221 |
222 | sceIoLseek(fd, enc_start, PSP_SEEK_SET);
223 | sceIoRead(fd, tmpBuf, files * 32);
224 |
225 | int i, j, pspcount = 0;
226 | u32 file_name[files], file_name_len[files], file_offset[files], file_size[files], is_file[files];
227 |
228 | for (i = 0; i < (int)(files * 2); i++) {
229 | AES128_ECB_encrypt(public_key, PSPAESKey, xor_key);
230 | xor128((u8 *)(tmpBuf + (i * 16)), (u8 *)(tmpBuf + (i * 16)), xor_key);
231 | iter128(public_key);
232 | }
233 |
234 | for (i = 0; i < (int)files; i++) {
235 | if (((u8 *)tmpBuf)[(i * 32) + 24] == 0x90) {
236 | file_name[pspcount] = toU32(tmpBuf + i * 32);
237 | file_name_len[pspcount] = toU32(tmpBuf + i * 32 + 4);
238 | file_offset[pspcount] = toU32(tmpBuf + i * 32 + 12);
239 | file_size[pspcount] = toU32(tmpBuf + i * 32 + 20);
240 | is_file[pspcount] = ((tmpBuf[i * 32 + 27] != 4) && file_size[pspcount]);
241 | pspcount++;
242 | }
243 | }
244 |
245 | int files_extracted = 0;
246 |
247 | for (i = 0; i < pspcount; i++) {
248 | sceIoLseek(fd, enc_start + file_name[i], PSP_SEEK_SET);
249 | int namesize = (file_name_len[i] + 15) & -16;
250 | sceIoRead(fd, tmpBuf, namesize);
251 | memcpy(public_key, pkgBuf + 0x70, 16);
252 |
253 | setiter128(public_key, file_name[i] >> 4);
254 |
255 | for (j = 0; j < (namesize >> 4); j++) {
256 | AES128_ECB_encrypt(public_key, PSPAESKey, xor_key);
257 | xor128((u8 *)(tmpBuf + (j * 16)), (u8 *)(tmpBuf + (j * 16)), xor_key);
258 | iter128(public_key);
259 | }
260 |
261 | char path[256];
262 | snprintf(path, sizeof(path), "%s%s/%s/%s", pkgi_get_storage_device(), PKGI_INSTALL_FOLDER, gameid, tmpBuf + 15);
263 | char* slash = strrchr(path, '/');
264 | *slash = 0;
265 | pkgi_mkdirs(path);
266 | *slash = '/';
267 |
268 | if (is_file[i]) {
269 | LOG("Currently extracting: %s", path);
270 | files_extracted++;
271 |
272 | progress = sceIoLseek(fd, enc_start + file_offset[i], PSP_SEEK_SET);
273 | sceIoRead(fd, tmpBuf, 1024 * 1024);
274 | update_install_progress(path + 14, progress);
275 |
276 | setiter128(public_key, file_offset[i] >> 4);
277 |
278 | SceUID dstfd = sceIoOpen(path, 0x602, 0777);
279 |
280 | u32 szcheck = 0, mincheck = 0;
281 |
282 | LOG("%d/%d bytes", mincheck, file_size[i]);
283 |
284 | for (j = 0; j < (int)(file_size[i] >> 4); j++) {
285 | if (szcheck == 1024 * 1024) {
286 | szcheck = 0;
287 | mincheck += 1024 * 1024;
288 |
289 | sceIoWrite(dstfd, tmpBuf, 1024 * 1024);
290 | sceIoRead(fd, tmpBuf, 1024 * 1024);
291 | update_install_progress(NULL, progress + mincheck);
292 |
293 | LOG("%d/%d bytes", mincheck, file_size[i]);
294 | }
295 |
296 | AES128_ECB_encrypt(public_key, PSPAESKey, xor_key);
297 | xor128((u8 *)((tmpBuf + (j * 16)) - mincheck), (u8 *)((tmpBuf + (j * 16)) - mincheck), xor_key);
298 | iter128(public_key);
299 |
300 | szcheck += 16;
301 | }
302 |
303 | if (mincheck < file_size[i])
304 | {
305 | sceIoWrite(dstfd, tmpBuf, file_size[i] - mincheck);
306 | update_install_progress(NULL, progress + file_size[i]);
307 | }
308 |
309 | sceIoClose(dstfd);
310 | }
311 | }
312 |
313 | update_install_progress(file + 9, fdsize);
314 | sceIoClose(fd);
315 | free(tmpBuf);
316 |
317 | LOG("files extracted: %d", files_extracted);
318 | LOG("Installation complete");
319 |
320 | return 1;
321 | }
322 |
--------------------------------------------------------------------------------
/source/libfont.c:
--------------------------------------------------------------------------------
1 | /*
2 | TINY3D - font library / (c) 2010 Hermes
3 |
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "pkgi.h"
12 | #include "libfont.h"
13 | #include "ttf_render.h"
14 | #include "pkgi_style.h"
15 |
16 | extern SDL_Renderer* renderer;
17 |
18 | struct t_font_description
19 | {
20 | int w, h, bh;
21 |
22 | uint8_t first_char;
23 | uint8_t last_char;
24 |
25 | uint8_t* rsx_text_offset;
26 | uint32_t rsx_bytes_per_char;
27 | uint32_t color_format;
28 |
29 | short fw[256]; // chr width
30 | short fy[256]; // chr y correction
31 | };
32 |
33 | static struct t_font_datas
34 | {
35 | int number_of_fonts;
36 | int current_font;
37 |
38 | struct t_font_description fonts[8];
39 |
40 | int sx, sy;
41 | int mono;
42 | int extra;
43 |
44 | uint32_t color, bkcolor;
45 |
46 | int align; //0 = left, 1 = center, 2 = right
47 | int autonewline;
48 |
49 | float X,Y,Z;
50 |
51 | } font_datas;
52 |
53 | typedef struct t_special_char
54 | {
55 | char value;
56 | short fy;
57 | float scale;
58 | pkgi_texture image;
59 | } special_char;
60 |
61 | static special_char special_chars[MAX_SPECIAL_CHARS];
62 | static int special_char_index = 0;
63 |
64 |
65 | void DrawTexture(pkgi_texture tex, int x, int y, int z, int w, int h, uint32_t rgba)
66 | {
67 | SDL_Rect dest = {
68 | .x = x,
69 | .y = y,
70 | .w = w,
71 | .h = h,
72 | };
73 |
74 | SDL_SetTextureAlphaMod(tex->texture, RGBA_A(rgba));
75 | SDL_RenderCopy(renderer, tex->texture, NULL, &dest);
76 | }
77 |
78 | special_char* GetSpecialCharFromValue(const char value)
79 | {
80 | int x;
81 | special_char* ret = NULL;
82 |
83 | for (x = 0; x < special_char_index; x++)
84 | {
85 | if (special_chars[x].value == value)
86 | ret = &(special_chars[x]);
87 | }
88 | return ret;
89 | }
90 |
91 | void ResetFont(void)
92 | {
93 | font_datas.current_font = font_datas.number_of_fonts =0;
94 |
95 | font_datas.color = 0xffffffff;
96 | font_datas.bkcolor = 0;
97 | font_datas.align = FONT_ALIGN_LEFT;
98 | font_datas.X = font_datas.Y = font_datas.Z = 0.0f;
99 | font_datas.autonewline = 0;
100 |
101 | font_datas.sx = font_datas.sy = 8;
102 | font_datas.mono = 0;
103 | }
104 |
105 | uint8_t * AddFontFromBitmapArray(uint8_t *font, uint8_t *texture, uint8_t first_char, uint8_t last_char, int w, int h, int bits_per_pixel, int byte_order)
106 | {
107 | int n, a, b;
108 | uint32_t buf[w*h];
109 | uint8_t i;
110 |
111 | if(font_datas.number_of_fonts >= 8) return texture;
112 |
113 | font_datas.fonts[font_datas.number_of_fonts].w = w;
114 | font_datas.fonts[font_datas.number_of_fonts].h = h;
115 | font_datas.fonts[font_datas.number_of_fonts].bh = h;
116 | font_datas.fonts[font_datas.number_of_fonts].color_format = 1; //TINY3D_TEX_FORMAT_A4R4G4B4; //TINY3D_TEX_FORMAT_A8R8G8B8;
117 | font_datas.fonts[font_datas.number_of_fonts].first_char = first_char;
118 | font_datas.fonts[font_datas.number_of_fonts].last_char = last_char;
119 | font_datas.align = FONT_ALIGN_LEFT;
120 |
121 | font_datas.color = 0xffffffff;
122 | font_datas.bkcolor = 0x0;
123 |
124 | font_datas.sx = w;
125 | font_datas.sy = h;
126 |
127 | font_datas.Z = 0.0f;
128 |
129 | for(n = 0; n < 256; n++) {
130 | font_datas.fonts[font_datas.number_of_fonts].fw[n] = 0;
131 | font_datas.fonts[font_datas.number_of_fonts].fy[n] = 0;
132 | }
133 |
134 | for(n = first_char; n <= last_char; n++) {
135 |
136 | font_datas.fonts[font_datas.number_of_fonts].fw[n] = w;
137 |
138 | texture = (uint8_t *) ((((long) texture) + 15) & ~15);
139 |
140 | if(n == first_char) font_datas.fonts[font_datas.number_of_fonts].rsx_text_offset = texture; //tiny3d_TextureOffset(texture);
141 |
142 | if(n == first_char+1) font_datas.fonts[font_datas.number_of_fonts].rsx_bytes_per_char = texture
143 | - font_datas.fonts[font_datas.number_of_fonts].rsx_text_offset;
144 |
145 | for(a = 0; a < h; a++) {
146 | for(b = 0; b < w; b++) {
147 |
148 | i = font[(b * bits_per_pixel)/8];
149 |
150 | if(byte_order)
151 | i= (i << ((b & (7/bits_per_pixel)) * bits_per_pixel))>> (8-bits_per_pixel);
152 | else
153 | i >>= (b & (7/bits_per_pixel)) * bits_per_pixel;
154 |
155 | i = (i & ((1 << bits_per_pixel)-1)) * 255 / ((1 << bits_per_pixel)-1);
156 |
157 | if(i) {//TINY3D_TEX_FORMAT_A1R5G5B5
158 | //i>>=3;
159 | //*((u16 *) texture) = (1<<15) | (i<<10) | (i<<5) | (i);
160 | //TINY3D_TEX_FORMAT_A4R4G4B4
161 | buf[a*w + b] = 0x000000FF;
162 | // i>>=4;
163 | // *((u16 *) texture) = (i<<12) | 0xfff;
164 | } else {
165 | // texture[0] = texture[1] = 0x0; //texture[2] = 0x0;
166 | buf[a*w + b] = 0x0; // alpha
167 | }
168 | }
169 | font += (((w+7) & ~7) * bits_per_pixel) / 8;
170 | }
171 |
172 | // Black font texture
173 | SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*) buf, w, h, 32, 4 * w, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
174 | *((SDL_Texture**) texture) = SDL_CreateTextureFromSurface(renderer, surface);
175 | SDL_FreeSurface(surface);
176 | texture += sizeof(SDL_Texture*);
177 |
178 | // White font texture
179 | for (a = 0; a < h*w; a++)
180 | if (buf[a]) buf[a] = 0xFFFFFFFF;
181 |
182 | surface = SDL_CreateRGBSurfaceFrom((void*) buf, w, h, 32, 4 * w, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
183 | *((SDL_Texture**) texture) = SDL_CreateTextureFromSurface(renderer, surface);
184 | SDL_FreeSurface(surface);
185 | texture += sizeof(SDL_Texture*);
186 | }
187 |
188 | texture = (uint8_t *) ((((long) texture) + 15) & ~15);
189 |
190 | font_datas.number_of_fonts++;
191 |
192 | return texture;
193 | }
194 |
195 | /*
196 | uint8_t * AddFontFromTTF(uint8_t *texture, uint8_t first_char, uint8_t last_char, int w, int h,
197 | void (* ttf_callback) (uint8_t chr, uint8_t * bitmap, short *w, short *h, short *y_correction))
198 | {
199 | int n, a, b;
200 | uint8_t i;
201 | uint8_t *font;
202 | static uint8_t letter_bitmap[257 * 256];
203 |
204 | int bits_per_pixel = 8;
205 |
206 | if(font_datas.number_of_fonts >= 8) return texture;
207 |
208 | if(h < 8) h = 8;
209 | if(w < 8) w = 8;
210 | if(h > 256) h = 256;
211 | if(w > 256) w = 256;
212 |
213 | font_datas.fonts[font_datas.number_of_fonts].w = w;
214 | font_datas.fonts[font_datas.number_of_fonts].h = h;
215 | font_datas.fonts[font_datas.number_of_fonts].bh = h+4;
216 | font_datas.fonts[font_datas.number_of_fonts].color_format = TINY3D_TEX_FORMAT_A4R4G4B4;
217 | font_datas.fonts[font_datas.number_of_fonts].first_char = first_char;
218 | font_datas.fonts[font_datas.number_of_fonts].last_char = last_char;
219 | font_datas.align = FONT_ALIGN_LEFT;
220 |
221 | font_datas.color = 0xffffffff;
222 | font_datas.bkcolor = 0x0;
223 |
224 | font_datas.sx = w;
225 | font_datas.sy = h;
226 |
227 | font_datas.Z = 0.0f;
228 |
229 | for(n = 0; n < 256; n++) {
230 | font_datas.fonts[font_datas.number_of_fonts].fw[n] = 0;
231 | font_datas.fonts[font_datas.number_of_fonts].fy[n] = 0;
232 | }
233 |
234 |
235 | for(n = first_char; n <= last_char; n++) {
236 |
237 | short hh = h;
238 |
239 | font = letter_bitmap;
240 |
241 | font_datas.fonts[font_datas.number_of_fonts].fw[n] = (short) w;
242 |
243 | ttf_callback((uint8_t) (n & 255), letter_bitmap, &font_datas.fonts[font_datas.number_of_fonts].fw[n], &hh, &font_datas.fonts[font_datas.number_of_fonts].fy[n]);
244 |
245 | // letter background correction
246 | if((hh + font_datas.fonts[font_datas.number_of_fonts].fy[n]) > font_datas.fonts[font_datas.number_of_fonts].bh)
247 | font_datas.fonts[font_datas.number_of_fonts].bh = hh + font_datas.fonts[font_datas.number_of_fonts].fy[n];
248 |
249 | texture = (uint8_t *) ((((long) texture) + 15) & ~15);
250 |
251 | if(n == first_char) font_datas.fonts[font_datas.number_of_fonts].rsx_text_offset = tiny3d_TextureOffset(texture);
252 |
253 | if(n == first_char+1) font_datas.fonts[font_datas.number_of_fonts].rsx_bytes_per_char = tiny3d_TextureOffset(texture)
254 | - font_datas.fonts[font_datas.number_of_fonts].rsx_text_offset;
255 |
256 | for(a = 0; a < h; a++) {
257 | for(b = 0; b < w; b++) {
258 |
259 | i = font[(b * bits_per_pixel)/8];
260 |
261 | i >>= (b & (7/bits_per_pixel)) * bits_per_pixel;
262 |
263 | i = (i & ((1 << bits_per_pixel)-1)) * 255 / ((1 << bits_per_pixel)-1);
264 |
265 | if(i) {//TINY3D_TEX_FORMAT_A4R4G4B4
266 | i>>=4;
267 | *((u16 *) texture) = (i<<12) | 0xfff;
268 | } else {
269 |
270 | texture[0] = texture[1] = 0x0; //texture[2] = 0x0;
271 | //texture[3] = 0x0; // alpha
272 | }
273 | texture+=2;
274 |
275 | }
276 |
277 | font += (w * bits_per_pixel) / 8;
278 |
279 | }
280 |
281 | }
282 |
283 | texture = (uint8_t *) ((((long) texture) + 15) & ~15);
284 |
285 | font_datas.number_of_fonts++;
286 |
287 | return texture;
288 | }
289 | */
290 |
291 | void SetCurrentFont(int nfont)
292 | {
293 | if(nfont < 0 || nfont >= font_datas.number_of_fonts) nfont = 0;
294 |
295 | font_datas.current_font = nfont;
296 | }
297 |
298 | void SetFontSize(int sx, int sy)
299 | {
300 | font_datas.sx = sx;
301 | font_datas.sy = sy;
302 | }
303 |
304 | void SetFontColor(uint32_t color, uint32_t bkcolor)
305 | {
306 | font_datas.color = color;
307 | font_datas.bkcolor = bkcolor;
308 | }
309 |
310 | void SetFontAlign(int mode)
311 | {
312 | font_datas.align = mode;
313 | font_datas.autonewline = 0;
314 | }
315 |
316 | void SetFontAutoNewLine(int width)
317 | {
318 | font_datas.autonewline = width;
319 | font_datas.align = FONT_ALIGN_LEFT;
320 | }
321 |
322 | void SetFontZ(float z)
323 | {
324 | font_datas.Z = z;
325 | }
326 |
327 | float GetFontX()
328 | {
329 | return font_datas.X;
330 | }
331 |
332 | float GetFontY()
333 | {
334 | return font_datas.Y;
335 | }
336 |
337 | void SetMonoSpace(int space)
338 | {
339 | font_datas.mono = space;
340 | }
341 |
342 | void SetExtraSpace(int space)
343 | {
344 | font_datas.extra = space;
345 | }
346 |
347 | void RegisterSpecialCharacter(char value, short fy, float scale, pkgi_texture image)
348 | {
349 | // Verify special character
350 | if (value == 0)
351 | return;
352 | if (image->texture == NULL)
353 | return;
354 | if (image->width == 0 || image->height == 0)
355 | return;
356 |
357 | // Verify value is not in use
358 | if (GetSpecialCharFromValue(value))
359 | return;
360 |
361 | // Verify room in array
362 | if (special_char_index < MAX_SPECIAL_CHARS)
363 | {
364 | special_chars[special_char_index].value = value;
365 | special_chars[special_char_index].fy = fy;
366 | special_chars[special_char_index].scale = scale;
367 | special_chars[special_char_index].image = image;
368 |
369 | special_char_index++;
370 | }
371 | }
372 |
373 | int WidthFromStr(const char * str)
374 | {
375 | return width_ttf_string(str, font_datas.sx, font_datas.sy);
376 | }
377 |
378 | int WidthFromStrMono(uint8_t * str)
379 | {
380 | int w = 0;
381 |
382 | while(*str) {
383 | w += (font_datas.sx * font_datas.mono) / font_datas.fonts[font_datas.current_font].w + font_datas.extra;
384 | str++;
385 | }
386 |
387 | return w;
388 | }
389 |
390 | int DrawCharSpecial(float x, float y, float z, const special_char* schr, uint8_t draw)
391 | {
392 | float dx = (float)font_datas.sx / (float)schr->image->width;
393 | float dy = (float)font_datas.sy / (float)schr->image->height;
394 | float min = (dx > dy ? dy : dx);
395 |
396 | dx = (min * schr->scale * schr->image->width);
397 | dy = (min * schr->scale * schr->image->height);
398 |
399 | if (!draw)
400 | return (int)dx;
401 |
402 | if (schr->fy)
403 | y += (float)schr->fy;
404 | else
405 | y += ((float)font_datas.sy - dy)/2;
406 |
407 | // Load sprite texture
408 | DrawTexture(schr->image, x, y, z, dx, dy, 0xFFFFFF00 | RGBA_A(font_datas.color));
409 |
410 | return (int)dx;
411 | }
412 |
413 | static void orbis2dDrawChar(float x, float y, const uint8_t* bitmap, int bw, int bh, int dw, int dh, uint32_t rgba)
414 | {
415 | SDL_Texture* sdl_tex = ((SDL_Texture**) bitmap)[(rgba & 0xFFFFFF00) ? 1 : 0];
416 | SDL_Rect dest = {
417 | .x = x,
418 | .y = y,
419 | .w = dw,
420 | .h = dh,
421 | };
422 |
423 | SDL_SetTextureAlphaMod(sdl_tex, RGBA_A(rgba));
424 | SDL_RenderCopy(renderer, sdl_tex, NULL, &dest);
425 | }
426 |
427 | void DrawCharMono(float x, float y, float z, uint8_t chr)
428 | {
429 | float dx = font_datas.sx, dy = font_datas.sy;
430 | float dx2 = (dx * font_datas.mono) / font_datas.fonts[font_datas.current_font].w;
431 | float dy2 = (float)(dy * font_datas.fonts[font_datas.current_font].bh) / (float)font_datas.fonts[font_datas.current_font].h;
432 |
433 | if (font_datas.number_of_fonts <= 0) return;
434 |
435 | if (chr < font_datas.fonts[font_datas.current_font].first_char) return;
436 |
437 | if (font_datas.bkcolor) {
438 | SDL_FRect rect = {
439 | .x = x,
440 | .y = y,
441 | .w = dx2,
442 | .h = dy2,
443 | };
444 |
445 | SDL_SetRenderDrawColor(renderer, RGBA_R(font_datas.bkcolor), RGBA_G(font_datas.bkcolor), RGBA_B(font_datas.bkcolor), RGBA_A(font_datas.bkcolor));
446 | SDL_RenderFillRectF(renderer, &rect);
447 | }
448 |
449 | y += (float)(font_datas.fonts[font_datas.current_font].fy[chr] * font_datas.sy) / (float)(font_datas.fonts[font_datas.current_font].h);
450 |
451 | if (chr > font_datas.fonts[font_datas.current_font].last_char) return;
452 |
453 | // Load sprite texture
454 | orbis2dDrawChar(x, y, font_datas.fonts[font_datas.current_font].rsx_text_offset + font_datas.fonts[font_datas.current_font].rsx_bytes_per_char
455 | * (chr - font_datas.fonts[font_datas.current_font].first_char), font_datas.fonts[font_datas.current_font].w,
456 | font_datas.fonts[font_datas.current_font].h, dx, dy, font_datas.color);
457 | }
458 |
459 | void DrawChar(float x, float y, float z, uint8_t chr)
460 | {
461 | special_char* schr = GetSpecialCharFromValue(chr);
462 | if (schr)
463 | {
464 | DrawCharSpecial(x, y, z, schr, 1);
465 | return;
466 | }
467 |
468 | float dx = font_datas.sx, dy = font_datas.sy;
469 | float dx2 = (dx * font_datas.fonts[font_datas.current_font].fw[chr]) / font_datas.fonts[font_datas.current_font].w;
470 | float dy2 = (float)(dy * font_datas.fonts[font_datas.current_font].bh) / (float)font_datas.fonts[font_datas.current_font].h;
471 |
472 | if (font_datas.number_of_fonts <= 0) return;
473 |
474 | if (chr < font_datas.fonts[font_datas.current_font].first_char) return;
475 |
476 | if (font_datas.bkcolor) {
477 | SDL_FRect rect = {
478 | .x = x,
479 | .y = y,
480 | .w = dx2,
481 | .h = dy2,
482 | };
483 |
484 | SDL_SetRenderDrawColor(renderer, RGBA_R(font_datas.bkcolor), RGBA_G(font_datas.bkcolor), RGBA_B(font_datas.bkcolor), RGBA_A(font_datas.bkcolor));
485 | SDL_RenderFillRectF(renderer, &rect);
486 | }
487 |
488 | y += (float)(font_datas.fonts[font_datas.current_font].fy[chr] * font_datas.sy) / (float)(font_datas.fonts[font_datas.current_font].h);
489 |
490 | if (chr > font_datas.fonts[font_datas.current_font].last_char) return;
491 |
492 | // Load sprite texture
493 | orbis2dDrawChar(x, y, font_datas.fonts[font_datas.current_font].rsx_text_offset + font_datas.fonts[font_datas.current_font].rsx_bytes_per_char
494 | * (chr - font_datas.fonts[font_datas.current_font].first_char), font_datas.fonts[font_datas.current_font].w,
495 | font_datas.fonts[font_datas.current_font].h, dx, dy, font_datas.color);
496 | }
497 |
498 | static int i_must_break_line(const char *str, float x)
499 | {
500 | int xx =0;
501 | int dx = (font_datas.sx+font_datas.extra);
502 |
503 | while(*str) {
504 | if(((uint8_t)*str) <= 32) break;
505 | xx += dx * font_datas.fonts[font_datas.current_font].fw[((uint8_t)*str)] / font_datas.fonts[font_datas.current_font].w;
506 | str++;
507 | }
508 |
509 | if(*str && (x+xx) >= font_datas.autonewline) return 1;
510 |
511 | return 0;
512 | }
513 |
514 | float DrawStringMono(float x, float y, const char *str)
515 | {
516 | float initX = x;
517 | int dx = font_datas.sx;
518 | font_datas.mono = font_datas.fonts[font_datas.current_font].w;
519 |
520 | switch (font_datas.align)
521 | {
522 | case FONT_ALIGN_SCREEN_CENTER:
523 | x= (PKGI_SCREEN_WIDTH - WidthFromStrMono((uint8_t *) str)) / 2;
524 | break;
525 |
526 | case FONT_ALIGN_RIGHT:
527 | x -= WidthFromStrMono((uint8_t *) str);
528 | break;
529 |
530 | case FONT_ALIGN_CENTER:
531 | x -= WidthFromStrMono((uint8_t *) str)/2;
532 | break;
533 |
534 | default:
535 | break;
536 | }
537 |
538 | while (*str) {
539 |
540 | if(*str == '\n') {
541 | x = initX;
542 | y += font_datas.sy * font_datas.fonts[font_datas.current_font].bh / font_datas.fonts[font_datas.current_font].h;
543 | str++;
544 | continue;
545 | } else {
546 | if(font_datas.autonewline && i_must_break_line(str, x)) {
547 | x = initX;
548 | y += font_datas.sy * font_datas.fonts[font_datas.current_font].bh / font_datas.fonts[font_datas.current_font].h;
549 | }
550 | }
551 |
552 | DrawChar(x, y, font_datas.Z, (uint8_t) *str);
553 | x += (dx * font_datas.mono) / font_datas.fonts[font_datas.current_font].w + font_datas.extra;
554 | str++;
555 | }
556 |
557 | font_datas.X = x; font_datas.Y = y;
558 |
559 | return x;
560 | }
561 |
562 | int skip_icon(int x, int y, char c)
563 | {
564 | special_char* schr = GetSpecialCharFromValue(c);
565 | if (schr)
566 | return DrawCharSpecial(x, y, font_datas.Z, schr, 0);
567 |
568 | else return 0;
569 | }
570 |
571 | int draw_icon(int x, int y, char c)
572 | {
573 | special_char* schr = GetSpecialCharFromValue(c);
574 | if (schr)
575 | return DrawCharSpecial(x, y, font_datas.Z, schr, 1);
576 |
577 | else return 0;
578 | }
579 |
580 | float DrawString(float x, float y, const char *str)
581 | {
582 | switch (font_datas.align)
583 | {
584 | case FONT_ALIGN_SCREEN_CENTER:
585 | x= (PKGI_SCREEN_WIDTH - WidthFromStr(str)) / 2;
586 | break;
587 |
588 | case FONT_ALIGN_RIGHT:
589 | x -= WidthFromStr(str);
590 | break;
591 |
592 | case FONT_ALIGN_CENTER:
593 | x -= WidthFromStr(str)/2;
594 | break;
595 |
596 | default:
597 | break;
598 | }
599 |
600 | display_ttf_string((int)x +1, (int)y +1, str, 0x00000000 | (font_datas.color & 0x000000ff), 0, font_datas.sx, font_datas.sy+4, &skip_icon);
601 |
602 | return display_ttf_string((int)x, (int)y, str, font_datas.color, font_datas.bkcolor, font_datas.sx, font_datas.sy+4, &draw_icon);
603 | }
604 |
605 | float DrawFormatString(float x, float y, char *format, ...)
606 | {
607 | char buff[4096];
608 | va_list opt;
609 |
610 | va_start(opt, format);
611 | vsprintf((void *) buff, format, opt);
612 | va_end(opt);
613 |
614 | return DrawString(x, y, buff);
615 | }
616 |
617 | float DrawFormatStringMono(float x, float y, char *format, ...)
618 | {
619 | char buff[4096];
620 | char *str = buff;
621 | va_list opt;
622 |
623 | va_start(opt, format);
624 | vsprintf((void *) buff, format, opt);
625 | va_end(opt);
626 |
627 | while (*str) {
628 | DrawCharMono(x, y, font_datas.Z, (uint8_t) *str);
629 | x += font_datas.sx;
630 | str++;
631 | }
632 |
633 | return(x);
634 | }
635 |
--------------------------------------------------------------------------------
/source/loadpng.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "pkgi.h"
8 | //#include "menu.h"
9 |
10 | #define PNG_SIGSIZE (8)
11 |
12 | typedef struct rawImage
13 | {
14 | uint32_t *datap;
15 | unsigned short width;
16 | unsigned short height;
17 | } rawImage_t __attribute__ ((aligned (16)));
18 |
19 |
20 | extern SDL_Renderer* renderer;
21 |
22 | static rawImage_t * imgCreateEmptyTexture(unsigned int w, unsigned int h)
23 | {
24 | rawImage_t *img=NULL;
25 | img=malloc(sizeof(rawImage_t));
26 | if(img!=NULL)
27 | {
28 | img->datap=malloc(w*h*4);
29 | if(img->datap==NULL)
30 | {
31 | free(img);
32 | return NULL;
33 | }
34 | img->width=w;
35 | img->height=h;
36 | }
37 | return img;
38 | }
39 |
40 | static void imgReadPngFromBuffer(png_structp png_ptr, png_bytep data, png_size_t length)
41 | {
42 | png_voidp *address = png_get_io_ptr(png_ptr);
43 | memcpy(data, (void *)*address, length);
44 | *address += length;
45 | }
46 |
47 | static rawImage_t *imgLoadPngGeneric(const void *io_ptr, png_rw_ptr read_data_fn)
48 | {
49 | png_structp png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
50 | if (png_ptr==NULL)
51 | goto error_create_read;
52 |
53 | png_infop info_ptr=png_create_info_struct(png_ptr);
54 | if (info_ptr==NULL)
55 | goto error_create_info;
56 |
57 | png_bytep *row_ptrs=NULL;
58 |
59 | if (setjmp(png_jmpbuf(png_ptr)))
60 | {
61 | png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
62 | if (row_ptrs!=NULL)
63 | free(row_ptrs);
64 |
65 | return NULL;
66 | }
67 |
68 | png_set_read_fn(png_ptr,(png_voidp)io_ptr,read_data_fn);
69 | png_set_sig_bytes(png_ptr,PNG_SIGSIZE);
70 | png_read_info(png_ptr,info_ptr);
71 |
72 | unsigned int width, height;
73 | int bit_depth, color_type;
74 |
75 | png_get_IHDR(png_ptr,info_ptr,&width,&height,&bit_depth,&color_type,NULL,NULL,NULL);
76 |
77 | if ((color_type==PNG_COLOR_TYPE_PALETTE && bit_depth<=8)
78 | || (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8)
79 | || png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)
80 | || (bit_depth==16))
81 | {
82 | png_set_expand(png_ptr);
83 | }
84 |
85 | if (bit_depth == 16)
86 | png_set_scale_16(png_ptr);
87 |
88 | if (bit_depth==8 && color_type==PNG_COLOR_TYPE_RGB)
89 | png_set_filler(png_ptr,0xFF,PNG_FILLER_AFTER);
90 |
91 | if (color_type==PNG_COLOR_TYPE_GRAY ||
92 | color_type==PNG_COLOR_TYPE_GRAY_ALPHA)
93 | png_set_gray_to_rgb(png_ptr);
94 |
95 | if (color_type==PNG_COLOR_TYPE_PALETTE) {
96 | png_set_palette_to_rgb(png_ptr);
97 | png_set_filler(png_ptr,0xFF,PNG_FILLER_AFTER);
98 | }
99 |
100 | if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth < 8)
101 | png_set_expand_gray_1_2_4_to_8(png_ptr);
102 |
103 | if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS))
104 | png_set_tRNS_to_alpha(png_ptr);
105 |
106 | if (bit_depth<8)
107 | png_set_packing(png_ptr);
108 |
109 | png_read_update_info(png_ptr, info_ptr);
110 |
111 | row_ptrs = (png_bytep *)malloc(sizeof(png_bytep)*height);
112 | if (!row_ptrs)
113 | goto error_alloc_rows;
114 |
115 | rawImage_t *texture = imgCreateEmptyTexture(width,height);
116 | if (!texture)
117 | goto error_create_tex;
118 |
119 | for (int i=0; idatap + i*width);
121 |
122 | png_read_image(png_ptr, row_ptrs);
123 |
124 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0);
125 | free(row_ptrs);
126 |
127 | return texture;
128 |
129 | error_create_tex:
130 | free(row_ptrs);
131 | error_alloc_rows:
132 | png_destroy_info_struct(png_ptr,&info_ptr);
133 | error_create_info:
134 | png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
135 | error_create_read:
136 | return NULL;
137 | }
138 |
139 | static rawImage_t *imgLoadPngFromBuffer(const void *buffer)
140 | {
141 | if(png_sig_cmp((png_byte *)buffer, 0, PNG_SIGSIZE) != 0)
142 | return NULL;
143 |
144 | uint64_t buffer_address=(uint32_t)buffer+PNG_SIGSIZE;
145 |
146 | return imgLoadPngGeneric((void *)&buffer_address, imgReadPngFromBuffer);
147 | }
148 |
149 | static rawImage_t *imgLoadPngFromFile(const char *path)
150 | {
151 | uint8_t *buf;
152 | size_t size;
153 | rawImage_t *img;
154 |
155 | size = pkgi_get_size(path);
156 | if (size < 0)
157 | return NULL;
158 |
159 | buf = malloc(size);
160 | if (!buf)
161 | return NULL;
162 |
163 | if(pkgi_load(path, buf, size) < 0)
164 | return NULL;
165 |
166 | img = imgLoadPngFromBuffer(buf);
167 | free(buf);
168 |
169 | return img;
170 | }
171 |
172 | pkgi_texture loadPngTexture(const char* path, const void* buffer)
173 | {
174 | rawImage_t* raw;
175 | pkgi_texture tex;
176 |
177 | tex = malloc(sizeof(struct pkgi_texture_s));
178 | if (!tex)
179 | return NULL;
180 |
181 | raw = path ? imgLoadPngFromFile(path) : imgLoadPngFromBuffer(buffer);
182 | if (!raw)
183 | {
184 | free(tex);
185 | return NULL;
186 | }
187 |
188 | SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(raw->datap, raw->width, raw->height, 32, 4 * raw->width,
189 | 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
190 |
191 | tex->texture = SDL_CreateTextureFromSurface(renderer, surface);
192 | tex->width = raw->width;
193 | tex->height = raw->height;
194 |
195 | SDL_FreeSurface(surface);
196 | free(raw->datap);
197 | free(raw);
198 |
199 | return tex;
200 | }
201 |
--------------------------------------------------------------------------------
/source/pkgi_aes.c:
--------------------------------------------------------------------------------
1 | #include "pkgi_aes.h"
2 | #include "pkgi_utils.h"
3 |
4 | #include
5 |
6 |
7 | void aes128_init(mbedtls_aes_context* ctx, const uint8_t* key)
8 | {
9 | mbedtls_aes_init(ctx);
10 | mbedtls_aes_setkey_enc(ctx, key, 128);
11 | }
12 |
13 | void aes128_init_dec(mbedtls_aes_context* ctx, const uint8_t* key)
14 | {
15 | mbedtls_aes_init(ctx);
16 | mbedtls_aes_setkey_dec(ctx, key, 128);
17 | /*
18 | mbedtls_aes_context enc;
19 | aes128_init(&enc, key);
20 | */
21 | }
22 |
23 | void aes128_ecb_encrypt(mbedtls_aes_context* ctx, const uint8_t* input, uint8_t* output)
24 | {
25 | mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, input, output);
26 | }
27 |
28 | void aes128_ecb_decrypt(mbedtls_aes_context* ctx, const uint8_t* input, uint8_t* output)
29 | {
30 | mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, input, output);
31 | }
32 |
33 | static void ctr_add(uint8_t* counter, uint64_t n)
34 | {
35 | for (int i=15; i>=0; i--)
36 | {
37 | n = n + counter[i];
38 | counter[i] = (uint8_t)n;
39 | n >>= 8;
40 | }
41 | }
42 |
43 | void aes128_ctr_xor(mbedtls_aes_context* context, const uint8_t* iv, uint64_t block, uint8_t* buffer, size_t size)
44 | {
45 | uint8_t tmp[16];
46 | uint8_t counter[16];
47 | for (uint32_t i=0; i<16; i++)
48 | {
49 | counter[i] = iv[i];
50 | }
51 | ctr_add(counter, block);
52 |
53 | while (size >= 16)
54 | {
55 | aes128_ecb_encrypt(context, counter, tmp);
56 | for (uint32_t i=0; i<16; i++)
57 | {
58 | *buffer++ ^= tmp[i];
59 | }
60 | ctr_add(counter, 1);
61 | size -= 16;
62 | }
63 |
64 | if (size != 0)
65 | {
66 | aes128_ecb_encrypt(context, counter, tmp);
67 | for (size_t i=0; ikey, key);
101 | memset(ctx->last, 0, 16);
102 | ctx->size = 0;
103 | }
104 |
105 | static void aes128_cmac_update(aes128_cmac_ctx* ctx, const uint8_t* buffer, uint32_t size)
106 | {
107 | if (ctx->size + size <= 16)
108 | {
109 | memcpy(ctx->block + ctx->size, buffer, size);
110 | ctx->size += size;
111 | return;
112 | }
113 |
114 | if (ctx->size != 0)
115 | {
116 | uint32_t avail = 16 - ctx->size;
117 | memcpy(ctx->block + ctx->size, buffer, avail < size ? avail : size);
118 | buffer += avail;
119 | size -= avail;
120 |
121 | aes128_cmac_process(&ctx->key, ctx->last, ctx->block, 16);
122 | }
123 |
124 | if (size >= 16)
125 | {
126 | uint32_t full = (size - 1) & ~15;
127 | aes128_cmac_process(&ctx->key, ctx->last, buffer, full);
128 | buffer += full;
129 | size -= full;
130 | }
131 |
132 | memcpy(ctx->block, buffer, size);
133 | ctx->size = size;
134 | }
135 |
136 | static void cmac_gfmul(uint8_t* block)
137 | {
138 | uint8_t carry = 0;
139 | for (int i = 15; i >= 0; i--)
140 | {
141 | uint8_t x = block[i];
142 | block[i] = (block[i] << 1) | (carry >> 7);
143 | carry = x;
144 | }
145 |
146 | block[15] ^= (carry & 0x80 ? 0x87 : 0);
147 | }
148 |
149 | static void aes128_cmac_done(aes128_cmac_ctx* ctx, uint8_t* mac)
150 | {
151 | uint8_t zero[16] = { 0 };
152 | aes128_ecb_encrypt(&ctx->key, zero, mac);
153 |
154 | cmac_gfmul(mac);
155 |
156 | if (ctx->size != 16)
157 | {
158 | cmac_gfmul(mac);
159 |
160 | ctx->block[ctx->size] = 0x80;
161 | memset(ctx->block + ctx->size + 1, 0, 16 - (ctx->size + 1));
162 | }
163 |
164 | for (size_t i = 0; i < 16; i++)
165 | {
166 | mac[i] ^= ctx->block[i];
167 | }
168 |
169 | aes128_cmac_process(&ctx->key, mac, ctx->last, 16);
170 | }
171 |
172 | void aes128_cmac(const uint8_t* key, const uint8_t* buffer, uint32_t size, uint8_t* mac)
173 | {
174 | aes128_cmac_ctx ctx;
175 | aes128_cmac_init(&ctx, key);
176 | aes128_cmac_update(&ctx, buffer, size);
177 | aes128_cmac_done(&ctx, mac);
178 | }
179 |
180 | void aes128_psp_decrypt(mbedtls_aes_context* ctx, const uint8_t* iv, uint32_t index, uint8_t* buffer, uint32_t size)
181 | {
182 | if(size % 16 != 0)
183 | return;
184 |
185 | uint8_t GCC_ALIGN(16) prev[16];
186 | uint8_t GCC_ALIGN(16) block[16];
187 |
188 | if (index == 0)
189 | {
190 | memset(prev, 0, 16);
191 | }
192 | else
193 | {
194 | memcpy(prev, iv, 12);
195 | set32le(prev + 12, index);
196 | }
197 |
198 | memcpy(block, iv, 16);
199 | set32le(block + 12, index);
200 |
201 | for (uint32_t i = 0; i < size; i += 16)
202 | {
203 | set32le(block + 12, get32le(block + 12) + 1);
204 |
205 | uint8_t out[16];
206 | aes128_ecb_decrypt(ctx, block, out);
207 |
208 | for (size_t k = 0; k < 16; k++)
209 | {
210 | *buffer++ ^= prev[k] ^ out[k];
211 | }
212 | memcpy(prev, block, 16);
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/source/pkgi_config.c:
--------------------------------------------------------------------------------
1 | #include "pkgi_config.h"
2 | #include "pkgi.h"
3 |
4 | static char* skipnonws(char* text, char* end)
5 | {
6 | while (text < end && *text != ' ' && *text != '\n' && *text != '\r')
7 | {
8 | text++;
9 | }
10 | return text;
11 | }
12 |
13 | static char* skipws(char* text, char* end)
14 | {
15 | while (text < end && (*text == ' ' || *text == '\n' || *text == '\r'))
16 | {
17 | text++;
18 | }
19 | return text;
20 | }
21 |
22 | static DbSort parse_sort(const char* value, DbSort sort)
23 | {
24 | if (pkgi_stricmp(value, "title") == 0)
25 | {
26 | return SortByTitle;
27 | }
28 | else if (pkgi_stricmp(value, "region") == 0)
29 | {
30 | return SortByRegion;
31 | }
32 | else if (pkgi_stricmp(value, "name") == 0)
33 | {
34 | return SortByName;
35 | }
36 | else if (pkgi_stricmp(value, "size") == 0)
37 | {
38 | return SortBySize;
39 | }
40 | else
41 | {
42 | return sort;
43 | }
44 | }
45 |
46 | static DbSortOrder parse_order(const char* value, DbSortOrder order)
47 | {
48 | if (pkgi_stricmp(value, "asc") == 0)
49 | {
50 | return SortAscending;
51 | }
52 | else if (pkgi_stricmp(value, "desc") == 0)
53 | {
54 | return SortDescending;
55 | }
56 | else
57 | {
58 | return order;
59 | }
60 | }
61 |
62 | static DbSortOrder parse_filter(char* value, uint32_t filter)
63 | {
64 | uint32_t result = 0;
65 |
66 | char* start = value;
67 | for (;;)
68 | {
69 | char ch = *value;
70 | if (ch == 0 || ch == ',')
71 | {
72 | *value = 0;
73 | if (pkgi_stricmp(start, "ASA") == 0)
74 | {
75 | result |= DbFilterRegionASA;
76 | }
77 | else if (pkgi_stricmp(start, "EUR") == 0)
78 | {
79 | result |= DbFilterRegionEUR;
80 | }
81 | else if (pkgi_stricmp(start, "JPN") == 0)
82 | {
83 | result |= DbFilterRegionJPN;
84 | }
85 | else if (pkgi_stricmp(start, "USA") == 0)
86 | {
87 | result |= DbFilterRegionUSA;
88 | }
89 | else
90 | {
91 | return filter;
92 | }
93 | if (ch == 0)
94 | {
95 | break;
96 | }
97 | value++;
98 | start = value;
99 | }
100 | else
101 | {
102 | value++;
103 | }
104 | }
105 |
106 | return result;
107 | }
108 |
109 | void pkgi_load_config(Config* config, char* refresh_url, uint32_t refresh_len)
110 | {
111 | refresh_url[0] = 0;
112 | config->sort = SortByName;
113 | config->order = SortAscending;
114 | config->filter = DbFilterAll;
115 | config->version_check = 1;
116 | config->install_mode_iso = 0;
117 | config->keep_pkg = 0;
118 | config->content = 0;
119 | config->allow_refresh = 0;
120 | config->storage = 0;
121 | pkgi_strncpy(config->language, 3, pkgi_get_user_language());
122 |
123 | char data[4096];
124 | char path[256];
125 | pkgi_snprintf(path, sizeof(path), "%s/config.txt", pkgi_get_config_folder());
126 | LOG("config location: %s", path);
127 |
128 | int loaded = pkgi_load(path, data, sizeof(data) - 1);
129 | if (loaded > 0)
130 | {
131 | data[loaded] = '\n';
132 |
133 | LOG("config.txt loaded, parsing");
134 | char* text = data;
135 | char* end = data + loaded + 1;
136 |
137 | if (loaded > 3 && (uint8_t)text[0] == 0xef && (uint8_t)text[1] == 0xbb && (uint8_t)text[2] == 0xbf)
138 | {
139 | text += 3;
140 | }
141 |
142 | while (text < end)
143 | {
144 | char* key = text;
145 |
146 | text = skipnonws(text, end);
147 | if (text == end) break;
148 |
149 | *text++ = 0;
150 |
151 | text = skipws(text, end);
152 | if (text == end) break;
153 |
154 | char* value = text;
155 |
156 | text = skipnonws(text, end);
157 | if (text == end) break;
158 |
159 | *text++ = 0;
160 |
161 | text = skipws(text, end);
162 |
163 | if (pkgi_stricontains(key, "url"))
164 | {
165 | for (int i = 0; i < MAX_CONTENT_TYPES; i++)
166 | if (pkgi_stricmp(key+3, pkgi_content_tag(i)) == 0)
167 | {
168 | pkgi_strncpy(refresh_url + refresh_len*i, refresh_len, value);
169 | config->allow_refresh = 1;
170 | }
171 | }
172 | else if (pkgi_stricmp(key, "sort") == 0)
173 | {
174 | config->sort = parse_sort(value, SortByName);
175 | }
176 | else if (pkgi_stricmp(key, "order") == 0)
177 | {
178 | config->order = parse_order(value, SortAscending);
179 | }
180 | else if (pkgi_stricmp(key, "filter") == 0)
181 | {
182 | config->filter = parse_filter(value, DbFilterAll);
183 | }
184 | else if (pkgi_stricmp(key, "no_version_check") == 0)
185 | {
186 | config->version_check = 0;
187 | }
188 | else if (pkgi_stricmp(key, "install_mode_iso") == 0)
189 | {
190 | config->install_mode_iso = (uint8_t)pkgi_strtoll(value);
191 | }
192 | else if (pkgi_stricmp(key, "keep_pkg") == 0)
193 | {
194 | config->keep_pkg = 1;
195 | }
196 | else if (pkgi_stricmp(key, "content") == 0)
197 | {
198 | config->content = (uint8_t)pkgi_strtoll(value);
199 | }
200 | else if (pkgi_stricmp(key, "language") == 0)
201 | {
202 | pkgi_strncpy(config->language, 2, value);
203 | }
204 | else if (pkgi_stricmp(key, "storage") == 0)
205 | {
206 | config->storage = (pkgi_stricmp("ms0", value) == 0);
207 | if (config->storage) pkgi_mkdirs("ms0:" PKGI_RAP_FOLDER);
208 | }
209 | }
210 | }
211 | else
212 | {
213 | LOG("config.txt cannot be loaded, using default values");
214 | }
215 | if (config->content == 0)
216 | {
217 | config->filter |= DbFilterAllContent;
218 | }
219 | else
220 | {
221 | config->filter |= (128 << config->content);
222 | }
223 | }
224 |
225 | const char* pkgi_content_tag(ContentType content)
226 | {
227 | switch (content)
228 | {
229 | case ContentGame: return "_games";
230 | case ContentDLC: return "_dlcs";
231 | case ContentTheme: return "_themes";
232 | case ContentPSX: return "_psx";
233 | case ContentDemo: return "_demos";
234 | case ContentUpdate: return "_updates";
235 | case ContentEmulator: return "_emulators";
236 | case ContentApp: return "_apps";
237 | default: return "";
238 | }
239 | }
240 |
241 | static const char* sort_str(DbSort sort)
242 | {
243 | switch (sort)
244 | {
245 | case SortByTitle: return "title";
246 | case SortByRegion: return "region";
247 | case SortByName: return "name";
248 | case SortBySize: return "size";
249 | default: return "";
250 | }
251 | }
252 |
253 | static const char* order_str(DbSortOrder order)
254 | {
255 | switch (order)
256 | {
257 | case SortAscending: return "asc";
258 | case SortDescending: return "desc";
259 | default: return "";
260 | }
261 | }
262 |
263 | void pkgi_save_config(const Config* config, const char* update_url, uint32_t update_len)
264 | {
265 | char data[4096];
266 | int len = 0;
267 |
268 | for (int i = 0; i < MAX_CONTENT_TYPES; i++)
269 | {
270 | const char* tmp_url = update_url + update_len*i;
271 | if (update_url && tmp_url[0] != 0)
272 | {
273 | len += pkgi_snprintf(data + len, sizeof(data) - len, "url%s %s\n", pkgi_content_tag(i), tmp_url);
274 | }
275 | }
276 | len += pkgi_snprintf(data + len, sizeof(data) - len, "language %s\n", config->language);
277 | len += pkgi_snprintf(data + len, sizeof(data) - len, "content %d\n", config->content);
278 | len += pkgi_snprintf(data + len, sizeof(data) - len, "sort %s\n", sort_str(config->sort));
279 | len += pkgi_snprintf(data + len, sizeof(data) - len, "order %s\n", order_str(config->order));
280 | len += pkgi_snprintf(data + len, sizeof(data) - len, "filter ");
281 | const char* sep = "";
282 | if (config->filter & DbFilterRegionASA)
283 | {
284 | len += pkgi_snprintf(data + len, sizeof(data) - len, "%sASA", sep);
285 | sep = ",";
286 | }
287 | if (config->filter & DbFilterRegionEUR)
288 | {
289 | len += pkgi_snprintf(data + len, sizeof(data) - len, "%sEUR", sep);
290 | sep = ",";
291 | }
292 | if (config->filter & DbFilterRegionJPN)
293 | {
294 | len += pkgi_snprintf(data + len, sizeof(data) - len, "%sJPN", sep);
295 | sep = ",";
296 | }
297 | if (config->filter & DbFilterRegionUSA)
298 | {
299 | len += pkgi_snprintf(data + len, sizeof(data) - len, "%sUSA", sep);
300 | sep = ",";
301 | }
302 | len += pkgi_snprintf(data + len, sizeof(data) - len, "\n");
303 |
304 | if (!config->version_check)
305 | {
306 | len += pkgi_snprintf(data + len, sizeof(data) - len, "no_version_check 1\n");
307 | }
308 |
309 | if (config->install_mode_iso)
310 | {
311 | len += pkgi_snprintf(data + len, sizeof(data) - len, "install_mode_iso %d\n", config->install_mode_iso);
312 | }
313 |
314 | if (config->keep_pkg)
315 | {
316 | len += pkgi_snprintf(data + len, sizeof(data) - len, "keep_pkg 1\n");
317 | }
318 |
319 | if (config->storage)
320 | {
321 | len += pkgi_snprintf(data + len, sizeof(data) - len, "storage ms0\n");
322 | }
323 |
324 | char path[256];
325 | pkgi_snprintf(path, sizeof(path), "%s/config.txt", pkgi_get_config_folder());
326 |
327 | if (pkgi_save(path, data, len))
328 | {
329 | LOG("saved config.txt");
330 | }
331 | else
332 | {
333 | LOG("cannot save config.txt");
334 | }
335 | }
--------------------------------------------------------------------------------
/source/pkgi_dialog.c:
--------------------------------------------------------------------------------
1 | #include "pkgi_dialog.h"
2 | #include "pkgi_style.h"
3 | #include "pkgi_utils.h"
4 | #include "pkgi.h"
5 |
6 | #include
7 | #include
8 |
9 | typedef enum {
10 | DialogNone,
11 | DialogMessage,
12 | DialogError,
13 | DialogProgress,
14 | DialogOkCancel,
15 | DialogDetails
16 | } DialogType;
17 |
18 | static DialogType dialog_type;
19 | static char dialog_title[256];
20 | static char dialog_text[256];
21 | static char dialog_extra[256];
22 | static char dialog_eta[256];
23 | static float dialog_progress;
24 | static int dialog_allow_close;
25 | static int dialog_cancelled;
26 | static pkgi_texture pkg_icon = NULL;
27 | static DbItem* db_item = NULL;
28 | static pkgi_dialog_callback_t dialog_callback = NULL;
29 |
30 | static int32_t dialog_width;
31 | static int32_t dialog_height;
32 | static int32_t dialog_delta;
33 |
34 | volatile int msg_dialog_action = 0;
35 |
36 |
37 | void pkgi_dialog_init(void)
38 | {
39 | dialog_type = DialogNone;
40 | dialog_allow_close = 1;
41 | }
42 |
43 | int pkgi_dialog_is_open(void)
44 | {
45 | return dialog_type != DialogNone;
46 | }
47 |
48 | int pkgi_dialog_is_cancelled(void)
49 | {
50 | return dialog_cancelled;
51 | }
52 |
53 | void pkgi_dialog_allow_close(int allow)
54 | {
55 | pkgi_dialog_lock();
56 | dialog_allow_close = allow;
57 | pkgi_dialog_unlock();
58 | }
59 |
60 | void pkgi_dialog_data_init(DialogType type, const char* title, const char* text)
61 | {
62 | pkgi_strncpy(dialog_title, sizeof(dialog_title), title);
63 | pkgi_strncpy(dialog_text, sizeof(dialog_text), text);
64 | dialog_extra[0] = 0;
65 | dialog_eta[0] = 0;
66 |
67 | dialog_cancelled = 0;
68 | dialog_type = type;
69 | dialog_delta = 1;
70 | }
71 |
72 | void pkgi_dialog_details(DbItem *item, const char* content_type)
73 | {
74 | pkgi_dialog_lock();
75 |
76 | // pkgi_snprintf(dialog_extra, sizeof(dialog_extra), PKGI_TMP_FOLDER "/%.9s.PNG", item->content + 7);
77 | // if (!pkg_icon && pkgi_get_size(dialog_extra))
78 | // pkg_icon = pkgi_load_png_file(dialog_extra);
79 |
80 | pkgi_snprintf(dialog_extra, sizeof(dialog_extra), "ID: %s\n%s: %s - RAP(%s) SHA256(%s)",
81 | item->content, _("Content"), content_type,
82 | (item->rap ? PKGI_UTF8_CHECK_ON : PKGI_UTF8_CHECK_OFF),
83 | (item->digest ? PKGI_UTF8_CHECK_ON : PKGI_UTF8_CHECK_OFF));
84 |
85 | pkgi_dialog_data_init(DialogDetails, item->name, dialog_extra);
86 | pkgi_strncpy(dialog_extra, sizeof(dialog_extra), item->description);
87 |
88 | db_item = item;
89 | pkgi_dialog_unlock();
90 | }
91 |
92 | void pkgi_dialog_message(const char* title, const char* text)
93 | {
94 | pkgi_dialog_lock();
95 | pkgi_dialog_data_init(DialogMessage, title, text);
96 | pkgi_dialog_unlock();
97 | }
98 |
99 | void pkgi_dialog_ok_cancel(const char* title, const char* text, pkgi_dialog_callback_t callback)
100 | {
101 | pkgi_dialog_lock();
102 | pkgi_dialog_data_init(DialogOkCancel, title, text);
103 | dialog_callback = callback;
104 | pkgi_dialog_unlock();
105 | }
106 |
107 | void pkgi_dialog_error(const char* text)
108 | {
109 | pkgi_dialog_lock();
110 | pkgi_dialog_data_init(DialogError, _("ERROR"), text);
111 | pkgi_dialog_unlock();
112 | }
113 |
114 | void pkgi_dialog_start_progress(const char* title, const char* text, float progress)
115 | {
116 | pkgi_dialog_lock();
117 | pkgi_dialog_data_init(DialogProgress, title, text);
118 | dialog_progress = progress;
119 | pkgi_dialog_unlock();
120 | }
121 |
122 | void pkgi_dialog_set_progress_title(const char* title)
123 | {
124 | pkgi_dialog_lock();
125 | pkgi_strncpy(dialog_title, sizeof(dialog_title), title);
126 | pkgi_dialog_unlock();
127 | }
128 |
129 | void pkgi_dialog_update_progress(const char* text, const char* extra, const char* eta, float progress)
130 | {
131 | pkgi_dialog_lock();
132 |
133 | pkgi_strncpy(dialog_text, sizeof(dialog_text), text);
134 | pkgi_strncpy(dialog_extra, sizeof(dialog_extra), extra ? extra : "");
135 | pkgi_strncpy(dialog_eta, sizeof(dialog_eta), eta ? eta : "");
136 |
137 | dialog_progress = (progress > 1.0f) ? 1.0f : progress;
138 |
139 | pkgi_dialog_unlock();
140 | }
141 |
142 | void pkgi_dialog_close(void)
143 | {
144 | dialog_delta = -1;
145 | }
146 |
147 | void pkgi_do_dialog(pkgi_input* input)
148 | {
149 | pkgi_dialog_lock();
150 |
151 | if (dialog_allow_close)
152 | {
153 | if ((dialog_type == DialogMessage || dialog_type == DialogError || dialog_type == DialogDetails) && (input->pressed & pkgi_ok_button()))
154 | {
155 | dialog_delta = -1;
156 | }
157 | else if ((dialog_type == DialogProgress || dialog_type == DialogOkCancel) && (input->pressed & pkgi_cancel_button()))
158 | {
159 | dialog_cancelled = 1;
160 | }
161 | else if (dialog_type == DialogOkCancel && (input->pressed & pkgi_ok_button()))
162 | {
163 | dialog_delta = -1;
164 | if (dialog_callback)
165 | {
166 | dialog_callback(MDIALOG_OK);
167 | dialog_callback = NULL;
168 | }
169 | }
170 | /*
171 | else if (dialog_type == DialogDetails && (input->pressed & PKGI_BUTTON_S))
172 | {
173 | int updates = pkgi_db_load_xml_updates(db_item->content, db_item->name);
174 | if (updates < 0)
175 | {
176 | pkgi_strncpy(dialog_text, sizeof(dialog_text), _("Failed to download the update list"));
177 | dialog_type = DialogError;
178 | }
179 | else
180 | {
181 | pkgi_snprintf(dialog_text, sizeof(dialog_text), "%d %s", updates, _("update(s) loaded"));
182 | dialog_type = DialogMessage;
183 | }
184 | }
185 | */
186 | }
187 |
188 | if (dialog_delta != 0)
189 | {
190 | dialog_width += dialog_delta * (int32_t)(input->delta * PKGI_ANIMATION_SPEED / 1000);
191 | dialog_height += dialog_delta * (int32_t)(input->delta * PKGI_ANIMATION_SPEED / 500);
192 |
193 | if (dialog_delta < 0 && (dialog_width <= 0 || dialog_height <= 0))
194 | {
195 | dialog_type = DialogNone;
196 | dialog_text[0] = 0;
197 | dialog_extra[0] = 0;
198 | dialog_eta[0] = 0;
199 |
200 | dialog_width = 0;
201 | dialog_height = 0;
202 | dialog_delta = 0;
203 |
204 | if (pkg_icon)
205 | {
206 | pkgi_free_texture(pkg_icon);
207 | pkg_icon = NULL;
208 | }
209 |
210 | pkgi_dialog_unlock();
211 | return;
212 | }
213 | else if (dialog_delta > 0)
214 | {
215 | if (dialog_width >= PKGI_DIALOG_WIDTH && dialog_height >= PKGI_DIALOG_HEIGHT)
216 | {
217 | dialog_delta = 0;
218 | }
219 | dialog_width = min32(dialog_width, PKGI_DIALOG_WIDTH);
220 | dialog_height = min32(dialog_height, PKGI_DIALOG_HEIGHT);
221 | }
222 | }
223 |
224 | DialogType local_type = dialog_type;
225 | char local_title[256];
226 | char local_text[256];
227 | char local_extra[256];
228 | char local_eta[256];
229 | float local_progress = dialog_progress;
230 | int local_allow_close = dialog_allow_close;
231 | int32_t local_width = dialog_width;
232 | int32_t local_height = dialog_height;
233 |
234 | pkgi_strncpy(local_title, sizeof(local_title), dialog_title);
235 | pkgi_strncpy(local_text, sizeof(local_text), dialog_text);
236 | pkgi_strncpy(local_extra, sizeof(local_extra), dialog_extra);
237 | pkgi_strncpy(local_eta, sizeof(local_eta), dialog_eta);
238 |
239 | pkgi_dialog_unlock();
240 |
241 | if (local_width != 0 && local_height != 0)
242 | {
243 | pkgi_draw_fill_rect_z((PKGI_SCREEN_WIDTH - local_width) / 2, (PKGI_SCREEN_HEIGHT - local_height) / 2, PKGI_MENU_Z, local_width, local_height, PKGI_COLOR_MENU_BACKGROUND);
244 | pkgi_draw_rect_z((PKGI_SCREEN_WIDTH - local_width) / 2, (PKGI_SCREEN_HEIGHT - local_height) / 2, PKGI_MENU_Z, local_width, local_height, PKGI_COLOR_MENU_BORDER);
245 | }
246 |
247 | if (local_width != PKGI_DIALOG_WIDTH || local_height != PKGI_DIALOG_HEIGHT)
248 | {
249 | return;
250 | }
251 |
252 | int font_height = pkgi_text_height("M");
253 |
254 | int w = PKGI_SCREEN_WIDTH - 2 * PKGI_DIALOG_HMARGIN;
255 | int h = PKGI_SCREEN_HEIGHT - 2 * PKGI_DIALOG_VMARGIN;
256 |
257 | if (local_title[0])
258 | {
259 | uint32_t color;
260 | if (local_type == DialogError)
261 | {
262 | color = PKGI_COLOR_TEXT_ERROR;
263 | }
264 | else
265 | {
266 | color = PKGI_COLOR_TEXT_DIALOG;
267 | }
268 |
269 | int width = pkgi_text_width_ttf(local_title);
270 | if (width > w + 2 * PKGI_DIALOG_PADDING)
271 | {
272 | pkgi_clip_set(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_DIALOG_VMARGIN + font_height, w - 2 * PKGI_DIALOG_PADDING, h - 2 * PKGI_DIALOG_PADDING);
273 | pkgi_draw_text_ttf(0, 0, PKGI_DIALOG_TEXT_Z, color, local_title);
274 | pkgi_clip_remove();
275 | }
276 | else
277 | {
278 | pkgi_draw_text_ttf((PKGI_SCREEN_WIDTH - width) / 2, PKGI_DIALOG_VMARGIN + font_height, PKGI_DIALOG_TEXT_Z, color, local_title);
279 | }
280 | }
281 |
282 | if (local_type == DialogProgress)
283 | {
284 | int extraw = pkgi_text_width(local_extra);
285 |
286 | int availw = PKGI_SCREEN_WIDTH - 2 * (PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING) - (extraw ? extraw + 10 : 10);
287 | pkgi_clip_set(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_SCREEN_HEIGHT / 2 - font_height - PKGI_DIALOG_PROCESS_BAR_PADDING, availw, font_height + 2);
288 | pkgi_draw_text_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_SCREEN_HEIGHT / 2 - font_height - PKGI_DIALOG_PROCESS_BAR_PADDING, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, local_text);
289 | pkgi_clip_remove();
290 |
291 | if (local_extra[0])
292 | {
293 | pkgi_draw_text_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_SCREEN_HEIGHT / 2 + PKGI_DIALOG_PROCESS_BAR_HEIGHT + PKGI_DIALOG_PROCESS_BAR_PADDING, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, local_extra);
294 | }
295 |
296 | if (local_progress < 0)
297 | {
298 | uint32_t avail = w - 2 * PKGI_DIALOG_PADDING;
299 |
300 | uint32_t start = (pkgi_time_msec() / 2) % (avail + PKGI_DIALOG_PROCESS_BAR_CHUNK);
301 | uint32_t end = start < PKGI_DIALOG_PROCESS_BAR_CHUNK ? start : start + PKGI_DIALOG_PROCESS_BAR_CHUNK > avail + PKGI_DIALOG_PROCESS_BAR_CHUNK ? avail : start;
302 | start = start < PKGI_DIALOG_PROCESS_BAR_CHUNK ? 0 : start - PKGI_DIALOG_PROCESS_BAR_CHUNK;
303 |
304 | pkgi_draw_fill_rect_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_SCREEN_HEIGHT / 2, PKGI_MENU_Z, avail, PKGI_DIALOG_PROCESS_BAR_HEIGHT, PKGI_COLOR_PROGRESS_BACKGROUND);
305 | pkgi_draw_fill_rect_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING + start, PKGI_SCREEN_HEIGHT / 2, PKGI_MENU_Z, end - start, PKGI_DIALOG_PROCESS_BAR_HEIGHT, PKGI_COLOR_PROGRESS_BAR);
306 | }
307 | else
308 | {
309 | pkgi_draw_fill_rect_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_SCREEN_HEIGHT / 2, PKGI_MENU_Z, w - 2 * PKGI_DIALOG_PADDING, PKGI_DIALOG_PROCESS_BAR_HEIGHT, PKGI_COLOR_PROGRESS_BACKGROUND);
310 | pkgi_draw_fill_rect_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_SCREEN_HEIGHT / 2, PKGI_MENU_Z, (int)((w - 2 * PKGI_DIALOG_PADDING) * local_progress), PKGI_DIALOG_PROCESS_BAR_HEIGHT, PKGI_COLOR_PROGRESS_BAR);
311 |
312 | char percent[256];
313 | pkgi_snprintf(percent, sizeof(percent), "%.0f%%", local_progress * 100.f);
314 |
315 | int percentw = pkgi_text_width(percent);
316 | pkgi_draw_text_z((PKGI_SCREEN_WIDTH - percentw) / 2, PKGI_SCREEN_HEIGHT / 2 + PKGI_DIALOG_PROCESS_BAR_HEIGHT + PKGI_DIALOG_PROCESS_BAR_PADDING, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, percent);
317 | }
318 |
319 | if (local_eta[0])
320 | {
321 | pkgi_draw_text_z(PKGI_DIALOG_HMARGIN + w - (PKGI_DIALOG_PADDING + pkgi_text_width(local_eta)), PKGI_SCREEN_HEIGHT / 2 + PKGI_DIALOG_PROCESS_BAR_HEIGHT + PKGI_DIALOG_PROCESS_BAR_PADDING, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, local_eta);
322 | }
323 |
324 | if (local_allow_close)
325 | {
326 | char text[256];
327 | pkgi_snprintf(text, sizeof(text), _("press %s to cancel"), pkgi_ok_button() == PKGI_BUTTON_X ? PKGI_UTF8_O : PKGI_UTF8_X);
328 | pkgi_draw_text_z((PKGI_SCREEN_WIDTH - pkgi_text_width(text)) / 2, PKGI_DIALOG_VMARGIN + h - 2 * font_height, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, text);
329 | }
330 | }
331 | else if (local_type == DialogDetails)
332 | {
333 | // pkgi_draw_texture_z(pkg_icon, PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING + 425, PKGI_DIALOG_VMARGIN + PKGI_DIALOG_PADDING + 25, PKGI_DIALOG_TEXT_Z, 0.5);
334 |
335 | pkgi_draw_text_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_DIALOG_VMARGIN + PKGI_DIALOG_PADDING + font_height*2, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, local_text);
336 | pkgi_draw_text_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_DIALOG_VMARGIN + PKGI_DIALOG_PADDING + font_height*4, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, local_extra);
337 |
338 | if (local_allow_close)
339 | {
340 | char text[256];
341 | pkgi_snprintf(text, sizeof(text), _("press %s to close"), pkgi_ok_button() == PKGI_BUTTON_X ? PKGI_UTF8_X : PKGI_UTF8_O, PKGI_UTF8_S);
342 | pkgi_draw_text_z((PKGI_SCREEN_WIDTH - pkgi_text_width(text)) / 2, PKGI_DIALOG_VMARGIN + h - 2 * font_height, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, text);
343 | }
344 | }
345 | else
346 | {
347 | uint32_t color;
348 | if (local_type == DialogMessage || local_type == DialogOkCancel)
349 | {
350 | color = PKGI_COLOR_TEXT_DIALOG;
351 | }
352 | else // local_type == DialogError
353 | {
354 | color = PKGI_COLOR_TEXT_ERROR;
355 | }
356 |
357 | int textw = pkgi_text_width(local_text);
358 | if (textw > w + 2 * PKGI_DIALOG_PADDING)
359 | {
360 | pkgi_clip_set(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_DIALOG_VMARGIN + PKGI_DIALOG_PADDING, w - 2 * PKGI_DIALOG_PADDING, h - 2 * PKGI_DIALOG_PADDING);
361 | pkgi_draw_text_z(PKGI_DIALOG_HMARGIN + PKGI_DIALOG_PADDING, PKGI_SCREEN_HEIGHT / 2 - font_height / 2, PKGI_DIALOG_TEXT_Z, color, local_text);
362 | pkgi_clip_remove();
363 | }
364 | else
365 | {
366 | pkgi_draw_text_z((PKGI_SCREEN_WIDTH - textw) / 2, PKGI_SCREEN_HEIGHT / 2 - font_height / 2, PKGI_DIALOG_TEXT_Z, color, local_text);
367 | }
368 |
369 | if (local_allow_close)
370 | {
371 | char text[256];
372 | if (local_type == DialogOkCancel)
373 | pkgi_snprintf(text, sizeof(text), "%s %s %s %s", pkgi_ok_button() == PKGI_BUTTON_X ? PKGI_UTF8_X : PKGI_UTF8_O, _("Enter"), pkgi_cancel_button() == PKGI_BUTTON_O ? PKGI_UTF8_O : PKGI_UTF8_X, _("Back"));
374 | else
375 | pkgi_snprintf(text, sizeof(text), _("press %s to close"), pkgi_ok_button() == PKGI_BUTTON_X ? PKGI_UTF8_X : PKGI_UTF8_O);
376 |
377 | pkgi_draw_text_z((PKGI_SCREEN_WIDTH - pkgi_text_width(text)) / 2, PKGI_DIALOG_VMARGIN + h - 2 * font_height, PKGI_DIALOG_TEXT_Z, PKGI_COLOR_TEXT_DIALOG, text);
378 | }
379 | }
380 | }
381 | /*
382 | void msg_dialog_event(msgButton button, void *userdata)
383 | {
384 | switch(button) {
385 |
386 | case MSG_DIALOG_BTN_YES:
387 | msg_dialog_action = 1;
388 | break;
389 | case MSG_DIALOG_BTN_NO:
390 | case MSG_DIALOG_BTN_ESCAPE:
391 | case MSG_DIALOG_BTN_NONE:
392 | msg_dialog_action = 2;
393 | break;
394 | default:
395 | break;
396 | }
397 | }
398 |
399 | int pkgi_msg_dialog(int tdialog, const char * str)
400 | {
401 | msg_dialog_action = 0;
402 |
403 | msgType mtype = MSG_DIALOG_NORMAL;
404 | mtype |= (tdialog ? (MSG_DIALOG_BTN_TYPE_YESNO | MSG_DIALOG_DEFAULT_CURSOR_NO) : MSG_DIALOG_BTN_TYPE_OK);
405 |
406 | msgDialogOpen2(mtype, str, msg_dialog_event, NULL, NULL);
407 |
408 | while(!msg_dialog_action)
409 | {
410 | pkgi_swap();
411 | }
412 |
413 | msgDialogAbort();
414 | pkgi_sleep(100);
415 |
416 | return (msg_dialog_action == 1);
417 | }
418 | */
--------------------------------------------------------------------------------
/source/pkgi_download.c:
--------------------------------------------------------------------------------
1 | #include "pkgi_download.h"
2 | #include "pkgi_dialog.h"
3 | #include "pkgi.h"
4 | #include "pkgi_utils.h"
5 | #include "pkgi_sha256.h"
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 |
15 | static char root[256];
16 | static char resume_file[256];
17 |
18 | static pkgi_http* http;
19 | static const DbItem* db_item;
20 | static int download_resume;
21 |
22 | static uint64_t initial_offset; // where http download resumes
23 | static uint64_t download_offset; // pkg absolute offset
24 | static uint64_t download_size; // pkg total size (from http request)
25 |
26 | static mbedtls_sha256_context sha;
27 |
28 | static void* item_file; // current file handle
29 | static char item_name[256]; // current file name
30 | static char item_path[256]; // current file path
31 |
32 | // pkg header
33 | static uint64_t total_size;
34 |
35 | // UI stuff
36 | static char dialog_extra[256];
37 | static char dialog_eta[256];
38 | static uint32_t info_start;
39 | static uint32_t info_update;
40 |
41 |
42 | static void calculate_eta(uint32_t speed)
43 | {
44 | uint64_t seconds = (total_size - download_offset) / speed;
45 | if (seconds < 60)
46 | {
47 | pkgi_snprintf(dialog_eta, sizeof(dialog_eta), "%s: %us", _("ETA"), (uint32_t)seconds);
48 | }
49 | else if (seconds < 3600)
50 | {
51 | pkgi_snprintf(dialog_eta, sizeof(dialog_eta), "%s: %um %02us", _("ETA"), (uint32_t)(seconds / 60), (uint32_t)(seconds % 60));
52 | }
53 | else
54 | {
55 | uint32_t hours = (uint32_t)(seconds / 3600);
56 | uint32_t minutes = (uint32_t)((seconds - hours * 3600) / 60);
57 | pkgi_snprintf(dialog_eta, sizeof(dialog_eta), "%s: %uh %02um", _("ETA"), hours, minutes);
58 | }
59 | }
60 |
61 | /* follow the CURLOPT_XFERINFOFUNCTION callback definition */
62 | static int update_progress(void *p, int64_t dltotal, int64_t dlnow, int64_t ultotal, int64_t ulnow)
63 | {
64 | uint32_t info_now = pkgi_time_msec();
65 |
66 | if (info_now >= info_update)
67 | {
68 | char text[256];
69 | pkgi_snprintf(text, sizeof(text), "%s", item_name);
70 |
71 | if (download_resume)
72 | {
73 | // if resuming download, then there is no "download speed"
74 | dialog_extra[0] = 0;
75 | }
76 | else
77 | {
78 | // report download speed
79 | uint32_t speed = (uint32_t)(((download_offset - initial_offset) * 1000) / (info_now - info_start));
80 | if (speed > 10 * 1000 * 1024)
81 | {
82 | pkgi_snprintf(dialog_extra, sizeof(dialog_extra), "%u %s/s", speed / 1024 / 1024, _("MB"));
83 | }
84 | else if (speed > 1000)
85 | {
86 | pkgi_snprintf(dialog_extra, sizeof(dialog_extra), "%u %s/s", speed / 1024, _("KB"));
87 | }
88 |
89 | if (speed != 0)
90 | {
91 | // report ETA
92 | calculate_eta(speed);
93 | }
94 | }
95 |
96 | float percent = total_size ? (float)((double)download_offset / total_size) : 0.f;
97 |
98 | pkgi_dialog_update_progress(text, dialog_extra, dialog_eta, percent);
99 | info_update = info_now + 500;
100 | progress_screen_refresh();
101 | }
102 |
103 | return (pkgi_dialog_is_cancelled());
104 | }
105 |
106 | static size_t write_verify_data(void *buffer, size_t size, size_t nmemb, void *stream)
107 | {
108 | size_t realsize = size * nmemb;
109 |
110 | if (pkgi_write(item_file, buffer, realsize))
111 | {
112 | download_offset += realsize;
113 | mbedtls_sha256_update(&sha, buffer, realsize);
114 | return (realsize);
115 | }
116 |
117 | return 0;
118 | }
119 |
120 | static void download_start(void)
121 | {
122 | LOG("resuming pkg download from %llu offset", initial_offset);
123 | download_offset = initial_offset;
124 | download_resume = 0;
125 | info_update = pkgi_time_msec() + 1000;
126 | pkgi_dialog_set_progress_title(_("Downloading..."));
127 | }
128 |
129 | static int download_data(void)
130 | {
131 | if (!http)
132 | {
133 | LOG("requesting %s @ %llu", db_item->url, initial_offset);
134 | http = pkgi_http_get(db_item->url, db_item->content, initial_offset);
135 | if (!http)
136 | {
137 | pkgi_dialog_error(_("Could not send HTTP request"));
138 | return 0;
139 | }
140 |
141 | int64_t http_length;
142 | if (!pkgi_http_response_length(http, &http_length))
143 | {
144 | pkgi_dialog_error(_("HTTP request failed"));
145 | return 0;
146 | }
147 | if (http_length < 0)
148 | {
149 | pkgi_dialog_error(_("HTTP response has unknown length"));
150 | return 0;
151 | }
152 |
153 | download_size = http_length;
154 | total_size = initial_offset + download_size;
155 |
156 | if (!pkgi_check_free_space(http_length))
157 | {
158 | LOG("error! out of space");
159 | return 0;
160 | }
161 |
162 | LOG("http response length = %lld, total pkg size = %llu", http_length, total_size);
163 | info_start = pkgi_time_msec();
164 | info_update = pkgi_time_msec() + 500;
165 | }
166 |
167 | if (!pkgi_http_read(http, &write_verify_data, &update_progress))
168 | {
169 | pkgi_save(resume_file, &sha, sizeof(sha));
170 |
171 | if (!pkgi_dialog_is_cancelled())
172 | {
173 | pkgi_dialog_error(_("HTTP download error"));
174 | }
175 | return 0;
176 | }
177 |
178 | return 1;
179 | }
180 |
181 | // this includes creating of all the parent folders necessary to actually create file
182 | static int create_file(void)
183 | {
184 | char folder[256];
185 | pkgi_strncpy(folder, sizeof(folder), item_path);
186 | char* last = pkgi_strrchr(folder, '/');
187 | *last = 0;
188 |
189 | if (!pkgi_mkdirs(folder))
190 | {
191 | char error[256];
192 | pkgi_snprintf(error, sizeof(error), "%s %s", _("cannot create folder"), folder);
193 | pkgi_dialog_error(error);
194 | return 0;
195 | }
196 |
197 | LOG("creating %s file", item_name);
198 | item_file = pkgi_create(item_path);
199 | if (!item_file)
200 | {
201 | char error[256];
202 | pkgi_snprintf(error, sizeof(error), "%s %s", _("cannot create file"), item_name);
203 | pkgi_dialog_error(error);
204 | return 0;
205 | }
206 |
207 | return 1;
208 | }
209 |
210 | static int resume_partial_file(void)
211 | {
212 | LOG("resuming %s file", item_name);
213 | item_file = pkgi_append(item_path);
214 | if (!item_file)
215 | {
216 | char error[256];
217 | pkgi_snprintf(error, sizeof(error), "%s %s", _("cannot resume file"), item_name);
218 | pkgi_dialog_error(error);
219 | return 0;
220 | }
221 |
222 | return 1;
223 | }
224 |
225 | static int download_pkg_file(void)
226 | {
227 | int result = 0;
228 |
229 | pkgi_strncpy(item_name, sizeof(item_name), root);
230 | pkgi_snprintf(item_path, sizeof(item_path), "%s%s/%s", pkgi_get_storage_device(), pkgi_get_temp_folder(), root);
231 | LOG("downloading %s", item_name);
232 |
233 | if (download_resume)
234 | {
235 | initial_offset = pkgi_get_size(item_path);
236 | if (!resume_partial_file()) goto bail;
237 | download_start();
238 | }
239 | else
240 | {
241 | if (!create_file()) goto bail;
242 | }
243 |
244 | if (!download_data()) goto bail;
245 |
246 | LOG("%s downloaded", item_path);
247 | result = 1;
248 |
249 | bail:
250 | if (item_file != NULL)
251 | {
252 | pkgi_close(item_file);
253 | item_file = NULL;
254 | }
255 | return result;
256 | }
257 |
258 | static int check_integrity(const uint8_t* digest)
259 | {
260 | if (!digest)
261 | {
262 | LOG("no integrity provided, skipping check");
263 | return 1;
264 | }
265 |
266 | uint8_t check[SHA256_DIGEST_SIZE];
267 | mbedtls_sha256_finish(&sha, check);
268 |
269 | LOG("checking integrity of pkg");
270 | if (!pkgi_memequ(digest, check, SHA256_DIGEST_SIZE))
271 | {
272 | LOG("pkg integrity is wrong, removing %s & resume data", item_path);
273 |
274 | pkgi_rm(item_path);
275 | pkgi_rm(resume_file);
276 |
277 | pkgi_dialog_error(_("pkg integrity failed, try downloading again"));
278 | return 0;
279 | }
280 |
281 | LOG("pkg integrity check succeeded");
282 | return 1;
283 | }
284 |
285 | static int create_rap(const char* contentid, const uint8_t* rap)
286 | {
287 | LOG("creating %s.rap", contentid);
288 | pkgi_dialog_update_progress(_("Creating RAP file"), NULL, NULL, 1.f);
289 |
290 | char path[256];
291 | pkgi_snprintf(path, sizeof(path), "%s%s/%s.rap", pkgi_get_storage_device(), PKGI_RAP_FOLDER, contentid);
292 |
293 | if (!pkgi_save(path, rap, PKGI_RAP_SIZE))
294 | {
295 | char error[256];
296 | pkgi_snprintf(error, sizeof(error), "%s %s.rap", _("Cannot save"), contentid);
297 | pkgi_dialog_error(error);
298 | return 0;
299 | }
300 |
301 | LOG("RAP file created");
302 | return 1;
303 | }
304 |
305 | static int is_zip(const char* filename)
306 | {
307 | const char* extension = pkgi_strrchr(filename, '.');
308 | return extension && (pkgi_stricmp(extension, ".zip") == 0);
309 | }
310 |
311 | int pkgi_download(const DbItem* item)
312 | {
313 | int result = 0;
314 |
315 | pkgi_snprintf(root, sizeof(root), "%s.%s", item->content, is_zip(item->url) ? "zip" : "pkg");
316 | LOG("package installation file: %s", root);
317 |
318 | pkgi_snprintf(resume_file, sizeof(resume_file), "%s%s/%s.resume", pkgi_get_storage_device(), pkgi_get_temp_folder(), item->content);
319 | if (pkgi_load(resume_file, &sha, sizeof(sha)) == sizeof(sha))
320 | {
321 | LOG("resume file exists, trying to resume");
322 | pkgi_dialog_set_progress_title(_("Resuming..."));
323 | download_resume = 1;
324 | }
325 | else
326 | {
327 | if (item->type == ContentLocal)
328 | {
329 | pkgi_strncpy(root, sizeof(root), item->url);
330 | pkgi_snprintf(item_path, sizeof(item_path), "%s%s/%s", pkgi_get_storage_device(), pkgi_get_temp_folder(), root);
331 | return 1;
332 | }
333 |
334 | LOG("cannot load resume file, starting download from scratch");
335 | pkgi_dialog_set_progress_title(_("Downloading..."));
336 | download_resume = 0;
337 | mbedtls_sha256_init(&sha);
338 | mbedtls_sha256_starts(&sha, 0);
339 | }
340 |
341 | http = NULL;
342 | item_file = NULL;
343 | total_size = 0;
344 | download_size = 0;
345 | download_offset = 0;
346 | initial_offset = 0;
347 | db_item = item;
348 |
349 | dialog_extra[0] = 0;
350 | dialog_eta[0] = 0;
351 | info_start = pkgi_time_msec();
352 | info_update = info_start + 1000;
353 |
354 | if (item->rap)
355 | {
356 | if (!create_rap(item->content, item->rap)) goto finish;
357 | }
358 |
359 | if (!download_pkg_file()) goto finish;
360 | if (!check_integrity(item->digest)) goto finish;
361 |
362 | pkgi_rm(resume_file);
363 | result = 1;
364 |
365 | finish:
366 | if (http)
367 | {
368 | pkgi_http_close(http);
369 | }
370 |
371 | return result;
372 | }
373 |
374 | void update_install_progress(const char *filename, int64_t progress)
375 | {
376 | download_offset = progress;
377 | if (filename) pkgi_strncpy(item_name, sizeof(item_name), filename);
378 | update_progress(NULL, 0, 0, 0, 0);
379 | }
380 |
381 | int pkgi_install(int iso_mode, int remove_pkg)
382 | {
383 | int result;
384 |
385 | download_size = pkgi_get_size(item_path);
386 | info_start = pkgi_time_msec();
387 |
388 | initial_offset = 0;
389 | download_offset = 0;
390 | total_size = download_size;
391 |
392 | // check if it's a zip file
393 | if (is_zip(item_path))
394 | result = extract_zip(item_path);
395 | else
396 | result = iso_mode ? convert_psp_pkg_iso(item_path, (iso_mode == 2)) : install_psp_pkg(item_path);
397 |
398 | if (result && remove_pkg)
399 | {
400 | pkgi_rm(item_path);
401 | }
402 |
403 | return (result);
404 | }
405 |
--------------------------------------------------------------------------------
/source/pkgi_menu.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "pkgi_menu.h"
3 | #include "pkgi_config.h"
4 | #include "pkgi_style.h"
5 | #include "pkgi.h"
6 |
7 | static int menu_search_clear;
8 |
9 | static Config menu_config;
10 | static uint32_t menu_selected;
11 | static int menu_allow_refresh;
12 |
13 | static MenuResult menu_result;
14 |
15 | static int32_t menu_width;
16 | static int32_t menu_delta;
17 | static int32_t pkgi_menu_width = 0;
18 |
19 | typedef enum {
20 | MenuSearch,
21 | MenuSearchClear,
22 | MenuText,
23 | MenuSort,
24 | MenuFilter,
25 | MenuRefresh,
26 | MenuMode,
27 | MenuUpdate,
28 | MenuKeepPkg,
29 | MenuContent
30 | } MenuType;
31 |
32 | typedef struct {
33 | MenuType type;
34 | const char* text;
35 | uint32_t value;
36 | } MenuEntry;
37 |
38 | static MenuEntry menu_entries[] =
39 | {
40 | { MenuSearch, "Search...", 0 },
41 | { MenuSearchClear, PKGI_UTF8_CLEAR " clear", 0 },
42 |
43 | { MenuText, "Sort by:", 0 },
44 | { MenuSort, "Title", SortByTitle },
45 | { MenuSort, "Region", SortByRegion },
46 | { MenuSort, "Name", SortByName },
47 | { MenuSort, "Size", SortBySize },
48 |
49 | { MenuText, "Content:", 0 },
50 | { MenuContent, "All", 0 },
51 |
52 | { MenuRefresh, "Refresh...", 0 },
53 |
54 | { MenuText, "Regions:", 0 },
55 | { MenuFilter, "Asia", DbFilterRegionASA },
56 | { MenuFilter, "Europe", DbFilterRegionEUR },
57 | { MenuFilter, "Japan", DbFilterRegionJPN },
58 | { MenuFilter, "USA", DbFilterRegionUSA },
59 |
60 | { MenuText, "Options:", 0 },
61 | { MenuMode, "Digital", 0 },
62 | { MenuKeepPkg, "Keep PKGs", 1 },
63 | { MenuUpdate, "Updates", 1 },
64 | };
65 |
66 | static MenuEntry content_entries[] =
67 | {
68 | { MenuFilter, "All", DbFilterAllContent },
69 | { MenuFilter, "Games", DbFilterContentGame },
70 | { MenuFilter, "DLCs", DbFilterContentDLC },
71 | { MenuFilter, "Themes", DbFilterContentTheme },
72 | { MenuFilter, "PSX", DbFilterContentPSX },
73 | { MenuFilter, "Demos", DbFilterContentDemo },
74 | { MenuFilter, "Updates", DbFilterContentUpdate },
75 | { MenuFilter, "Emulators", DbFilterContentEmulator },
76 | { MenuFilter, "Apps", DbFilterContentApp },
77 | { MenuFilter, "Local PKGs", DbFilterContentLocal }
78 | };
79 |
80 | static MenuEntry format_entries[] =
81 | {
82 | { MenuMode, "Digital", 0 },
83 | { MenuMode, "ISO", 1 },
84 | { MenuMode, "CSO", 2 }
85 | };
86 |
87 | int pkgi_menu_is_open(void)
88 | {
89 | return menu_width != 0;
90 | }
91 |
92 | MenuResult pkgi_menu_result()
93 | {
94 | return menu_result;
95 | }
96 |
97 | void pkgi_menu_get(Config* config)
98 | {
99 | *config = menu_config;
100 | }
101 |
102 | static void set_max_width(const MenuEntry* entries, int size)
103 | {
104 | for (int j, i = 0; i < size; i++)
105 | {
106 | if ((j = pkgi_text_width(entries[i].text) + PKGI_MENU_LEFT_PADDING*2) > pkgi_menu_width/2)
107 | pkgi_menu_width = j*2;
108 | }
109 | }
110 |
111 | void pkgi_menu_start(int search_clear, const Config* config)
112 | {
113 | menu_search_clear = search_clear;
114 | menu_width = 1;
115 | menu_delta = 1;
116 | menu_config = *config;
117 | menu_allow_refresh = config->allow_refresh;
118 |
119 | menu_entries[0].text = _("Search...");
120 | menu_entries[2].text = _("Sort by:");
121 | menu_entries[3].text = _("Title");
122 | menu_entries[4].text = _("Region");
123 | menu_entries[5].text = _("Name");
124 | menu_entries[6].text = _("Size");
125 | menu_entries[7].text = _("Content:");
126 | menu_entries[8].text = _("All");
127 | menu_entries[9].text = _("Refresh...");
128 | menu_entries[10].text = _("Regions:");
129 | menu_entries[11].text = _("Asia");
130 | menu_entries[12].text = _("Europe");
131 | menu_entries[13].text = _("Japan");
132 | menu_entries[14].text = _("USA");
133 | menu_entries[15].text = _("Options:");
134 | menu_entries[16].text = _("ISO");
135 | menu_entries[17].text = _("Keep PKGs");
136 | menu_entries[18].text = _("Updates");
137 |
138 | content_entries[0].text = _("All");
139 | content_entries[1].text = _("Games");
140 | content_entries[2].text = _("DLCs");
141 | content_entries[3].text = _("Themes");
142 | content_entries[4].text = _("PSX");
143 | content_entries[5].text = _("Demos");
144 | content_entries[6].text = _("Updates");
145 | content_entries[7].text = _("Emulators");
146 | content_entries[8].text = _("Apps");
147 | content_entries[9].text = _("Local PKGs");
148 |
149 | format_entries[0].text = _("Digital");
150 | format_entries[1].text = _("ISO");
151 | format_entries[2].text = _("CSO");
152 |
153 | if (pkgi_menu_width)
154 | return;
155 |
156 | pkgi_menu_width = PKGI_MENU_WIDTH;
157 | set_max_width(menu_entries, PKGI_COUNTOF(menu_entries));
158 | set_max_width(content_entries, PKGI_COUNTOF(content_entries));
159 | }
160 |
161 | int pkgi_do_menu(pkgi_input* input)
162 | {
163 | if (menu_delta != 0)
164 | {
165 | menu_width += menu_delta * (int32_t)(input->delta * PKGI_ANIMATION_SPEED/ 3000);
166 |
167 | if (menu_delta < 0 && menu_width <= 0)
168 | {
169 | menu_width = 0;
170 | menu_delta = 0;
171 | return 0;
172 | }
173 | else if (menu_delta > 0 && menu_width >= pkgi_menu_width)
174 | {
175 | menu_width = pkgi_menu_width;
176 | menu_delta = 0;
177 | }
178 | }
179 |
180 | if (menu_width != 0)
181 | {
182 | pkgi_draw_fill_rect_z(PKGI_SCREEN_WIDTH - (menu_width + PKGI_MAIN_HMARGIN), PKGI_MAIN_VMARGIN, PKGI_MENU_Z, menu_width, PKGI_MENU_HEIGHT, PKGI_COLOR_MENU_BACKGROUND);
183 | pkgi_draw_rect_z(PKGI_SCREEN_WIDTH - (menu_width + PKGI_MAIN_HMARGIN), PKGI_MAIN_VMARGIN, PKGI_MENU_Z, menu_width, PKGI_MENU_HEIGHT, PKGI_COLOR_MENU_BORDER);
184 | }
185 |
186 | if (input->active & PKGI_BUTTON_UP)
187 | {
188 | do {
189 | if (menu_selected == 0)
190 | {
191 | menu_selected = PKGI_COUNTOF(menu_entries) - 1;
192 | }
193 | else
194 | {
195 | menu_selected--;
196 | }
197 | } while (menu_entries[menu_selected].type == MenuText
198 | || (menu_entries[menu_selected].type == MenuSearchClear && !menu_search_clear)
199 | || (menu_entries[menu_selected].type == MenuRefresh && !menu_allow_refresh));
200 | }
201 |
202 | if (input->active & PKGI_BUTTON_DOWN)
203 | {
204 | do {
205 | if (menu_selected == PKGI_COUNTOF(menu_entries) - 1)
206 | {
207 | menu_selected = 0;
208 | }
209 | else
210 | {
211 | menu_selected++;
212 | }
213 | } while (menu_entries[menu_selected].type == MenuText
214 | || (menu_entries[menu_selected].type == MenuSearchClear && !menu_search_clear)
215 | || (menu_entries[menu_selected].type == MenuRefresh && !menu_allow_refresh));
216 | }
217 |
218 | if (input->pressed & pkgi_cancel_button())
219 | {
220 | menu_result = MenuResultCancel;
221 | menu_delta = -1;
222 | return 1;
223 | }
224 | else if (input->pressed & PKGI_BUTTON_T)
225 | {
226 | menu_result = MenuResultAccept;
227 | menu_delta = -1;
228 | return 1;
229 | }
230 | else if (input->pressed & pkgi_ok_button())
231 | {
232 | MenuType type = menu_entries[menu_selected].type;
233 | if (type == MenuSearch)
234 | {
235 | menu_result = MenuResultSearch;
236 | menu_delta = -1;
237 | return 1;
238 | }
239 | if (type == MenuSearchClear)
240 | {
241 | menu_selected--;
242 | menu_result = MenuResultSearchClear;
243 | menu_delta = -1;
244 | return 1;
245 | }
246 | else if (type == MenuRefresh)
247 | {
248 | menu_result = MenuResultRefresh;
249 | menu_delta = -1;
250 | return 1;
251 | }
252 | else if (type == MenuSort)
253 | {
254 | DbSort value = (DbSort)menu_entries[menu_selected].value;
255 | if (menu_config.sort == value)
256 | {
257 | menu_config.order = menu_config.order == SortAscending ? SortDescending : SortAscending;
258 | }
259 | else
260 | {
261 | menu_config.sort = value;
262 | }
263 | }
264 | else if (type == MenuFilter)
265 | {
266 | menu_config.filter ^= menu_entries[menu_selected].value;
267 | }
268 | else if (type == MenuMode)
269 | {
270 | menu_config.install_mode_iso++;
271 | if (menu_config.install_mode_iso == PKGI_COUNTOF(format_entries))
272 | menu_config.install_mode_iso = 0;
273 | }
274 | else if (type == MenuKeepPkg)
275 | {
276 | menu_config.keep_pkg ^= menu_entries[menu_selected].value;
277 | }
278 | else if (type == MenuUpdate)
279 | {
280 | menu_config.version_check ^= menu_entries[menu_selected].value;
281 | }
282 | else if (type == MenuContent)
283 | {
284 | menu_config.filter ^= content_entries[menu_config.content].value;
285 |
286 | menu_config.content++;
287 | if (menu_config.content == MAX_CONTENT_TYPES)
288 | menu_config.content = 0;
289 |
290 | menu_config.filter ^= content_entries[menu_config.content].value;
291 | }
292 | }
293 |
294 | if (menu_width != pkgi_menu_width)
295 | {
296 | return 1;
297 | }
298 |
299 | int font_height = pkgi_text_height("M");
300 |
301 | int y = PKGI_MENU_TOP_PADDING;
302 | for (uint32_t i = 0; i < PKGI_COUNTOF(menu_entries); i++)
303 | {
304 | const MenuEntry* entry = menu_entries + i;
305 |
306 | MenuType type = entry->type;
307 | if (type == MenuText)
308 | {
309 | y += font_height;
310 | }
311 | else if (type == MenuSearchClear && !menu_search_clear)
312 | {
313 | continue;
314 | }
315 | else if (type == MenuRefresh)
316 | {
317 | if (!menu_allow_refresh)
318 | {
319 | continue;
320 | }
321 | y += font_height;
322 | }
323 |
324 | int x = PKGI_SCREEN_WIDTH - (pkgi_menu_width + PKGI_MAIN_HMARGIN) + PKGI_MENU_LEFT_PADDING + (i > 9 ? pkgi_menu_width/2 : 0);
325 | if (i == 10)
326 | {
327 | y = PKGI_MENU_TOP_PADDING + font_height*2;
328 | }
329 |
330 | char text[64];
331 | if (type == MenuSearch || type == MenuSearchClear || type == MenuText || type == MenuRefresh)
332 | {
333 | pkgi_strncpy(text, sizeof(text), entry->text);
334 | }
335 | else if (type == MenuSort)
336 | {
337 | if (menu_config.sort == (DbSort)entry->value)
338 | {
339 | pkgi_snprintf(text, sizeof(text), "%s %s",
340 | menu_config.order == SortAscending ? PKGI_UTF8_SORT_ASC : PKGI_UTF8_SORT_DESC,
341 | entry->text);
342 | }
343 | else
344 | {
345 | x += pkgi_text_width(PKGI_UTF8_SORT_ASC " ");
346 | pkgi_strncpy(text, sizeof(text), entry->text);
347 | }
348 | }
349 | else if (type == MenuFilter)
350 | {
351 | pkgi_snprintf(text, sizeof(text), "%s %s",
352 | menu_config.filter & entry->value ? PKGI_UTF8_CHECK_ON : PKGI_UTF8_CHECK_OFF,
353 | entry->text);
354 | }
355 | else if (type == MenuMode)
356 | {
357 | pkgi_snprintf(text, sizeof(text), PKGI_UTF8_CLEAR " %s", format_entries[menu_config.install_mode_iso].text);
358 | }
359 | else if (type == MenuKeepPkg)
360 | {
361 | pkgi_snprintf(text, sizeof(text), "%s %s",
362 | menu_config.keep_pkg == entry->value ? PKGI_UTF8_CHECK_ON : PKGI_UTF8_CHECK_OFF, entry->text);
363 | }
364 | else if (type == MenuUpdate)
365 | {
366 | pkgi_snprintf(text, sizeof(text), "%s %s",
367 | menu_config.version_check == entry->value ? PKGI_UTF8_CHECK_ON : PKGI_UTF8_CHECK_OFF, entry->text);
368 | }
369 | else if (type == MenuContent)
370 | {
371 | pkgi_snprintf(text, sizeof(text), PKGI_UTF8_CLEAR " %s", content_entries[menu_config.content].text);
372 | }
373 |
374 | if (menu_selected == i)
375 | {
376 | pkgi_draw_fill_rect_z(PKGI_SCREEN_WIDTH - (pkgi_menu_width + PKGI_MAIN_HMARGIN/2) + (i > 9 ? pkgi_menu_width/2 : 0), y, PKGI_MENU_Z, pkgi_menu_width/2 - PKGI_MAIN_HMARGIN, font_height, PKGI_COLOR(20, 20, 20));
377 | }
378 | pkgi_draw_text_z(x, y, PKGI_MENU_TEXT_Z, PKGI_COLOR_TEXT_MENU, text);
379 |
380 | if (type != MenuSearchClear || !menu_search_clear)
381 | {
382 | y += font_height;
383 | }
384 | }
385 |
386 | return 1;
387 | }
388 |
--------------------------------------------------------------------------------
/source/ttf_fonts.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | //#include "types.h"
6 | #include "ttf_render.h"
7 | #include "pkgi_style.h"
8 |
9 | extern SDL_Renderer* renderer;
10 |
11 | /******************************************************************************************************************************************************/
12 | /* TTF functions to load and convert fonts */
13 | /******************************************************************************************************************************************************/
14 |
15 | static int ttf_inited = 0;
16 |
17 | static FT_Library freetype;
18 | static FT_Face face[4];
19 | static int f_face[4] = {0, 0, 0, 0};
20 |
21 | static int file_exist(const char* path)
22 | {
23 | FILE* f = fopen(path, "rb");
24 | if (f) {
25 | fclose(f);
26 | return 1;
27 | }
28 | return 0;
29 | }
30 |
31 | int TTFLoadFont(int set, const char * path, void * from_memory, int size_from_memory)
32 | {
33 |
34 | if(!ttf_inited)
35 | FT_Init_FreeType(&freetype);
36 | ttf_inited = 1;
37 |
38 | f_face[set] = 0;
39 |
40 | if(path) {
41 | if((file_exist(path) == 0) || FT_New_Face(freetype, path, 0, &face[set])<0) return -1;
42 | } else {
43 | if(FT_New_Memory_Face(freetype, from_memory, size_from_memory, 0, &face[set])) return -1;
44 | }
45 |
46 | f_face[set] = 1;
47 |
48 | return 0;
49 | }
50 |
51 | /* release all */
52 |
53 | void TTFUnloadFont()
54 | {
55 | if(!ttf_inited) return;
56 | FT_Done_FreeType(freetype);
57 | ttf_inited = 0;
58 | }
59 |
60 | /* function to render the character
61 | chr : character from 0 to 255
62 | bitmap: uint8_t bitmap passed to render the character character (max 256 x 256 x 1 (8 bits Alpha))
63 | *w : w is the bitmap width as input and the width of the character (used to increase X) as output
64 | *h : h is the bitmap height as input and the height of the character (used to Y correction combined with y_correction) as output
65 | y_correction : the Y correction to display the character correctly in the screen
66 |
67 | */
68 | /*
69 | void TTF_to_Bitmap(uint8_t chr, uint8_t * bitmap, short *w, short *h, short *y_correction)
70 | {
71 | if(f_face[0]) FT_Set_Pixel_Sizes(face[0], (*w), (*h));
72 | if(f_face[1]) FT_Set_Pixel_Sizes(face[1], (*w), (*h));
73 | if(f_face[2]) FT_Set_Pixel_Sizes(face[2], (*w), (*h));
74 | if(f_face[3]) FT_Set_Pixel_Sizes(face[3], (*w), (*h));
75 |
76 | FT_GlyphSlot slot;
77 |
78 | memset(bitmap, 0, (*w) * (*h));
79 |
80 | FT_UInt index;
81 |
82 | if(f_face[0] && (index = FT_Get_Char_Index(face[0], (char) chr))!=0
83 | && !FT_Load_Glyph(face[0], index, FT_LOAD_RENDER )) slot = face[0]->glyph;
84 | else if(f_face[1] && (index = FT_Get_Char_Index(face[1], (char) chr))!=0
85 | && !FT_Load_Glyph(face[1], index, FT_LOAD_RENDER )) slot = face[1]->glyph;
86 | else if(f_face[2] && (index = FT_Get_Char_Index(face[2], (char) chr))!=0
87 | && !FT_Load_Glyph(face[2], index, FT_LOAD_RENDER )) slot = face[2]->glyph;
88 | else if(f_face[3] && (index = FT_Get_Char_Index(face[3], (char) chr))!=0
89 | && !FT_Load_Glyph(face[3], index, FT_LOAD_RENDER )) slot = face[3]->glyph;
90 | else {(*w) = 0; return;}
91 |
92 | int n, m, ww;
93 |
94 | *y_correction = (*h) - 1 - slot->bitmap_top;
95 |
96 | ww = 0;
97 |
98 | for(n = 0; n < slot->bitmap.rows; n++) {
99 | for (m = 0; m < slot->bitmap.width; m++) {
100 |
101 | if(m >= (*w) || n >= (*h)) continue;
102 |
103 | bitmap[m] = (uint8_t) slot->bitmap.buffer[ww + m];
104 | }
105 |
106 | bitmap += *w;
107 |
108 | ww += slot->bitmap.width;
109 | }
110 |
111 | *w = ((slot->advance.x + 31) >> 6) + ((slot->bitmap_left < 0) ? -slot->bitmap_left : 0);
112 | *h = slot->bitmap.rows;
113 | }
114 |
115 | int Render_String_UTF8(uint16_t * bitmap, int w, int h, uint8_t *string, int sw, int sh)
116 | {
117 | int posx = 0;
118 | int n, m, ww, ww2;
119 | uint8_t color;
120 | uint32_t ttf_char;
121 |
122 | if(f_face[0]) FT_Set_Pixel_Sizes(face[0], sw, sh);
123 | if(f_face[1]) FT_Set_Pixel_Sizes(face[1], sw, sh);
124 | if(f_face[2]) FT_Set_Pixel_Sizes(face[2], sw, sh);
125 | if(f_face[3]) FT_Set_Pixel_Sizes(face[3], sw, sh);
126 |
127 | //FT_Set_Pixel_Sizes(face, sw, sh);
128 | FT_GlyphSlot slot = NULL;
129 |
130 | memset(bitmap, 0, w * h * 2);
131 |
132 | while(*string) {
133 |
134 | if(*string == 32 || *string == 9) {posx += sw>>1; string++; continue;}
135 |
136 | if(*string & 128) {
137 | m = 1;
138 |
139 | if((*string & 0xf8)==0xf0) { // 4 bytes
140 | ttf_char = (uint32_t) (*(string++) & 3);
141 | m = 3;
142 | } else if((*string & 0xE0)==0xE0) { // 3 bytes
143 | ttf_char = (uint32_t) (*(string++) & 0xf);
144 | m = 2;
145 | } else if((*string & 0xE0)==0xC0) { // 2 bytes
146 | ttf_char = (uint32_t) (*(string++) & 0x1f);
147 | m = 1;
148 | } else {string++;continue;} // error!
149 |
150 | for(n = 0; n < m; n++) {
151 | if(!*string) break; // error!
152 | if((*string & 0xc0) != 0x80) break; // error!
153 | ttf_char = (ttf_char <<6) |((uint32_t) (*(string++) & 63));
154 | }
155 |
156 | if((n != m) && !*string) break;
157 |
158 | } else ttf_char = (uint32_t) *(string++);
159 |
160 | if(ttf_char == 13 || ttf_char == 10) ttf_char='/';
161 |
162 | FT_UInt index;
163 |
164 | if(f_face[0] && (index = FT_Get_Char_Index(face[0], ttf_char))!=0
165 | && !FT_Load_Glyph(face[0], index, FT_LOAD_RENDER )) slot = face[0]->glyph;
166 | else if(f_face[1] && (index = FT_Get_Char_Index(face[1], ttf_char))!=0
167 | && !FT_Load_Glyph(face[1], index, FT_LOAD_RENDER )) slot = face[1]->glyph;
168 | else if(f_face[2] && (index = FT_Get_Char_Index(face[2], ttf_char))!=0
169 | && !FT_Load_Glyph(face[2], index, FT_LOAD_RENDER )) slot = face[2]->glyph;
170 | else if(f_face[3] && (index = FT_Get_Char_Index(face[3], ttf_char))!=0
171 | && !FT_Load_Glyph(face[3], index, FT_LOAD_RENDER )) slot = face[3]->glyph;
172 | else ttf_char = 0;
173 |
174 | if(ttf_char!=0 && slot->bitmap.buffer) {
175 | ww = ww2 = 0;
176 |
177 | int y_correction = sh - 1 - slot->bitmap_top;
178 | if(y_correction < 0) y_correction = 0;
179 | ww2 = y_correction * w;
180 |
181 | for(n = 0; n < slot->bitmap.rows; n++) {
182 | if(n + y_correction >= h) break;
183 | for (m = 0; m < slot->bitmap.width; m++) {
184 |
185 | if(m + posx >= w) continue;
186 |
187 | color = (uint8_t) slot->bitmap.buffer[ww + m];
188 |
189 | if(color) bitmap[posx + m + ww2] = (color<<8) | 0xfff;
190 | }
191 |
192 | ww2 += w;
193 |
194 | ww += slot->bitmap.width;
195 | }
196 |
197 | }
198 |
199 | if(slot) posx+= slot->bitmap.width;
200 | }
201 | return posx;
202 | }
203 | */
204 | // constructor dinamico de fuentes 32 x 32
205 |
206 | typedef struct ttf_dyn {
207 | uint32_t ttf;
208 | SDL_Texture *text[2];
209 | uint32_t r_use;
210 | uint16_t y_start;
211 | uint16_t width;
212 | uint16_t height;
213 | uint16_t flags;
214 |
215 | } ttf_dyn;
216 |
217 | #define MAX_CHARS 1600
218 | #define TEX_SZ 32
219 |
220 | static ttf_dyn ttf_font_datas[MAX_CHARS];
221 |
222 | static uint32_t r_use= 0;
223 |
224 | float Y_ttf = 0.0f;
225 | float Z_ttf = 0.0f;
226 |
227 | static int Win_X_ttf = 0;
228 | static int Win_Y_ttf = 0;
229 | static int Win_W_ttf = PKGI_SCREEN_WIDTH;
230 | static int Win_H_ttf = PKGI_SCREEN_HEIGHT;
231 |
232 |
233 | static uint32_t Win_flag = 0;
234 |
235 | void set_ttf_window(int x, int y, int width, int height, uint32_t mode)
236 | {
237 | Win_X_ttf = x;
238 | Win_Y_ttf = y;
239 | Win_W_ttf = width;
240 | Win_H_ttf = height;
241 | Win_flag = mode;
242 | Y_ttf = 0.0f;
243 | Z_ttf = 0.0f;
244 |
245 | }
246 |
247 | uint16_t * init_ttf_table(uint8_t *texture)
248 | {
249 | int n;
250 |
251 | r_use= 0;
252 | for(n= 0; n < MAX_CHARS; n++) {
253 | memset(&ttf_font_datas[n], 0, sizeof(ttf_dyn));
254 | }
255 |
256 | return (uint16_t*)texture;
257 |
258 | }
259 |
260 | void reset_ttf_frame(void)
261 | {
262 | int n;
263 |
264 | for(n = 0; n < MAX_CHARS; n++) {
265 |
266 | ttf_font_datas[n].flags &= 1;
267 |
268 | }
269 |
270 | r_use++;
271 |
272 | }
273 | static void DrawBox_ttf(float x, float y, float z, float w, float h, uint32_t rgba)
274 | {
275 | SDL_FRect rect = {
276 | .x = x,
277 | .y = y,
278 | .w = w,
279 | .h = h,
280 | };
281 |
282 | SDL_SetRenderDrawColor(renderer, RGBA_R(rgba), RGBA_G(rgba), RGBA_B(rgba), RGBA_A(rgba));
283 | SDL_RenderFillRectF(renderer, &rect);
284 | /*
285 | tiny3d_SetPolygon(TINY3D_QUADS);
286 |
287 |
288 | tiny3d_VertexPos(x , y , z);
289 | tiny3d_VertexColor(rgba);
290 |
291 | tiny3d_VertexPos(x + w, y , z);
292 |
293 | tiny3d_VertexPos(x + w, y + h, z);
294 |
295 | tiny3d_VertexPos(x , y + h, z);
296 |
297 | tiny3d_End();
298 | */
299 | }
300 |
301 | static SDL_Texture* create_texture(const uint8_t* bitmap, uint32_t rgba)
302 | {
303 | uint32_t buf[TEX_SZ * TEX_SZ];
304 |
305 | for (int i = 0; i < TEX_SZ*TEX_SZ; i++)
306 | buf[i] = bitmap[i] ? ((rgba & 0xFFFFFF00) | bitmap[i]) : 0;
307 |
308 | SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*) buf, TEX_SZ, TEX_SZ, 32, 4 * TEX_SZ, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
309 | SDL_Texture* sdl_tex = SDL_CreateTextureFromSurface(renderer, surface);
310 | SDL_SetTextureBlendMode(sdl_tex, SDL_BLENDMODE_BLEND);
311 | SDL_FreeSurface(surface);
312 |
313 | return (sdl_tex);
314 | }
315 |
316 | static void DrawTextBox_ttf(SDL_Texture* bitmap, float x, float y, float z, float w, float h, uint32_t rgba, float tx, float ty)
317 | {
318 | SDL_FRect dest = {
319 | .x = x,
320 | .y = y,
321 | .w = w,
322 | .h = h,
323 | };
324 |
325 | SDL_SetTextureAlphaMod(bitmap, RGBA_A(rgba));
326 | SDL_RenderCopyF(renderer, bitmap, NULL, &dest);
327 | }
328 |
329 | #define TTF_UX 30 * (TEX_SZ /32)
330 | #define TTF_UY 24 * (TEX_SZ /32)
331 |
332 |
333 | int display_ttf_string(int posx, int posy, const char *string, uint32_t color, uint32_t bkcolor, int sw, int sh, int (*DrawIcon_cb)(int, int, char))
334 | {
335 | int l,n, m, ww, ww2;
336 | uint8_t colorc;
337 | uint32_t ttf_char;
338 | uint8_t *ustring = (uint8_t *) string;
339 |
340 | int lenx = 0;
341 |
342 | while(*ustring) {
343 |
344 | if(posy >= Win_H_ttf) break;
345 |
346 | if(*ustring == ' ') {posx += sw>>1; ustring++; continue;}
347 |
348 | if(*ustring & 128) {
349 | m = 1;
350 |
351 | if((*ustring & 0xf8)==0xf0) { // 4 bytes
352 | ttf_char = (uint32_t) (*(ustring++) & 3);
353 | m = 3;
354 | } else if((*ustring & 0xE0)==0xE0) { // 3 bytes
355 | ttf_char = (uint32_t) (*(ustring++) & 0xf);
356 | m = 2;
357 | } else if((*ustring & 0xE0)==0xC0) { // 2 bytes
358 | ttf_char = (uint32_t) (*(ustring++) & 0x1f);
359 | m = 1;
360 | } else {ustring++;continue;} // error!
361 |
362 | for(n = 0; n < m; n++) {
363 | if(!*ustring) break; // error!
364 | if((*ustring & 0xc0) != 0x80) break; // error!
365 | ttf_char = (ttf_char <<6) |((uint32_t) (*(ustring++) & 63));
366 | }
367 |
368 | if((n != m) && !*ustring) break;
369 |
370 | } else ttf_char = (uint32_t) *(ustring++);
371 |
372 |
373 | if(Win_flag & WIN_SKIP_LF) {
374 | if(ttf_char == '\r' || ttf_char == '\n') ttf_char=' ';
375 | } else {
376 | if(Win_flag & WIN_DOUBLE_LF) {
377 | if(ttf_char == '\r') {if(posx > lenx) lenx = posx; posx = 0;continue;}
378 | if(ttf_char == '\n') {posy += sh;continue;}
379 | } else {
380 | if(ttf_char == '\n') {if(posx > lenx) lenx = posx; posx = 0;posy += sh;continue;}
381 | }
382 | }
383 |
384 | if ((ttf_char < 32) && DrawIcon_cb) {
385 | int resx = DrawIcon_cb(posx, posy, (char) ttf_char);
386 | if (resx > 0) {
387 | posx += resx;
388 | continue;
389 | }
390 | else
391 | ttf_char='?';
392 | }
393 |
394 | // search ttf_char
395 | if(ttf_char < 128) n= ttf_char;
396 | else {
397 | m= 0;
398 | int rel=0;
399 |
400 | for(n= 128; n < MAX_CHARS; n++) {
401 | if(!(ttf_font_datas[n].flags & 1)) m= n;
402 |
403 | if((ttf_font_datas[n].flags & 3)==1) {
404 | int trel= r_use - ttf_font_datas[n].r_use;
405 | if(m==0) {m= n;rel = trel;}
406 | else if(rel > trel) {m= n;rel = trel;}
407 |
408 | }
409 | if(ttf_font_datas[n].ttf == ttf_char) break;
410 | }
411 |
412 | if(m==0) m = 128;
413 |
414 | }
415 |
416 | if(n >= MAX_CHARS) {ttf_font_datas[m].flags = 0; l= m;} else l=n;
417 |
418 | // building the character
419 | if(!(ttf_font_datas[l].flags & 1)) {
420 |
421 | if(f_face[0]) FT_Set_Pixel_Sizes(face[0], TTF_UX, TTF_UY);
422 | if(f_face[1]) FT_Set_Pixel_Sizes(face[1], TTF_UX, TTF_UY);
423 | if(f_face[2]) FT_Set_Pixel_Sizes(face[2], TTF_UX, TTF_UY);
424 | if(f_face[3]) FT_Set_Pixel_Sizes(face[3], TTF_UX, TTF_UY);
425 |
426 | FT_GlyphSlot slot = NULL;
427 |
428 | uint8_t bitmap[TEX_SZ * TEX_SZ];
429 | memset(bitmap, 0, TEX_SZ * TEX_SZ);
430 |
431 | ///////////
432 |
433 | FT_UInt index;
434 |
435 | if(f_face[0] && (index = FT_Get_Char_Index(face[0], ttf_char))!=0
436 | && !FT_Load_Glyph(face[0], index, FT_LOAD_RENDER )) slot = face[0]->glyph;
437 | else if(f_face[1] && (index = FT_Get_Char_Index(face[1], ttf_char))!=0
438 | && !FT_Load_Glyph(face[1], index, FT_LOAD_RENDER )) slot = face[1]->glyph;
439 | else if(f_face[2] && (index = FT_Get_Char_Index(face[2], ttf_char))!=0
440 | && !FT_Load_Glyph(face[2], index, FT_LOAD_RENDER )) slot = face[2]->glyph;
441 | else if(f_face[3] && (index = FT_Get_Char_Index(face[3], ttf_char))!=0
442 | && !FT_Load_Glyph(face[3], index, FT_LOAD_RENDER )) slot = face[3]->glyph;
443 | else ttf_char = 0;
444 |
445 | if(ttf_char!=0) {
446 | ww = ww2 = 0;
447 |
448 | int y_correction = TTF_UY - 1 - slot->bitmap_top;
449 | if(y_correction < 0) y_correction = 0;
450 |
451 | ttf_font_datas[l].flags = 1;
452 | ttf_font_datas[l].y_start = y_correction;
453 | ttf_font_datas[l].height = slot->bitmap.rows;
454 | ttf_font_datas[l].width = slot->bitmap.width;
455 | ttf_font_datas[l].ttf = ttf_char;
456 |
457 |
458 | for(n = 0; n < slot->bitmap.rows; n++) {
459 | if(n >= TEX_SZ) break;
460 | for (m = 0; m < slot->bitmap.width; m++) {
461 |
462 | if(m >= TEX_SZ) continue;
463 |
464 | colorc = (uint8_t) slot->bitmap.buffer[ww + m];
465 |
466 | if(colorc) bitmap[m + ww2] = colorc; //(colorc<<8) | 0xfff;
467 | }
468 |
469 | ww2 += TEX_SZ;
470 |
471 | ww += slot->bitmap.width;
472 | }
473 | ttf_font_datas[l].text[0] = create_texture(bitmap, 0x000000FF);
474 | ttf_font_datas[l].text[1] = create_texture(bitmap, 0xFFFFFFFF);
475 | }
476 | else continue;
477 | }
478 |
479 | // displaying the character
480 | ttf_font_datas[l].flags |= 2; // in use
481 | ttf_font_datas[l].r_use = r_use;
482 |
483 | if((Win_flag & WIN_AUTO_LF) && (posx + (ttf_font_datas[l].width * sw / TEX_SZ) + 1) > Win_W_ttf) {
484 | posx = 0;
485 | posy += sh;
486 | }
487 |
488 | uint32_t ccolor = color;
489 | uint32_t cx =(ttf_font_datas[l].width * sw / TEX_SZ) + 1;
490 |
491 | // skip if out of window
492 | if((posx + cx) > Win_W_ttf || (posy + sh) > Win_H_ttf ) ccolor = 0;
493 |
494 | if(ccolor) {
495 | // tiny3d_SetTextureWrap(0, tiny3d_TextureOffset(bitmap), 32, 32, 32 * 2,
496 | // TINY3D_TEX_FORMAT_A4R4G4B4, TEXTWRAP_CLAMP, TEXTWRAP_CLAMP, TEXTURE_LINEAR);
497 |
498 | if (bkcolor != 0) DrawBox_ttf((float) (Win_X_ttf + posx), (float) (Win_Y_ttf + posy) + ((float) ttf_font_datas[l].y_start * sh) * 0.03125f,
499 | Z_ttf, (float) sw, (float) sh, bkcolor);
500 | DrawTextBox_ttf(ttf_font_datas[l].text[((color & 0xFFFFFF00) != 0)], (float) (Win_X_ttf + posx), (float) (Win_Y_ttf + posy) + ((float) ttf_font_datas[l].y_start * sh) * 0.03125f,
501 | Z_ttf, (float) sw, (float) sh, color, 0.99f, 0.99f);
502 | }
503 |
504 | posx+= cx;
505 | }
506 |
507 | Y_ttf = (float) posy + sh;
508 |
509 | if(posx < lenx) posx = lenx;
510 | return posx;
511 | }
512 |
513 | int width_ttf_string(const char *string, int sw, int sh)
514 | {
515 | return (display_ttf_string(0, 0, string, 0, 0, sw, sh, NULL));
516 | }
517 |
--------------------------------------------------------------------------------
/source/zip_util.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "pkgi.h"
9 | #include "pkgi_download.h"
10 |
11 | #define UNZIP_BUF_SIZE 0x20000
12 |
13 | static inline uint64_t min64(uint64_t a, uint64_t b)
14 | {
15 | return a < b ? a : b;
16 | }
17 |
18 | int extract_zip(const char* zip_file)
19 | {
20 | char path[256];
21 | uint8_t* buffer;
22 | int64_t zsize = pkgi_get_size(zip_file);
23 | struct zip* archive = zip_open(zip_file, ZIP_RDONLY | ZIP_CHECKCONS, NULL);
24 | int files = zip_get_num_files(archive);
25 |
26 | LOG("Extracting %s to <%s>...", zip_file, pkgi_get_storage_device());
27 |
28 | if (files <= 0) {
29 | LOG("Empty ZIP file.");
30 | zip_close(archive);
31 | return 0;
32 | }
33 |
34 | buffer = malloc(UNZIP_BUF_SIZE);
35 | if (!buffer)
36 | return 0;
37 |
38 | for (int i = 0; i < files; i++) {
39 | const char* filename = zip_get_name(archive, i, 0);
40 |
41 | update_install_progress(filename, (zsize * i)/files);
42 | LOG("Unzip [%d/%d] '%s'...", i+1, files, filename);
43 |
44 | if (!filename)
45 | continue;
46 |
47 | if (filename[0] == '/')
48 | filename++;
49 |
50 | if (strncasecmp(filename, "PSP/GAME/", 9) == 0)
51 | filename += 9;
52 |
53 | snprintf(path, sizeof(path)-1, "%s/PSP/GAME/%s", pkgi_get_storage_device(), filename);
54 | char* slash = strrchr(path, '/');
55 | *slash = 0;
56 | pkgi_mkdirs(path);
57 | *slash = '/';
58 |
59 | if (filename[strlen(filename) - 1] == '/')
60 | continue;
61 |
62 | struct zip_stat st;
63 | if (zip_stat_index(archive, i, 0, &st)) {
64 | LOG("Unable to access file %s in zip.", filename);
65 | continue;
66 | }
67 | struct zip_file* zfd = zip_fopen_index(archive, i, 0);
68 | if (!zfd) {
69 | LOG("Unable to open file %s in zip.", filename);
70 | continue;
71 | }
72 |
73 | FILE* tfd = fopen(path, "wb");
74 | if(!tfd) {
75 | free(buffer);
76 | zip_fclose(zfd);
77 | zip_close(archive);
78 | LOG("Error opening temporary file '%s'.", path);
79 | return 0;
80 | }
81 |
82 | uint64_t pos = 0, count;
83 | while (pos < st.size) {
84 | count = min64(UNZIP_BUF_SIZE, st.size - pos);
85 | if (zip_fread(zfd, buffer, count) != count) {
86 | free(buffer);
87 | fclose(tfd);
88 | zip_fclose(zfd);
89 | zip_close(archive);
90 | LOG("Error reading from zip.");
91 | return 0;
92 | }
93 |
94 | fwrite(buffer, count, 1, tfd);
95 | pos += count;
96 | }
97 |
98 | zip_fclose(zfd);
99 | fclose(tfd);
100 |
101 | update_install_progress(NULL, zsize * (i+1)/files);
102 | }
103 |
104 | if (archive) {
105 | zip_close(archive);
106 | }
107 |
108 | update_install_progress(NULL, zsize);
109 | free(buffer);
110 |
111 | return files;
112 | }
113 |
--------------------------------------------------------------------------------
/translate.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: PKGi PSP\n"
4 | "POT-Creation-Date: 2023-09-29 19:00-0300\n"
5 | "PO-Revision-Date: 2023-09-29 19:00-0300\n"
6 | "Last-Translator: \n"
7 | "Language-Team: \n"
8 | "Language: en\n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=UTF-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "X-Generator: Poedit 3.0\n"
13 | "X-Poedit-Basepath: ./source\n"
14 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
15 | "X-Poedit-Flags-xgettext: --add-comments\n"
16 | "X-Poedit-SourceCharset: UTF-8\n"
17 | "X-Poedit-KeywordsList: _\n"
18 | "X-Poedit-SearchPath-0: .\n"
19 |
20 | #: pkgi.c:82
21 | msgid "Installing..."
22 | msgstr ""
23 |
24 | #: pkgi.c:82
25 | msgid "Please wait..."
26 | msgstr ""
27 |
28 | #: pkgi.c:94
29 | msgid "Installation failed"
30 | msgstr ""
31 |
32 | #: pkgi.c:118
33 | msgid "Successfully downloaded"
34 | msgstr ""
35 |
36 | #: pkgi.c:122
37 | msgid "Task successfully queued (reboot to start)"
38 | msgstr ""
39 |
40 | #: pkgi.c:163
41 | msgid "GB"
42 | msgstr ""
43 |
44 | #: pkgi.c:167 pkgi_download.c:249
45 | msgid "MB"
46 | msgstr ""
47 |
48 | #: pkgi.c:171 pkgi.c:504 pkgi_download.c:253
49 | msgid "KB"
50 | msgstr ""
51 |
52 | #: pkgi.c:175
53 | msgid "B"
54 | msgstr ""
55 |
56 | #: pkgi.c:185
57 | #, c-format
58 | msgid "pkg requires %u %s free space, but only %u %s available"
59 | msgstr ""
60 |
61 | #: pkgi.c:225 pkgi_menu.c:109 pkgi_menu.c:121
62 | msgid "All"
63 | msgstr ""
64 |
65 | #: pkgi.c:226 pkgi_menu.c:122
66 | msgid "Games"
67 | msgstr ""
68 |
69 | #: pkgi.c:227 pkgi_menu.c:123
70 | msgid "DLCs"
71 | msgstr ""
72 |
73 | #: pkgi.c:228 pkgi_menu.c:124
74 | msgid "Themes"
75 | msgstr ""
76 |
77 | #: pkgi.c:229 pkgi_menu.c:125
78 | msgid "Avatars"
79 | msgstr ""
80 |
81 | #: pkgi.c:230 pkgi_menu.c:126
82 | msgid "Demos"
83 | msgstr ""
84 |
85 | #: pkgi.c:231 pkgi_menu.c:118 pkgi_menu.c:127
86 | msgid "Updates"
87 | msgstr ""
88 |
89 | #: pkgi.c:232 pkgi_menu.c:128
90 | msgid "Emulators"
91 | msgstr ""
92 |
93 | #: pkgi.c:233 pkgi_menu.c:129
94 | msgid "Apps"
95 | msgstr ""
96 |
97 | #: pkgi.c:234 pkgi_menu.c:130
98 | msgid "Tools"
99 | msgstr ""
100 |
101 | #: pkgi.c:235
102 | msgid "Unknown"
103 | msgstr ""
104 |
105 | #: pkgi.c:253
106 | msgid "Exit to XMB?"
107 | msgstr ""
108 |
109 | #: pkgi.c:436
110 | msgid "No items!"
111 | msgstr ""
112 |
113 | #: pkgi.c:462
114 | msgid "Item already installed, download again?"
115 | msgstr ""
116 |
117 | #: pkgi.c:471 pkgi_download.c:372 pkgi_download.c:652
118 | msgid "Downloading..."
119 | msgstr ""
120 |
121 | #: pkgi.c:471 pkgi.c:674
122 | msgid "Preparing..."
123 | msgstr ""
124 |
125 | #: pkgi.c:504 pkgi.c:508
126 | msgid "Refreshing"
127 | msgstr ""
128 |
129 | #: pkgi.c:563 pkgi.c:567
130 | msgid "Count"
131 | msgstr ""
132 |
133 | #: pkgi.c:575
134 | msgid "Free"
135 | msgstr ""
136 |
137 | #: pkgi.c:585
138 | msgid "Select"
139 | msgstr ""
140 |
141 | #: pkgi.c:585
142 | msgid "Close"
143 | msgstr ""
144 |
145 | #: pkgi.c:585
146 | msgid "Cancel"
147 | msgstr ""
148 |
149 | #: pkgi.c:589 pkgi_download.c:136
150 | msgid "Download"
151 | msgstr ""
152 |
153 | #: pkgi.c:589
154 | msgid "Menu"
155 | msgstr ""
156 |
157 | #: pkgi.c:589
158 | msgid "Details"
159 | msgstr ""
160 |
161 | #: pkgi.c:589
162 | msgid "Exit"
163 | msgstr ""
164 |
165 | #: pkgi.c:678
166 | msgid "Successfully downloaded PKGi PSP update"
167 | msgstr ""
168 |
169 | #: pkgi.c:1051
170 | msgid "Search"
171 | msgstr ""
172 |
173 | #: pkgi_db.c:145 pkgi_db.c:153
174 | msgid "failed to download list from"
175 | msgstr ""
176 |
177 | #: pkgi_db.c:159
178 | msgid "list is too large... check for newer pkgi version!"
179 | msgstr ""
180 |
181 | #: pkgi_db.c:187
182 | msgid "list is empty... check the DB server"
183 | msgstr ""
184 |
185 | #: pkgi_db.c:390
186 | msgid "ERROR: pkgi.txt file(s) missing or bad config.txt file"
187 | msgstr ""
188 |
189 | #: pkgi_dialog.c:80
190 | msgid "Content"
191 | msgstr ""
192 |
193 | #: pkgi_dialog.c:108
194 | msgid "ERROR"
195 | msgstr ""
196 |
197 | #: pkgi_dialog.c:169
198 | msgid "Failed to download the update list"
199 | msgstr ""
200 |
201 | #: pkgi_dialog.c:174
202 | msgid "update(s) loaded"
203 | msgstr ""
204 |
205 | #: pkgi_dialog.c:319
206 | #, c-format
207 | msgid "press %s to cancel"
208 | msgstr ""
209 |
210 | #: pkgi_dialog.c:333
211 | #, c-format
212 | msgid "press %s to close - %s to scan updates"
213 | msgstr ""
214 |
215 | #: pkgi_dialog.c:365
216 | msgid "Enter"
217 | msgstr ""
218 |
219 | #: pkgi_dialog.c:365
220 | msgid "Back"
221 | msgstr ""
222 |
223 | #: pkgi_dialog.c:367
224 | #, c-format
225 | msgid "press %s to close"
226 | msgstr ""
227 |
228 | #: pkgi_download.c:183
229 | msgid "Install"
230 | msgstr ""
231 |
232 | #: pkgi_download.c:216 pkgi_download.c:220 pkgi_download.c:226
233 | msgid "ETA"
234 | msgstr ""
235 |
236 | #: pkgi_download.c:310 pkgi_download.c:392
237 | msgid "Could not send HTTP request"
238 | msgstr ""
239 |
240 | #: pkgi_download.c:317 pkgi_download.c:399
241 | msgid "HTTP request failed"
242 | msgstr ""
243 |
244 | #: pkgi_download.c:322 pkgi_download.c:404
245 | msgid "HTTP response has unknown length"
246 | msgstr ""
247 |
248 | #: pkgi_download.c:331
249 | msgid "Not enough free space on HDD"
250 | msgstr ""
251 |
252 | #: pkgi_download.c:340
253 | msgid "Could not create task directory on HDD."
254 | msgstr ""
255 |
256 | #: pkgi_download.c:348
257 | msgid "Saving background task..."
258 | msgstr ""
259 |
260 | #: pkgi_download.c:354
261 | msgid "Could not create PKG file to HDD."
262 | msgstr ""
263 |
264 | #: pkgi_download.c:360
265 | msgid "Could not create task files to HDD."
266 | msgstr ""
267 |
268 | #: pkgi_download.c:425
269 | msgid "HTTP download error"
270 | msgstr ""
271 |
272 | #: pkgi_download.c:432
273 | msgid "HTTP connection closed"
274 | msgstr ""
275 |
276 | #: pkgi_download.c:445
277 | msgid "failed to write to"
278 | msgstr ""
279 |
280 | #: pkgi_download.c:465
281 | msgid "cannot create folder"
282 | msgstr ""
283 |
284 | #: pkgi_download.c:475
285 | msgid "cannot create file"
286 | msgstr ""
287 |
288 | #: pkgi_download.c:490
289 | msgid "cannot resume file"
290 | msgstr ""
291 |
292 | #: pkgi_download.c:562
293 | msgid "pkg integrity failed, try downloading again"
294 | msgstr ""
295 |
296 | #: pkgi_download.c:573
297 | msgid "Creating RAP file"
298 | msgstr ""
299 |
300 | #: pkgi_download.c:581 pkgi_download.c:626
301 | msgid "Cannot save"
302 | msgstr ""
303 |
304 | #: pkgi_download.c:621
305 | msgid "Creating RIF file"
306 | msgstr ""
307 |
308 | #: pkgi_download.c:646
309 | msgid "Resuming..."
310 | msgstr ""
311 |
312 | #: pkgi_download.c:652
313 | msgid "Adding background task..."
314 | msgstr ""
315 |
316 | #: pkgi_download.c:675
317 | msgid "Downloading icon"
318 | msgstr ""
319 |
320 | #: pkgi_download.c:713
321 | msgid "Could not create install directory on HDD."
322 | msgstr ""
323 |
324 | #: pkgi_menu.c:102
325 | msgid "Search..."
326 | msgstr ""
327 |
328 | #: pkgi_menu.c:103
329 | msgid "Sort by:"
330 | msgstr ""
331 |
332 | #: pkgi_menu.c:104
333 | msgid "Title"
334 | msgstr ""
335 |
336 | #: pkgi_menu.c:105
337 | msgid "Region"
338 | msgstr ""
339 |
340 | #: pkgi_menu.c:106
341 | msgid "Name"
342 | msgstr ""
343 |
344 | #: pkgi_menu.c:107
345 | msgid "Size"
346 | msgstr ""
347 |
348 | #: pkgi_menu.c:108
349 | msgid "Content:"
350 | msgstr ""
351 |
352 | #: pkgi_menu.c:110
353 | msgid "Regions:"
354 | msgstr ""
355 |
356 | #: pkgi_menu.c:111
357 | msgid "Asia"
358 | msgstr ""
359 |
360 | #: pkgi_menu.c:112
361 | msgid "Europe"
362 | msgstr ""
363 |
364 | #: pkgi_menu.c:113
365 | msgid "Japan"
366 | msgstr ""
367 |
368 | #: pkgi_menu.c:114
369 | msgid "USA"
370 | msgstr ""
371 |
372 | #: pkgi_menu.c:115
373 | msgid "Options:"
374 | msgstr ""
375 |
376 | #: pkgi_menu.c:116
377 | msgid "ISO"
378 | msgstr ""
379 |
380 | #: pkgi_menu.c:117
381 | msgid "Keep PKGs"
382 | msgstr ""
383 |
384 | #: pkgi_menu.c:119
385 | msgid "Refresh..."
386 | msgstr ""
387 |
--------------------------------------------------------------------------------