├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── LICENSE.md ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── m4 ├── ax_cxx_compile_stdcxx.m4 ├── ax_cxx_compile_stdcxx_14.m4 ├── libtool.m4 ├── ltoptions.m4 ├── ltsugar.m4 ├── ltversion.m4 └── lt~obsolete.m4 └── src ├── common ├── be_val.h ├── elf.h ├── rplwrap.h ├── type_traits.h └── utils.h ├── elf2rpl └── main.cpp ├── readrpl ├── generate_exports_def.cpp ├── generate_exports_def.h ├── main.cpp ├── print.cpp ├── print.h ├── readrpl.h ├── verify.cpp └── verify.h ├── rplexportgen └── rplexportgen.cpp ├── rplimportgen └── rplimportgen.cpp ├── udplogserver └── main.cpp └── wuhbtool ├── entities ├── BufferFileEntry.cpp ├── BufferFileEntry.h ├── DirectoryEntry.cpp ├── DirectoryEntry.h ├── FileEntry.cpp ├── FileEntry.h ├── NodeEntry.cpp ├── NodeEntry.h ├── OSFileEntry.cpp ├── OSFileEntry.h ├── RootEntry.cpp └── RootEntry.h ├── main.cpp ├── services ├── RomFSService.cpp ├── RomFSService.h ├── RomFSStructs.h ├── TgaGzService.cpp └── TgaGzService.h └── utils ├── filepath.cpp ├── filepath.h ├── types.h └── utils.h /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://EditorConfig.org 2 | 3 | # Top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | charset = utf-8 12 | 13 | # 3 space indentation 14 | [*.{c,h,cpp,hpp,ac}] 15 | indent_style = space 16 | indent_size = 3 17 | 18 | # Tab indentation 19 | [Makefile*] 20 | indent_style = tab 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: ${{ matrix.name }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | name: [ 13 | ubuntu-20.04 14 | ] 15 | include: 16 | - name: ubuntu-20.04 17 | os: ubuntu-20.04 18 | 19 | steps: 20 | - uses: actions/checkout@v1 21 | with: 22 | submodules: true 23 | 24 | - name: Install 25 | run: | 26 | sudo apt install autoconf libtool libz-dev libfreeimage-dev pkg-config 27 | 28 | - name: Build 29 | run: | 30 | ./autogen.sh 31 | mkdir build 32 | cd build 33 | ../configure 34 | make 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | INSTALL 2 | Makefile.in 3 | aclocal.m4 4 | autom4te.cache 5 | compile 6 | config.guess 7 | config.sub 8 | configure 9 | depcomp 10 | install-sh 11 | ltmain.sh 12 | missing 13 | build/ 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libraries/fmt"] 2 | path = libraries/fmt 3 | url = https://github.com/fmtlib/fmt.git 4 | [submodule "libraries/excmd"] 5 | path = libraries/excmd 6 | url = https://github.com/exjam/excmd.git 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### wut-tools 1.3.4 2 | elf2rpl: 3 | - change load section alignment to 32. 4 | 5 | #### wut-tools 1.3.3 6 | - rplexportgen: Fix double null-terminator byte in export names. 7 | 8 | #### wut-tools 1.3.2 9 | - Fix macOS freeimage detection 10 | 11 | #### wut-tools 1.3.1 12 | - Fix windows freeimage detection 13 | 14 | #### wut-tools 1.3.0 15 | elf2rpl: 16 | - enforce the SHF_WRITE flag on data sections. 17 | - fix address calculation for SYMTAB/STRTAB relocations. 18 | - Automatically discard DWARF debugging sections. 19 | 20 | wuhbtool: 21 | - new tool 22 | 23 | #### wut-tools 1.2.0 24 | - rplimportgen: Add optional linker script generation. 25 | - rplexportgen: Add support for a NAME field. 26 | 27 | #### wut-tools 1.1.1 28 | - Buildfix from 1.1.0, no major changes. 29 | 30 | #### wut-tools 1.1.0 31 | - elf2rpl: Added a new feature, `__rplwrap`. This will rename any symbol named `__rplwrap_` (where `` is any string) to just ``. If a `` already exists that would conflict with the new symbol, it is renamed to `__rplwrap_name`. 32 | - rplimportgen: Add support for `:TEXT_WRAP` and `:DATA_WRAP` sections. Every symbol in these sections will be prefixed with `__rplwrap_`. This is useful for cases where Cafe functions conflict with libc functions, and should not be used outside of libc or wut internals. 33 | - No known loader, including decaf, readrpl and the Cafe system loader.elf, actually uses or checks the crc32 and count parameters on an import section. To allow import garbage-collection, these have been hardcoded to dummy values. 34 | - rplimportgen now places each imported function into a dedicated section, -ffunction-sections style. This allows ld to garbage-collect unused imports, but also requires an updated linker script that only ships with wut 1.0.0-beta9 and later. 35 | 36 | #### wut-tools 1.0.0 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Makefile.am -- Process this file with automake to produce Makefile.in 2 | ACLOCAL_AMFLAGS = -I m4 3 | 4 | common_CPPFLAGS = -I$(top_srcdir)/src/common 5 | excmd_CPPFLAGS = -I${top_srcdir}/libraries/excmd/src 6 | fmt_CPPFLAGS = -I${top_srcdir}/libraries/fmt/include 7 | 8 | excmd_files = libraries/excmd/src/excmd_exception.h \ 9 | libraries/excmd/src/excmd.h \ 10 | libraries/excmd/src/excmd_meta.h \ 11 | libraries/excmd/src/excmd_str.h \ 12 | libraries/excmd/src/excmd_value_parser.h 13 | 14 | 15 | bin_PROGRAMS = elf2rpl readrpl rplexportgen rplimportgen wuhbtool udplogserver 16 | 17 | noinst_LTLIBRARIES = libfmt.la 18 | 19 | libfmt_la_SOURCES = libraries/fmt/include/fmt/core.h \ 20 | libraries/fmt/include/fmt/format.h \ 21 | libraries/fmt/include/fmt/format-inl.h \ 22 | libraries/fmt/include/fmt/ostream.h \ 23 | libraries/fmt/include/fmt/posix.h \ 24 | libraries/fmt/include/fmt/printf.h \ 25 | libraries/fmt/include/fmt/ranges.h \ 26 | libraries/fmt/include/fmt/time.h \ 27 | libraries/fmt/src/format.cc \ 28 | libraries/fmt/src/posix.cc 29 | 30 | libfmt_la_CPPFLAGS = $(fmt_CPPFLAGS) 31 | 32 | elf2rpl_SOURCES = $(excmd_files) src/elf2rpl/main.cpp 33 | 34 | elf2rpl_CPPFLAGS = @ZLIB_CFLAGS@ $(common_CPPFLAGS) ${excmd_CPPFLAGS} ${fmt_CPPFLAGS} 35 | elf2rpl_LDADD = libfmt.la @ZLIB_LIBS@ 36 | 37 | readrpl_SOURCES = src/readrpl/generate_exports_def.cpp \ 38 | src/readrpl/generate_exports_def.h \ 39 | src/readrpl/main.cpp src/readrpl/print.cpp \ 40 | src/readrpl/print.h src/readrpl/readrpl.h \ 41 | src/readrpl/verify.cpp src/readrpl/verify.h 42 | 43 | readrpl_CPPFLAGS = @ZLIB_CFLAGS@ $(common_CPPFLAGS) ${excmd_CPPFLAGS} ${fmt_CPPFLAGS} 44 | readrpl_LDADD = libfmt.la @ZLIB_LIBS@ 45 | 46 | rplexportgen_SOURCES = src/rplexportgen/rplexportgen.cpp 47 | 48 | rplexportgen_CPPFLAGS = @ZLIB_CFLAGS@ $(common_CPPFLAGS) ${excmd_CPPFLAGS} ${fmt_CPPFLAGS} 49 | rplexportgen_LDADD = @ZLIB_LIBS@ 50 | 51 | rplimportgen_SOURCES = src/rplimportgen/rplimportgen.cpp 52 | 53 | rplimportgen_CPPFLAGS = @ZLIB_CFLAGS@ $(common_CPPFLAGS) ${excmd_CPPFLAGS} ${fmt_CPPFLAGS} 54 | rplimportgen_LDADD = @ZLIB_LIBS@ 55 | 56 | wuhbtool_SOURCES = $(excmd_files) \ 57 | src/wuhbtool/main.cpp \ 58 | src/wuhbtool/entities/BufferFileEntry.cpp \ 59 | src/wuhbtool/entities/BufferFileEntry.h \ 60 | src/wuhbtool/entities/DirectoryEntry.cpp \ 61 | src/wuhbtool/entities/DirectoryEntry.h \ 62 | src/wuhbtool/entities/FileEntry.cpp \ 63 | src/wuhbtool/entities/FileEntry.h \ 64 | src/wuhbtool/entities/NodeEntry.cpp \ 65 | src/wuhbtool/entities/NodeEntry.h \ 66 | src/wuhbtool/entities/OSFileEntry.cpp \ 67 | src/wuhbtool/entities/OSFileEntry.h \ 68 | src/wuhbtool/entities/RootEntry.cpp \ 69 | src/wuhbtool/entities/RootEntry.h \ 70 | src/wuhbtool/services/RomFSService.cpp \ 71 | src/wuhbtool/services/RomFSService.h \ 72 | src/wuhbtool/services/RomFSStructs.h \ 73 | src/wuhbtool/services/TgaGzService.cpp \ 74 | src/wuhbtool/services/TgaGzService.h \ 75 | src/wuhbtool/utils/filepath.cpp \ 76 | src/wuhbtool/utils/filepath.h \ 77 | src/wuhbtool/utils/types.h \ 78 | src/wuhbtool/utils/utils.h 79 | 80 | wuhbtool_CPPFLAGS = @ZLIB_CFLAGS@ $(common_CPPFLAGS) ${excmd_CPPFLAGS} 81 | wuhbtool_LDADD = @ZLIB_LIBS@ @FREEIMAGE_LIBS@ 82 | 83 | udplogserver_SOURCES = src/udplogserver/main.cpp 84 | udplogserver_LDADD = @NET_LIBS@ 85 | 86 | EXTRA_DIST = autogen.sh src/common/be_val.h src/common/elf.h src/common/rplwrap.h src/common/type_traits.h src/common/utils.h LICENSE.md 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://github.com/devkitPro/wut-tools/workflows/C/C++%20CI/badge.svg?branch=master)](https://github.com/devkitPro/wut-tools/actions?workflow=C%2FC%2B%2B+CI) 2 | 3 | # wut-tools 4 | Tools for [wut](https://github.com/devkitPro/wut). 5 | 6 | Licensed under the terms of the GNU General Public License, version 2 or later (GPLv2+). 7 | 8 | ## Install 9 | 10 | It is recommended to install wut by using the [devkitPro package manager](https://devkitpro.org/wiki/devkitPro_pacman) 11 | 12 | ``` 13 | sudo dkp-pacman -Syu wut-tools 14 | ``` 15 | 16 | ## Building 17 | 18 | ### Dependencies 19 | - autoconf 20 | - libtool 21 | - libz-dev 22 | - pkg-config 23 | 24 | ### Building 25 | 26 | For development purposes you may want to build this from source and replace your existing wut-tools installation: 27 | 28 | ``` 29 | dkp-pacman -R wut-tools 30 | ./autogen.sh 31 | mkdir build 32 | cd build 33 | ../configure --prefix=$DEVKITPRO/tools 34 | make install 35 | ``` 36 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if test ! "x$(which libtoolize)" = "x"; then 3 | echo "Running libtoolize" 4 | libtoolize --copy --force --automake 5 | else 6 | if test ! "x$(which gintltoolize)" = "x"; then 7 | echo "Running glibtoolize" 8 | glibtoolize --copy --force --automake 9 | fi 10 | fi 11 | autoreconf --install --force --verbose 12 | 13 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.61) 5 | AC_INIT([wut-tools],[1.3.5],[https://github.com/devkitPro/wut-tools/issues]) 6 | AC_CONFIG_SRCDIR([src/elf2rpl/main.cpp]) 7 | AC_CONFIG_MACRO_DIR([m4]) 8 | 9 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 10 | 11 | AC_CANONICAL_BUILD 12 | AC_CANONICAL_HOST 13 | 14 | # Checks for programs. 15 | AC_PROG_CXX 16 | AC_PROG_INSTALL 17 | AC_CANONICAL_HOST 18 | AC_CANONICAL_BUILD 19 | AC_PROG_LIBTOOL 20 | 21 | AC_SYS_LARGEFILE 22 | 23 | AX_CXX_COMPILE_STDCXX_14(noext, mandatory) 24 | 25 | PKG_CHECK_MODULES([ZLIB], zlib, [ 26 | AC_DEFINE([HAVE_LIBZ], [1], [Define if using zlib.]) 27 | ]) 28 | 29 | NET_LIBS="" 30 | 31 | case "$host" in 32 | *-*-mingw*) 33 | NET_LIBS="-lws2_32" 34 | CFLAGS="$CFLAGS -D__USE_MINGW_ANSI_STDIO" 35 | ;; 36 | esac 37 | 38 | case "$host" in 39 | *-apple-darwin*) 40 | STDCPP_LIBS="-lc++" 41 | ;; 42 | 43 | *) 44 | STDCPP_LIBS="-lstdc++" 45 | ;; 46 | 47 | esac 48 | 49 | AC_CHECK_HEADER([FreeImage.h]) 50 | 51 | AC_MSG_CHECKING([for libfreeimage]) 52 | save_LIBS="$LIBS" 53 | LIBS="-lfreeimage ${LIBS} ${NET_LIBS} ${STDCPP_LIBS} -lm" 54 | AC_LINK_IFELSE( 55 | [AC_LANG_PROGRAM([[#include ]], 56 | [[FreeImage_DeInitialise()]])], 57 | [freeimage_result=yes], 58 | [freeimage_result=no]) 59 | AC_MSG_RESULT([$freeimage_result]) 60 | LIBS="$save_LIBS" 61 | if test "x$freeimage_result" = "xyes"; then 62 | FREEIMAGE_LIBS="-lfreeimage ${NET_LIBS} ${STDCPP_LIBS} -lm" 63 | else 64 | AC_MSG_ERROR(['libfreeimage' not found]) 65 | fi 66 | 67 | CFLAGS="$CFLAGS -std=gnu99" 68 | 69 | AC_SUBST(ZLIB_CFLAGS) 70 | AC_SUBST(ZLIB_LIBS) 71 | AC_SUBST(FREEIMAGE_LIBS) 72 | AC_SUBST(NET_LIBS) 73 | AC_CONFIG_FILES([Makefile]) 74 | AC_OUTPUT 75 | -------------------------------------------------------------------------------- /m4/ax_cxx_compile_stdcxx.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check for baseline language coverage in the compiler for the specified 12 | # version of the C++ standard. If necessary, add switches to CXX and 13 | # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) 14 | # or '14' (for the C++14 standard). 15 | # 16 | # The second argument, if specified, indicates whether you insist on an 17 | # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. 18 | # -std=c++11). If neither is specified, you get whatever works, with 19 | # preference for an extended mode. 20 | # 21 | # The third argument, if specified 'mandatory' or if left unspecified, 22 | # indicates that baseline support for the specified C++ standard is 23 | # required and that the macro should error out if no mode with that 24 | # support is found. If specified 'optional', then configuration proceeds 25 | # regardless, after defining HAVE_CXX${VERSION} if and only if a 26 | # supporting mode is found. 27 | # 28 | # LICENSE 29 | # 30 | # Copyright (c) 2008 Benjamin Kosnik 31 | # Copyright (c) 2012 Zack Weinberg 32 | # Copyright (c) 2013 Roy Stogner 33 | # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov 34 | # Copyright (c) 2015 Paul Norman 35 | # Copyright (c) 2015 Moritz Klammler 36 | # 37 | # Copying and distribution of this file, with or without modification, are 38 | # permitted in any medium without royalty provided the copyright notice 39 | # and this notice are preserved. This file is offered as-is, without any 40 | # warranty. 41 | 42 | #serial 4 43 | 44 | dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro 45 | dnl (serial version number 13). 46 | 47 | AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl 48 | m4_if([$1], [11], [], 49 | [$1], [14], [], 50 | [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])], 51 | [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl 52 | m4_if([$2], [], [], 53 | [$2], [ext], [], 54 | [$2], [noext], [], 55 | [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl 56 | m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], 57 | [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], 58 | [$3], [optional], [ax_cxx_compile_cxx$1_required=false], 59 | [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) 60 | AC_LANG_PUSH([C++])dnl 61 | ac_success=no 62 | AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, 63 | ax_cv_cxx_compile_cxx$1, 64 | [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], 65 | [ax_cv_cxx_compile_cxx$1=yes], 66 | [ax_cv_cxx_compile_cxx$1=no])]) 67 | if test x$ax_cv_cxx_compile_cxx$1 = xyes; then 68 | ac_success=yes 69 | fi 70 | 71 | m4_if([$2], [noext], [], [dnl 72 | if test x$ac_success = xno; then 73 | for switch in -std=gnu++$1 -std=gnu++0x; do 74 | cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) 75 | AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, 76 | $cachevar, 77 | [ac_save_CXX="$CXX" 78 | CXX="$CXX $switch" 79 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], 80 | [eval $cachevar=yes], 81 | [eval $cachevar=no]) 82 | CXX="$ac_save_CXX"]) 83 | if eval test x\$$cachevar = xyes; then 84 | CXX="$CXX $switch" 85 | if test -n "$CXXCPP" ; then 86 | CXXCPP="$CXXCPP $switch" 87 | fi 88 | ac_success=yes 89 | break 90 | fi 91 | done 92 | fi]) 93 | 94 | m4_if([$2], [ext], [], [dnl 95 | if test x$ac_success = xno; then 96 | dnl HP's aCC needs +std=c++11 according to: 97 | dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf 98 | dnl Cray's crayCC needs "-h std=c++11" 99 | for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do 100 | cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) 101 | AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, 102 | $cachevar, 103 | [ac_save_CXX="$CXX" 104 | CXX="$CXX $switch" 105 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], 106 | [eval $cachevar=yes], 107 | [eval $cachevar=no]) 108 | CXX="$ac_save_CXX"]) 109 | if eval test x\$$cachevar = xyes; then 110 | CXX="$CXX $switch" 111 | if test -n "$CXXCPP" ; then 112 | CXXCPP="$CXXCPP $switch" 113 | fi 114 | ac_success=yes 115 | break 116 | fi 117 | done 118 | fi]) 119 | AC_LANG_POP([C++]) 120 | if test x$ax_cxx_compile_cxx$1_required = xtrue; then 121 | if test x$ac_success = xno; then 122 | AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) 123 | fi 124 | fi 125 | if test x$ac_success = xno; then 126 | HAVE_CXX$1=0 127 | AC_MSG_NOTICE([No compiler with C++$1 support was found]) 128 | else 129 | HAVE_CXX$1=1 130 | AC_DEFINE(HAVE_CXX$1,1, 131 | [define if the compiler supports basic C++$1 syntax]) 132 | fi 133 | AC_SUBST(HAVE_CXX$1) 134 | ]) 135 | 136 | 137 | dnl Test body for checking C++11 support 138 | 139 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], 140 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 141 | ) 142 | 143 | 144 | dnl Test body for checking C++14 support 145 | 146 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], 147 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 148 | _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 149 | ) 150 | 151 | 152 | dnl Tests for new features in C++11 153 | 154 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ 155 | 156 | // If the compiler admits that it is not ready for C++11, why torture it? 157 | // Hopefully, this will speed up the test. 158 | 159 | #ifndef __cplusplus 160 | 161 | #error "This is not a C++ compiler" 162 | 163 | #elif __cplusplus < 201103L 164 | 165 | #error "This is not a C++11 compiler" 166 | 167 | #else 168 | 169 | namespace cxx11 170 | { 171 | 172 | namespace test_static_assert 173 | { 174 | 175 | template 176 | struct check 177 | { 178 | static_assert(sizeof(int) <= sizeof(T), "not big enough"); 179 | }; 180 | 181 | } 182 | 183 | namespace test_final_override 184 | { 185 | 186 | struct Base 187 | { 188 | virtual void f() {} 189 | }; 190 | 191 | struct Derived : public Base 192 | { 193 | virtual void f() override {} 194 | }; 195 | 196 | } 197 | 198 | namespace test_double_right_angle_brackets 199 | { 200 | 201 | template < typename T > 202 | struct check {}; 203 | 204 | typedef check single_type; 205 | typedef check> double_type; 206 | typedef check>> triple_type; 207 | typedef check>>> quadruple_type; 208 | 209 | } 210 | 211 | namespace test_decltype 212 | { 213 | 214 | int 215 | f() 216 | { 217 | int a = 1; 218 | decltype(a) b = 2; 219 | return a + b; 220 | } 221 | 222 | } 223 | 224 | namespace test_type_deduction 225 | { 226 | 227 | template < typename T1, typename T2 > 228 | struct is_same 229 | { 230 | static const bool value = false; 231 | }; 232 | 233 | template < typename T > 234 | struct is_same 235 | { 236 | static const bool value = true; 237 | }; 238 | 239 | template < typename T1, typename T2 > 240 | auto 241 | add(T1 a1, T2 a2) -> decltype(a1 + a2) 242 | { 243 | return a1 + a2; 244 | } 245 | 246 | int 247 | test(const int c, volatile int v) 248 | { 249 | static_assert(is_same::value == true, ""); 250 | static_assert(is_same::value == false, ""); 251 | static_assert(is_same::value == false, ""); 252 | auto ac = c; 253 | auto av = v; 254 | auto sumi = ac + av + 'x'; 255 | auto sumf = ac + av + 1.0; 256 | static_assert(is_same::value == true, ""); 257 | static_assert(is_same::value == true, ""); 258 | static_assert(is_same::value == true, ""); 259 | static_assert(is_same::value == false, ""); 260 | static_assert(is_same::value == true, ""); 261 | return (sumf > 0.0) ? sumi : add(c, v); 262 | } 263 | 264 | } 265 | 266 | namespace test_noexcept 267 | { 268 | 269 | int f() { return 0; } 270 | int g() noexcept { return 0; } 271 | 272 | static_assert(noexcept(f()) == false, ""); 273 | static_assert(noexcept(g()) == true, ""); 274 | 275 | } 276 | 277 | namespace test_constexpr 278 | { 279 | 280 | template < typename CharT > 281 | unsigned long constexpr 282 | strlen_c_r(const CharT *const s, const unsigned long acc) noexcept 283 | { 284 | return *s ? strlen_c_r(s + 1, acc + 1) : acc; 285 | } 286 | 287 | template < typename CharT > 288 | unsigned long constexpr 289 | strlen_c(const CharT *const s) noexcept 290 | { 291 | return strlen_c_r(s, 0UL); 292 | } 293 | 294 | static_assert(strlen_c("") == 0UL, ""); 295 | static_assert(strlen_c("1") == 1UL, ""); 296 | static_assert(strlen_c("example") == 7UL, ""); 297 | static_assert(strlen_c("another\0example") == 7UL, ""); 298 | 299 | } 300 | 301 | namespace test_rvalue_references 302 | { 303 | 304 | template < int N > 305 | struct answer 306 | { 307 | static constexpr int value = N; 308 | }; 309 | 310 | answer<1> f(int&) { return answer<1>(); } 311 | answer<2> f(const int&) { return answer<2>(); } 312 | answer<3> f(int&&) { return answer<3>(); } 313 | 314 | void 315 | test() 316 | { 317 | int i = 0; 318 | const int c = 0; 319 | static_assert(decltype(f(i))::value == 1, ""); 320 | static_assert(decltype(f(c))::value == 2, ""); 321 | static_assert(decltype(f(0))::value == 3, ""); 322 | } 323 | 324 | } 325 | 326 | namespace test_uniform_initialization 327 | { 328 | 329 | struct test 330 | { 331 | static const int zero {}; 332 | static const int one {1}; 333 | }; 334 | 335 | static_assert(test::zero == 0, ""); 336 | static_assert(test::one == 1, ""); 337 | 338 | } 339 | 340 | namespace test_lambdas 341 | { 342 | 343 | void 344 | test1() 345 | { 346 | auto lambda1 = [](){}; 347 | auto lambda2 = lambda1; 348 | lambda1(); 349 | lambda2(); 350 | } 351 | 352 | int 353 | test2() 354 | { 355 | auto a = [](int i, int j){ return i + j; }(1, 2); 356 | auto b = []() -> int { return '0'; }(); 357 | auto c = [=](){ return a + b; }(); 358 | auto d = [&](){ return c; }(); 359 | auto e = [a, &b](int x) mutable { 360 | const auto identity = [](int y){ return y; }; 361 | for (auto i = 0; i < a; ++i) 362 | a += b--; 363 | return x + identity(a + b); 364 | }(0); 365 | return a + b + c + d + e; 366 | } 367 | 368 | int 369 | test3() 370 | { 371 | const auto nullary = [](){ return 0; }; 372 | const auto unary = [](int x){ return x; }; 373 | using nullary_t = decltype(nullary); 374 | using unary_t = decltype(unary); 375 | const auto higher1st = [](nullary_t f){ return f(); }; 376 | const auto higher2nd = [unary](nullary_t f1){ 377 | return [unary, f1](unary_t f2){ return f2(unary(f1())); }; 378 | }; 379 | return higher1st(nullary) + higher2nd(nullary)(unary); 380 | } 381 | 382 | } 383 | 384 | namespace test_variadic_templates 385 | { 386 | 387 | template 388 | struct sum; 389 | 390 | template 391 | struct sum 392 | { 393 | static constexpr auto value = N0 + sum::value; 394 | }; 395 | 396 | template <> 397 | struct sum<> 398 | { 399 | static constexpr auto value = 0; 400 | }; 401 | 402 | static_assert(sum<>::value == 0, ""); 403 | static_assert(sum<1>::value == 1, ""); 404 | static_assert(sum<23>::value == 23, ""); 405 | static_assert(sum<1, 2>::value == 3, ""); 406 | static_assert(sum<5, 5, 11>::value == 21, ""); 407 | static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); 408 | 409 | } 410 | 411 | // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae 412 | // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function 413 | // because of this. 414 | namespace test_template_alias_sfinae 415 | { 416 | 417 | struct foo {}; 418 | 419 | template 420 | using member = typename T::member_type; 421 | 422 | template 423 | void func(...) {} 424 | 425 | template 426 | void func(member*) {} 427 | 428 | void test(); 429 | 430 | void test() { func(0); } 431 | 432 | } 433 | 434 | } // namespace cxx11 435 | 436 | #endif // __cplusplus >= 201103L 437 | 438 | ]]) 439 | 440 | 441 | dnl Tests for new features in C++14 442 | 443 | m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ 444 | 445 | // If the compiler admits that it is not ready for C++14, why torture it? 446 | // Hopefully, this will speed up the test. 447 | 448 | #ifndef __cplusplus 449 | 450 | #error "This is not a C++ compiler" 451 | 452 | #elif __cplusplus < 201402L 453 | 454 | #error "This is not a C++14 compiler" 455 | 456 | #else 457 | 458 | namespace cxx14 459 | { 460 | 461 | namespace test_polymorphic_lambdas 462 | { 463 | 464 | int 465 | test() 466 | { 467 | const auto lambda = [](auto&&... args){ 468 | const auto istiny = [](auto x){ 469 | return (sizeof(x) == 1UL) ? 1 : 0; 470 | }; 471 | const int aretiny[] = { istiny(args)... }; 472 | return aretiny[0]; 473 | }; 474 | return lambda(1, 1L, 1.0f, '1'); 475 | } 476 | 477 | } 478 | 479 | namespace test_binary_literals 480 | { 481 | 482 | constexpr auto ivii = 0b0000000000101010; 483 | static_assert(ivii == 42, "wrong value"); 484 | 485 | } 486 | 487 | namespace test_generalized_constexpr 488 | { 489 | 490 | template < typename CharT > 491 | constexpr unsigned long 492 | strlen_c(const CharT *const s) noexcept 493 | { 494 | auto length = 0UL; 495 | for (auto p = s; *p; ++p) 496 | ++length; 497 | return length; 498 | } 499 | 500 | static_assert(strlen_c("") == 0UL, ""); 501 | static_assert(strlen_c("x") == 1UL, ""); 502 | static_assert(strlen_c("test") == 4UL, ""); 503 | static_assert(strlen_c("another\0test") == 7UL, ""); 504 | 505 | } 506 | 507 | namespace test_lambda_init_capture 508 | { 509 | 510 | int 511 | test() 512 | { 513 | auto x = 0; 514 | const auto lambda1 = [a = x](int b){ return a + b; }; 515 | const auto lambda2 = [a = lambda1(x)](){ return a; }; 516 | return lambda2(); 517 | } 518 | 519 | } 520 | 521 | namespace test_digit_seperators 522 | { 523 | 524 | constexpr auto ten_million = 100'000'000; 525 | static_assert(ten_million == 100000000, ""); 526 | 527 | } 528 | 529 | namespace test_return_type_deduction 530 | { 531 | 532 | auto f(int& x) { return x; } 533 | decltype(auto) g(int& x) { return x; } 534 | 535 | template < typename T1, typename T2 > 536 | struct is_same 537 | { 538 | static constexpr auto value = false; 539 | }; 540 | 541 | template < typename T > 542 | struct is_same 543 | { 544 | static constexpr auto value = true; 545 | }; 546 | 547 | int 548 | test() 549 | { 550 | auto x = 0; 551 | static_assert(is_same::value, ""); 552 | static_assert(is_same::value, ""); 553 | return x; 554 | } 555 | 556 | } 557 | 558 | } // namespace cxx14 559 | 560 | #endif // __cplusplus >= 201402L 561 | 562 | ]]) 563 | -------------------------------------------------------------------------------- /m4/ax_cxx_compile_stdcxx_14.m4: -------------------------------------------------------------------------------- 1 | # ============================================================================ 2 | # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_14.html 3 | # ============================================================================ 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CXX_COMPILE_STDCXX_14([ext|noext], [mandatory|optional]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check for baseline language coverage in the compiler for the C++14 12 | # standard; if necessary, add switches to CXX and CXXCPP to enable 13 | # support. 14 | # 15 | # This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX 16 | # macro with the version set to C++14. The two optional arguments are 17 | # forwarded literally as the second and third argument respectively. 18 | # Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for 19 | # more information. If you want to use this macro, you also need to 20 | # download the ax_cxx_compile_stdcxx.m4 file. 21 | # 22 | # LICENSE 23 | # 24 | # Copyright (c) 2015 Moritz Klammler 25 | # 26 | # Copying and distribution of this file, with or without modification, are 27 | # permitted in any medium without royalty provided the copyright notice 28 | # and this notice are preserved. This file is offered as-is, without any 29 | # warranty. 30 | 31 | #serial 4 32 | 33 | AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) 34 | AC_DEFUN([AX_CXX_COMPILE_STDCXX_14], [AX_CXX_COMPILE_STDCXX([14], [$1], [$2])]) 35 | -------------------------------------------------------------------------------- /m4/ltoptions.m4: -------------------------------------------------------------------------------- 1 | # Helper functions for option handling. -*- Autoconf -*- 2 | # 3 | # Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software 4 | # Foundation, Inc. 5 | # Written by Gary V. Vaughan, 2004 6 | # 7 | # This file is free software; the Free Software Foundation gives 8 | # unlimited permission to copy and/or distribute it, with or without 9 | # modifications, as long as this notice is preserved. 10 | 11 | # serial 8 ltoptions.m4 12 | 13 | # This is to help aclocal find these macros, as it can't see m4_define. 14 | AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) 15 | 16 | 17 | # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) 18 | # ------------------------------------------ 19 | m4_define([_LT_MANGLE_OPTION], 20 | [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) 21 | 22 | 23 | # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) 24 | # --------------------------------------- 25 | # Set option OPTION-NAME for macro MACRO-NAME, and if there is a 26 | # matching handler defined, dispatch to it. Other OPTION-NAMEs are 27 | # saved as a flag. 28 | m4_define([_LT_SET_OPTION], 29 | [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl 30 | m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), 31 | _LT_MANGLE_DEFUN([$1], [$2]), 32 | [m4_warning([Unknown $1 option '$2'])])[]dnl 33 | ]) 34 | 35 | 36 | # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) 37 | # ------------------------------------------------------------ 38 | # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. 39 | m4_define([_LT_IF_OPTION], 40 | [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) 41 | 42 | 43 | # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) 44 | # ------------------------------------------------------- 45 | # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME 46 | # are set. 47 | m4_define([_LT_UNLESS_OPTIONS], 48 | [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), 49 | [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), 50 | [m4_define([$0_found])])])[]dnl 51 | m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 52 | ])[]dnl 53 | ]) 54 | 55 | 56 | # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) 57 | # ---------------------------------------- 58 | # OPTION-LIST is a space-separated list of Libtool options associated 59 | # with MACRO-NAME. If any OPTION has a matching handler declared with 60 | # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about 61 | # the unknown option and exit. 62 | m4_defun([_LT_SET_OPTIONS], 63 | [# Set options 64 | m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), 65 | [_LT_SET_OPTION([$1], _LT_Option)]) 66 | 67 | m4_if([$1],[LT_INIT],[ 68 | dnl 69 | dnl Simply set some default values (i.e off) if boolean options were not 70 | dnl specified: 71 | _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no 72 | ]) 73 | _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no 74 | ]) 75 | dnl 76 | dnl If no reference was made to various pairs of opposing options, then 77 | dnl we run the default mode handler for the pair. For example, if neither 78 | dnl 'shared' nor 'disable-shared' was passed, we enable building of shared 79 | dnl archives by default: 80 | _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) 81 | _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) 82 | _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) 83 | _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], 84 | [_LT_ENABLE_FAST_INSTALL]) 85 | _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], 86 | [_LT_WITH_AIX_SONAME([aix])]) 87 | ]) 88 | ])# _LT_SET_OPTIONS 89 | 90 | 91 | ## --------------------------------- ## 92 | ## Macros to handle LT_INIT options. ## 93 | ## --------------------------------- ## 94 | 95 | # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) 96 | # ----------------------------------------- 97 | m4_define([_LT_MANGLE_DEFUN], 98 | [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) 99 | 100 | 101 | # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) 102 | # ----------------------------------------------- 103 | m4_define([LT_OPTION_DEFINE], 104 | [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl 105 | ])# LT_OPTION_DEFINE 106 | 107 | 108 | # dlopen 109 | # ------ 110 | LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes 111 | ]) 112 | 113 | AU_DEFUN([AC_LIBTOOL_DLOPEN], 114 | [_LT_SET_OPTION([LT_INIT], [dlopen]) 115 | AC_DIAGNOSE([obsolete], 116 | [$0: Remove this warning and the call to _LT_SET_OPTION when you 117 | put the 'dlopen' option into LT_INIT's first parameter.]) 118 | ]) 119 | 120 | dnl aclocal-1.4 backwards compatibility: 121 | dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) 122 | 123 | 124 | # win32-dll 125 | # --------- 126 | # Declare package support for building win32 dll's. 127 | LT_OPTION_DEFINE([LT_INIT], [win32-dll], 128 | [enable_win32_dll=yes 129 | 130 | case $host in 131 | *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) 132 | AC_CHECK_TOOL(AS, as, false) 133 | AC_CHECK_TOOL(DLLTOOL, dlltool, false) 134 | AC_CHECK_TOOL(OBJDUMP, objdump, false) 135 | ;; 136 | esac 137 | 138 | test -z "$AS" && AS=as 139 | _LT_DECL([], [AS], [1], [Assembler program])dnl 140 | 141 | test -z "$DLLTOOL" && DLLTOOL=dlltool 142 | _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl 143 | 144 | test -z "$OBJDUMP" && OBJDUMP=objdump 145 | _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl 146 | ])# win32-dll 147 | 148 | AU_DEFUN([AC_LIBTOOL_WIN32_DLL], 149 | [AC_REQUIRE([AC_CANONICAL_HOST])dnl 150 | _LT_SET_OPTION([LT_INIT], [win32-dll]) 151 | AC_DIAGNOSE([obsolete], 152 | [$0: Remove this warning and the call to _LT_SET_OPTION when you 153 | put the 'win32-dll' option into LT_INIT's first parameter.]) 154 | ]) 155 | 156 | dnl aclocal-1.4 backwards compatibility: 157 | dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) 158 | 159 | 160 | # _LT_ENABLE_SHARED([DEFAULT]) 161 | # ---------------------------- 162 | # implement the --enable-shared flag, and supports the 'shared' and 163 | # 'disable-shared' LT_INIT options. 164 | # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. 165 | m4_define([_LT_ENABLE_SHARED], 166 | [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl 167 | AC_ARG_ENABLE([shared], 168 | [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], 169 | [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], 170 | [p=${PACKAGE-default} 171 | case $enableval in 172 | yes) enable_shared=yes ;; 173 | no) enable_shared=no ;; 174 | *) 175 | enable_shared=no 176 | # Look at the argument we got. We use all the common list separators. 177 | lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, 178 | for pkg in $enableval; do 179 | IFS=$lt_save_ifs 180 | if test "X$pkg" = "X$p"; then 181 | enable_shared=yes 182 | fi 183 | done 184 | IFS=$lt_save_ifs 185 | ;; 186 | esac], 187 | [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) 188 | 189 | _LT_DECL([build_libtool_libs], [enable_shared], [0], 190 | [Whether or not to build shared libraries]) 191 | ])# _LT_ENABLE_SHARED 192 | 193 | LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) 194 | LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) 195 | 196 | # Old names: 197 | AC_DEFUN([AC_ENABLE_SHARED], 198 | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) 199 | ]) 200 | 201 | AC_DEFUN([AC_DISABLE_SHARED], 202 | [_LT_SET_OPTION([LT_INIT], [disable-shared]) 203 | ]) 204 | 205 | AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) 206 | AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) 207 | 208 | dnl aclocal-1.4 backwards compatibility: 209 | dnl AC_DEFUN([AM_ENABLE_SHARED], []) 210 | dnl AC_DEFUN([AM_DISABLE_SHARED], []) 211 | 212 | 213 | 214 | # _LT_ENABLE_STATIC([DEFAULT]) 215 | # ---------------------------- 216 | # implement the --enable-static flag, and support the 'static' and 217 | # 'disable-static' LT_INIT options. 218 | # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. 219 | m4_define([_LT_ENABLE_STATIC], 220 | [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl 221 | AC_ARG_ENABLE([static], 222 | [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], 223 | [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], 224 | [p=${PACKAGE-default} 225 | case $enableval in 226 | yes) enable_static=yes ;; 227 | no) enable_static=no ;; 228 | *) 229 | enable_static=no 230 | # Look at the argument we got. We use all the common list separators. 231 | lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, 232 | for pkg in $enableval; do 233 | IFS=$lt_save_ifs 234 | if test "X$pkg" = "X$p"; then 235 | enable_static=yes 236 | fi 237 | done 238 | IFS=$lt_save_ifs 239 | ;; 240 | esac], 241 | [enable_static=]_LT_ENABLE_STATIC_DEFAULT) 242 | 243 | _LT_DECL([build_old_libs], [enable_static], [0], 244 | [Whether or not to build static libraries]) 245 | ])# _LT_ENABLE_STATIC 246 | 247 | LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) 248 | LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) 249 | 250 | # Old names: 251 | AC_DEFUN([AC_ENABLE_STATIC], 252 | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) 253 | ]) 254 | 255 | AC_DEFUN([AC_DISABLE_STATIC], 256 | [_LT_SET_OPTION([LT_INIT], [disable-static]) 257 | ]) 258 | 259 | AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) 260 | AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) 261 | 262 | dnl aclocal-1.4 backwards compatibility: 263 | dnl AC_DEFUN([AM_ENABLE_STATIC], []) 264 | dnl AC_DEFUN([AM_DISABLE_STATIC], []) 265 | 266 | 267 | 268 | # _LT_ENABLE_FAST_INSTALL([DEFAULT]) 269 | # ---------------------------------- 270 | # implement the --enable-fast-install flag, and support the 'fast-install' 271 | # and 'disable-fast-install' LT_INIT options. 272 | # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. 273 | m4_define([_LT_ENABLE_FAST_INSTALL], 274 | [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl 275 | AC_ARG_ENABLE([fast-install], 276 | [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], 277 | [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], 278 | [p=${PACKAGE-default} 279 | case $enableval in 280 | yes) enable_fast_install=yes ;; 281 | no) enable_fast_install=no ;; 282 | *) 283 | enable_fast_install=no 284 | # Look at the argument we got. We use all the common list separators. 285 | lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, 286 | for pkg in $enableval; do 287 | IFS=$lt_save_ifs 288 | if test "X$pkg" = "X$p"; then 289 | enable_fast_install=yes 290 | fi 291 | done 292 | IFS=$lt_save_ifs 293 | ;; 294 | esac], 295 | [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) 296 | 297 | _LT_DECL([fast_install], [enable_fast_install], [0], 298 | [Whether or not to optimize for fast installation])dnl 299 | ])# _LT_ENABLE_FAST_INSTALL 300 | 301 | LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) 302 | LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) 303 | 304 | # Old names: 305 | AU_DEFUN([AC_ENABLE_FAST_INSTALL], 306 | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) 307 | AC_DIAGNOSE([obsolete], 308 | [$0: Remove this warning and the call to _LT_SET_OPTION when you put 309 | the 'fast-install' option into LT_INIT's first parameter.]) 310 | ]) 311 | 312 | AU_DEFUN([AC_DISABLE_FAST_INSTALL], 313 | [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) 314 | AC_DIAGNOSE([obsolete], 315 | [$0: Remove this warning and the call to _LT_SET_OPTION when you put 316 | the 'disable-fast-install' option into LT_INIT's first parameter.]) 317 | ]) 318 | 319 | dnl aclocal-1.4 backwards compatibility: 320 | dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) 321 | dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) 322 | 323 | 324 | # _LT_WITH_AIX_SONAME([DEFAULT]) 325 | # ---------------------------------- 326 | # implement the --with-aix-soname flag, and support the `aix-soname=aix' 327 | # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT 328 | # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. 329 | m4_define([_LT_WITH_AIX_SONAME], 330 | [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl 331 | shared_archive_member_spec= 332 | case $host,$enable_shared in 333 | power*-*-aix[[5-9]]*,yes) 334 | AC_MSG_CHECKING([which variant of shared library versioning to provide]) 335 | AC_ARG_WITH([aix-soname], 336 | [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], 337 | [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], 338 | [case $withval in 339 | aix|svr4|both) 340 | ;; 341 | *) 342 | AC_MSG_ERROR([Unknown argument to --with-aix-soname]) 343 | ;; 344 | esac 345 | lt_cv_with_aix_soname=$with_aix_soname], 346 | [AC_CACHE_VAL([lt_cv_with_aix_soname], 347 | [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) 348 | with_aix_soname=$lt_cv_with_aix_soname]) 349 | AC_MSG_RESULT([$with_aix_soname]) 350 | if test aix != "$with_aix_soname"; then 351 | # For the AIX way of multilib, we name the shared archive member 352 | # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', 353 | # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. 354 | # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, 355 | # the AIX toolchain works better with OBJECT_MODE set (default 32). 356 | if test 64 = "${OBJECT_MODE-32}"; then 357 | shared_archive_member_spec=shr_64 358 | else 359 | shared_archive_member_spec=shr 360 | fi 361 | fi 362 | ;; 363 | *) 364 | with_aix_soname=aix 365 | ;; 366 | esac 367 | 368 | _LT_DECL([], [shared_archive_member_spec], [0], 369 | [Shared archive member basename, for filename based shared library versioning on AIX])dnl 370 | ])# _LT_WITH_AIX_SONAME 371 | 372 | LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) 373 | LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) 374 | LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) 375 | 376 | 377 | # _LT_WITH_PIC([MODE]) 378 | # -------------------- 379 | # implement the --with-pic flag, and support the 'pic-only' and 'no-pic' 380 | # LT_INIT options. 381 | # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. 382 | m4_define([_LT_WITH_PIC], 383 | [AC_ARG_WITH([pic], 384 | [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], 385 | [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], 386 | [lt_p=${PACKAGE-default} 387 | case $withval in 388 | yes|no) pic_mode=$withval ;; 389 | *) 390 | pic_mode=default 391 | # Look at the argument we got. We use all the common list separators. 392 | lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, 393 | for lt_pkg in $withval; do 394 | IFS=$lt_save_ifs 395 | if test "X$lt_pkg" = "X$lt_p"; then 396 | pic_mode=yes 397 | fi 398 | done 399 | IFS=$lt_save_ifs 400 | ;; 401 | esac], 402 | [pic_mode=m4_default([$1], [default])]) 403 | 404 | _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl 405 | ])# _LT_WITH_PIC 406 | 407 | LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) 408 | LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) 409 | 410 | # Old name: 411 | AU_DEFUN([AC_LIBTOOL_PICMODE], 412 | [_LT_SET_OPTION([LT_INIT], [pic-only]) 413 | AC_DIAGNOSE([obsolete], 414 | [$0: Remove this warning and the call to _LT_SET_OPTION when you 415 | put the 'pic-only' option into LT_INIT's first parameter.]) 416 | ]) 417 | 418 | dnl aclocal-1.4 backwards compatibility: 419 | dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) 420 | 421 | ## ----------------- ## 422 | ## LTDL_INIT Options ## 423 | ## ----------------- ## 424 | 425 | m4_define([_LTDL_MODE], []) 426 | LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], 427 | [m4_define([_LTDL_MODE], [nonrecursive])]) 428 | LT_OPTION_DEFINE([LTDL_INIT], [recursive], 429 | [m4_define([_LTDL_MODE], [recursive])]) 430 | LT_OPTION_DEFINE([LTDL_INIT], [subproject], 431 | [m4_define([_LTDL_MODE], [subproject])]) 432 | 433 | m4_define([_LTDL_TYPE], []) 434 | LT_OPTION_DEFINE([LTDL_INIT], [installable], 435 | [m4_define([_LTDL_TYPE], [installable])]) 436 | LT_OPTION_DEFINE([LTDL_INIT], [convenience], 437 | [m4_define([_LTDL_TYPE], [convenience])]) 438 | -------------------------------------------------------------------------------- /m4/ltsugar.m4: -------------------------------------------------------------------------------- 1 | # ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- 2 | # 3 | # Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software 4 | # Foundation, Inc. 5 | # Written by Gary V. Vaughan, 2004 6 | # 7 | # This file is free software; the Free Software Foundation gives 8 | # unlimited permission to copy and/or distribute it, with or without 9 | # modifications, as long as this notice is preserved. 10 | 11 | # serial 6 ltsugar.m4 12 | 13 | # This is to help aclocal find these macros, as it can't see m4_define. 14 | AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) 15 | 16 | 17 | # lt_join(SEP, ARG1, [ARG2...]) 18 | # ----------------------------- 19 | # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their 20 | # associated separator. 21 | # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier 22 | # versions in m4sugar had bugs. 23 | m4_define([lt_join], 24 | [m4_if([$#], [1], [], 25 | [$#], [2], [[$2]], 26 | [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) 27 | m4_define([_lt_join], 28 | [m4_if([$#$2], [2], [], 29 | [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) 30 | 31 | 32 | # lt_car(LIST) 33 | # lt_cdr(LIST) 34 | # ------------ 35 | # Manipulate m4 lists. 36 | # These macros are necessary as long as will still need to support 37 | # Autoconf-2.59, which quotes differently. 38 | m4_define([lt_car], [[$1]]) 39 | m4_define([lt_cdr], 40 | [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], 41 | [$#], 1, [], 42 | [m4_dquote(m4_shift($@))])]) 43 | m4_define([lt_unquote], $1) 44 | 45 | 46 | # lt_append(MACRO-NAME, STRING, [SEPARATOR]) 47 | # ------------------------------------------ 48 | # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. 49 | # Note that neither SEPARATOR nor STRING are expanded; they are appended 50 | # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). 51 | # No SEPARATOR is output if MACRO-NAME was previously undefined (different 52 | # than defined and empty). 53 | # 54 | # This macro is needed until we can rely on Autoconf 2.62, since earlier 55 | # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. 56 | m4_define([lt_append], 57 | [m4_define([$1], 58 | m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) 59 | 60 | 61 | 62 | # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) 63 | # ---------------------------------------------------------- 64 | # Produce a SEP delimited list of all paired combinations of elements of 65 | # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list 66 | # has the form PREFIXmINFIXSUFFIXn. 67 | # Needed until we can rely on m4_combine added in Autoconf 2.62. 68 | m4_define([lt_combine], 69 | [m4_if(m4_eval([$# > 3]), [1], 70 | [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl 71 | [[m4_foreach([_Lt_prefix], [$2], 72 | [m4_foreach([_Lt_suffix], 73 | ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, 74 | [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) 75 | 76 | 77 | # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) 78 | # ----------------------------------------------------------------------- 79 | # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited 80 | # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. 81 | m4_define([lt_if_append_uniq], 82 | [m4_ifdef([$1], 83 | [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], 84 | [lt_append([$1], [$2], [$3])$4], 85 | [$5])], 86 | [lt_append([$1], [$2], [$3])$4])]) 87 | 88 | 89 | # lt_dict_add(DICT, KEY, VALUE) 90 | # ----------------------------- 91 | m4_define([lt_dict_add], 92 | [m4_define([$1($2)], [$3])]) 93 | 94 | 95 | # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) 96 | # -------------------------------------------- 97 | m4_define([lt_dict_add_subkey], 98 | [m4_define([$1($2:$3)], [$4])]) 99 | 100 | 101 | # lt_dict_fetch(DICT, KEY, [SUBKEY]) 102 | # ---------------------------------- 103 | m4_define([lt_dict_fetch], 104 | [m4_ifval([$3], 105 | m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), 106 | m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) 107 | 108 | 109 | # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) 110 | # ----------------------------------------------------------------- 111 | m4_define([lt_if_dict_fetch], 112 | [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], 113 | [$5], 114 | [$6])]) 115 | 116 | 117 | # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) 118 | # -------------------------------------------------------------- 119 | m4_define([lt_dict_filter], 120 | [m4_if([$5], [], [], 121 | [lt_join(m4_quote(m4_default([$4], [[, ]])), 122 | lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), 123 | [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl 124 | ]) 125 | -------------------------------------------------------------------------------- /m4/ltversion.m4: -------------------------------------------------------------------------------- 1 | # ltversion.m4 -- version numbers -*- Autoconf -*- 2 | # 3 | # Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. 4 | # Written by Scott James Remnant, 2004 5 | # 6 | # This file is free software; the Free Software Foundation gives 7 | # unlimited permission to copy and/or distribute it, with or without 8 | # modifications, as long as this notice is preserved. 9 | 10 | # @configure_input@ 11 | 12 | # serial 4179 ltversion.m4 13 | # This file is part of GNU Libtool 14 | 15 | m4_define([LT_PACKAGE_VERSION], [2.4.6]) 16 | m4_define([LT_PACKAGE_REVISION], [2.4.6]) 17 | 18 | AC_DEFUN([LTVERSION_VERSION], 19 | [macro_version='2.4.6' 20 | macro_revision='2.4.6' 21 | _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) 22 | _LT_DECL(, macro_revision, 0) 23 | ]) 24 | -------------------------------------------------------------------------------- /m4/lt~obsolete.m4: -------------------------------------------------------------------------------- 1 | # lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- 2 | # 3 | # Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software 4 | # Foundation, Inc. 5 | # Written by Scott James Remnant, 2004. 6 | # 7 | # This file is free software; the Free Software Foundation gives 8 | # unlimited permission to copy and/or distribute it, with or without 9 | # modifications, as long as this notice is preserved. 10 | 11 | # serial 5 lt~obsolete.m4 12 | 13 | # These exist entirely to fool aclocal when bootstrapping libtool. 14 | # 15 | # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), 16 | # which have later been changed to m4_define as they aren't part of the 17 | # exported API, or moved to Autoconf or Automake where they belong. 18 | # 19 | # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN 20 | # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us 21 | # using a macro with the same name in our local m4/libtool.m4 it'll 22 | # pull the old libtool.m4 in (it doesn't see our shiny new m4_define 23 | # and doesn't know about Autoconf macros at all.) 24 | # 25 | # So we provide this file, which has a silly filename so it's always 26 | # included after everything else. This provides aclocal with the 27 | # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything 28 | # because those macros already exist, or will be overwritten later. 29 | # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. 30 | # 31 | # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. 32 | # Yes, that means every name once taken will need to remain here until 33 | # we give up compatibility with versions before 1.7, at which point 34 | # we need to keep only those names which we still refer to. 35 | 36 | # This is to help aclocal find these macros, as it can't see m4_define. 37 | AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) 38 | 39 | m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) 40 | m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) 41 | m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) 42 | m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) 43 | m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) 44 | m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) 45 | m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) 46 | m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) 47 | m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) 48 | m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) 49 | m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) 50 | m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) 51 | m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) 52 | m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) 53 | m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) 54 | m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) 55 | m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) 56 | m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) 57 | m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) 58 | m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) 59 | m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) 60 | m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) 61 | m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) 62 | m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) 63 | m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) 64 | m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) 65 | m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) 66 | m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) 67 | m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) 68 | m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) 69 | m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) 70 | m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) 71 | m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) 72 | m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) 73 | m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) 74 | m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) 75 | m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) 76 | m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) 77 | m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) 78 | m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) 79 | m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) 80 | m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) 81 | m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) 82 | m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) 83 | m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) 84 | m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) 85 | m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) 86 | m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) 87 | m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) 88 | m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) 89 | m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) 90 | m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) 91 | m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) 92 | m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) 93 | m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) 94 | m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) 95 | m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) 96 | m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) 97 | m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) 98 | m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) 99 | m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) 100 | -------------------------------------------------------------------------------- /src/common/be_val.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "utils.h" 3 | #include "type_traits.h" 4 | #include 5 | 6 | template 7 | class be_val 8 | { 9 | public: 10 | static_assert(!std::is_array::value, 11 | "be_val invalid type: array"); 12 | 13 | static_assert(!std::is_pointer::value, 14 | "be_val invalid type: pointer"); 15 | 16 | static_assert(sizeof(Type) == 1 || sizeof(Type) == 2 || sizeof(Type) == 4 || sizeof(Type) == 8, 17 | "be_val invalid type size"); 18 | 19 | using value_type = Type; 20 | 21 | be_val() = default; 22 | 23 | be_val(const value_type &value) : 24 | mStorage(byte_swap(value)) 25 | { 26 | } 27 | 28 | value_type value() const 29 | { 30 | return byte_swap(mStorage); 31 | } 32 | 33 | void setValue(value_type value) 34 | { 35 | mStorage = byte_swap(value); 36 | } 37 | 38 | operator value_type() const 39 | { 40 | return value(); 41 | } 42 | 43 | template::value || 45 | std::is_constructible::value 46 | >::type> 47 | explicit operator bool() const 48 | { 49 | return static_cast(value()); 50 | } 51 | 52 | template::value || 54 | std::is_constructible::value || 55 | std::is_convertible::type>::value 56 | >::type> 57 | explicit operator OtherType() const 58 | { 59 | return static_cast(value()); 60 | } 61 | 62 | template::value>::type> 64 | be_val & operator =(const OtherType &other) 65 | { 66 | setValue(value_type { other }); 67 | return *this; 68 | } 69 | 70 | template::value>::type> 72 | be_val & operator =(OtherType &&other) 73 | { 74 | setValue(value_type { std::forward(other) }); 75 | return *this; 76 | } 77 | 78 | template::value>::type> 80 | be_val & operator =(const be_val &other) 81 | { 82 | setValue(value_type { other.value() }); 83 | return *this; 84 | } 85 | 86 | template::value>::type> 88 | be_val & operator =(be_val &&other) 89 | { 90 | setValue(value_type { other.value() }); 91 | return *this; 92 | } 93 | 94 | template 95 | auto operator ==(const OtherType &other) 96 | -> decltype(std::declval().operator ==(std::declval())) const 97 | { 98 | return value() == other; 99 | } 100 | 101 | template 102 | auto operator !=(const OtherType &other) 103 | -> decltype(std::declval().operator !=(std::declval())) const 104 | { 105 | return value() != other; 106 | } 107 | 108 | template 109 | auto operator >=(const OtherType &other) 110 | -> decltype(std::declval().operator >=(std::declval())) const 111 | { 112 | return value() >= other; 113 | } 114 | 115 | template 116 | auto operator <=(const OtherType &other) 117 | -> decltype(std::declval().operator <=(std::declval())) const 118 | { 119 | return value() <= other; 120 | } 121 | 122 | template 123 | auto operator >(const OtherType &other) 124 | -> decltype(std::declval().operator >(std::declval())) const 125 | { 126 | return value() > other; 127 | } 128 | 129 | template 130 | auto operator <(const OtherType &other) 131 | -> decltype(std::declval().operator <(std::declval())) const 132 | { 133 | return value() < other; 134 | } 135 | 136 | template 137 | auto operator +() 138 | -> decltype(std::declval(). operator+()) const 139 | { 140 | return +value(); 141 | } 142 | 143 | template 144 | auto operator -() 145 | -> decltype(std::declval(). operator-()) const 146 | { 147 | return -value(); 148 | } 149 | 150 | template 151 | auto operator +(const OtherType &other) 152 | -> decltype(std::declval().operator +(std::declval())) const 153 | { 154 | return value() + other; 155 | } 156 | 157 | template 158 | auto operator -(const OtherType &other) 159 | -> decltype(std::declval().operator -(std::declval())) const 160 | { 161 | return value() - other; 162 | } 163 | 164 | template 165 | auto operator *(const OtherType &other) 166 | -> decltype(std::declval().operator *(std::declval())) const 167 | { 168 | return value() * other; 169 | } 170 | 171 | template 172 | auto operator /(const OtherType &other) 173 | -> decltype(std::declval().operator /(std::declval())) const 174 | { 175 | return value() / other; 176 | } 177 | 178 | template 179 | auto operator %(const OtherType &other) 180 | -> decltype(std::declval().operator %(std::declval())) const 181 | { 182 | return value() % other; 183 | } 184 | 185 | template 186 | auto operator |(const OtherType &other) 187 | -> decltype(std::declval().operator |(std::declval())) const 188 | { 189 | return value() | other; 190 | } 191 | 192 | template 193 | auto operator &(const OtherType &other) 194 | -> decltype(std::declval().operator &(std::declval())) const 195 | { 196 | return value() & other; 197 | } 198 | 199 | template 200 | auto operator ^(const OtherType &other) 201 | -> decltype(std::declval().operator ^(std::declval())) const 202 | { 203 | return value() ^ other; 204 | } 205 | 206 | template 207 | auto operator <<(const OtherType &other) 208 | -> decltype(std::declval().operator <<(std::declval())) const 209 | { 210 | return value() << other; 211 | } 212 | 213 | template 214 | auto operator >>(const OtherType &other) 215 | -> decltype(std::declval().operator >>(std::declval())) const 216 | { 217 | return value() >> other; 218 | } 219 | 220 | template() + std::declval())> 222 | be_val &operator +=(const OtherType &other) 223 | { 224 | *this = value() + other; 225 | return *this; 226 | } 227 | 228 | template() - std::declval())> 230 | be_val &operator -=(const OtherType &other) 231 | { 232 | *this = value() - other; 233 | return *this; 234 | } 235 | 236 | template() * std::declval())> 238 | be_val &operator *=(const OtherType &other) 239 | { 240 | *this = value() * other; 241 | return *this; 242 | } 243 | 244 | template() / std::declval())> 246 | be_val &operator /=(const OtherType &other) 247 | { 248 | *this = value() / other; 249 | return *this; 250 | } 251 | 252 | template() % std::declval())> 254 | be_val &operator %=(const OtherType &other) 255 | { 256 | *this = value() % other; 257 | return *this; 258 | } 259 | 260 | template() | std::declval())> 262 | be_val &operator |=(const OtherType &other) 263 | { 264 | *this = static_cast(value() | other); 265 | return *this; 266 | } 267 | 268 | template() & std::declval())> 270 | be_val &operator &=(const OtherType &other) 271 | { 272 | *this = static_cast(value() & other); 273 | return *this; 274 | } 275 | 276 | template() ^ std::declval())> 278 | be_val &operator ^=(const OtherType &other) 279 | { 280 | *this = static_cast(value() ^ other); 281 | return *this; 282 | } 283 | 284 | template() << std::declval())> 286 | be_val &operator <<=(const OtherType &other) 287 | { 288 | *this = value() << other; 289 | return *this; 290 | } 291 | 292 | template() >> std::declval())> 294 | be_val &operator >>=(const OtherType &other) 295 | { 296 | *this = value() >> other; 297 | return *this; 298 | } 299 | 300 | template() + 1)> 302 | be_val &operator ++() 303 | { 304 | setValue(value() + 1); 305 | return *this; 306 | } 307 | 308 | template() + 1)> 310 | be_val operator ++(int) 311 | { 312 | auto before = *this; 313 | setValue(value() + 1); 314 | return before; 315 | } 316 | 317 | template() - 1)> 319 | be_val &operator --() 320 | { 321 | setValue(value() - 1); 322 | return *this; 323 | } 324 | 325 | template() - 1)> 327 | be_val operator --(int) 328 | { 329 | auto before = *this; 330 | setValue(value() - 1); 331 | return before; 332 | } 333 | 334 | template 336 | auto operator [](const IndexType &index) 337 | -> decltype(std::declval().operator [](std::declval())) 338 | { 339 | return value().operator [](index); 340 | } 341 | 342 | template 343 | auto operator ->() 344 | -> decltype(std::declval().operator ->()) 345 | { 346 | return value().operator ->(); 347 | } 348 | 349 | template 350 | auto operator ->() const 351 | -> decltype(std::declval().operator ->()) 352 | { 353 | return value().operator ->(); 354 | } 355 | 356 | template 357 | auto operator *() 358 | -> decltype(std::declval().operator *()) 359 | { 360 | return value().operator *(); 361 | } 362 | 363 | template 364 | auto operator *() const 365 | -> decltype(std::declval().operator *()) 366 | { 367 | return value().operator ->(); 368 | } 369 | 370 | private: 371 | value_type mStorage; 372 | }; 373 | -------------------------------------------------------------------------------- /src/common/elf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "be_val.h" 4 | #include "utils.h" 5 | 6 | #pragma pack(push, 1) 7 | 8 | namespace elf 9 | { 10 | 11 | enum Machine : uint16_t // e_machine 12 | { 13 | EM_PPC = 20 // PowerPC 14 | }; 15 | 16 | enum Encoding : uint8_t // e_encoding 17 | { 18 | ELFDATANONE = 0, 19 | ELFDATA2LSB = 1, 20 | ELFDATA2MSB = 2 21 | }; 22 | 23 | enum Class : uint8_t // e_class 24 | { 25 | ELFCLASSNONE = 0, 26 | ELFCLASS32 = 1, 27 | ELFCLASS64 = 2 28 | }; 29 | 30 | enum Version : uint8_t // e_elf_version 31 | { 32 | EV_NONE = 0, 33 | EV_CURRENT = 1, 34 | }; 35 | 36 | enum FileType : uint32_t // e_type 37 | { 38 | ET_NONE = 0, // No file type 39 | ET_REL = 1, // Relocatable file 40 | ET_EXEC = 2, // Executable file 41 | ET_DYN = 3, // Shared object file 42 | ET_CORE = 4, // Core file 43 | ET_LOPROC = 0xff00, // Beginning of processor-specific codes 44 | ET_CAFE_RPL = 0xff01, // Cafe RPL file 45 | ET_HIPROC = 0xffff // Processor-specific 46 | }; 47 | 48 | enum EABI : uint16_t // e_abi 49 | { 50 | EABI_CAFE = 0xcafe // WiiU CafeOS 51 | }; 52 | 53 | enum SectionFlags : uint32_t // sh_flags 54 | { 55 | SHF_WRITE = 0x1, 56 | SHF_ALLOC = 0x2, 57 | SHF_EXECINSTR = 0x4, 58 | SHF_DEFLATED = 0x08000000, 59 | SHF_MASKPROC = 0xF0000000, 60 | }; 61 | 62 | enum SectionType : uint32_t // sh_type 63 | { 64 | SHT_NULL = 0, // No associated section (inactive entry). 65 | SHT_PROGBITS = 1, // Program-defined contents. 66 | SHT_SYMTAB = 2, // Symbol table. 67 | SHT_STRTAB = 3, // String table. 68 | SHT_RELA = 4, // Relocation entries; explicit addends. 69 | SHT_HASH = 5, // Symbol hash table. 70 | SHT_DYNAMIC = 6, // Information for dynamic linking. 71 | SHT_NOTE = 7, // Information about the file. 72 | SHT_NOBITS = 8, // Data occupies no space in the file. 73 | SHT_REL = 9, // Relocation entries; no explicit addends. 74 | SHT_SHLIB = 10, // Reserved. 75 | SHT_DYNSYM = 11, // Symbol table. 76 | SHT_INIT_ARRAY = 14, // Pointers to initialization functions. 77 | SHT_FINI_ARRAY = 15, // Pointers to termination functions. 78 | SHT_PREINIT_ARRAY = 16, // Pointers to pre-init functions. 79 | SHT_GROUP = 17, // Section group. 80 | SHT_SYMTAB_SHNDX = 18, // Indices for SHN_XINDEX entries. 81 | SHT_LOPROC = 0x70000000, // Lowest processor arch-specific type. 82 | SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type. 83 | SHT_LOUSER = 0x80000000, // Lowest type reserved for applications. 84 | SHT_RPL_EXPORTS = 0x80000001, // RPL Exports 85 | SHT_RPL_IMPORTS = 0x80000002, // RPL Imports 86 | SHT_RPL_CRCS = 0x80000003, // RPL CRCs 87 | SHT_RPL_FILEINFO = 0x80000004,// RPL FileInfo 88 | SHT_HIUSER = 0xffffffff // Highest type reserved for applications. 89 | }; 90 | 91 | enum SymbolBinding : uint32_t // st_info > 4 92 | { 93 | STB_LOCAL = 0, // Local symbol, not visible outside obj file containing def 94 | STB_GLOBAL = 1, // Global symbol, visible to all object files being combined 95 | STB_WEAK = 2, // Weak symbol, like global but lower-precedence 96 | STB_GNU_UNIQUE = 10, 97 | STB_LOOS = 10, // Lowest operating system-specific binding type 98 | STB_HIOS = 12, // Highest operating system-specific binding type 99 | STB_LOPROC = 13, // Lowest processor-specific binding type 100 | STB_HIPROC = 15 // Highest processor-specific binding type 101 | }; 102 | 103 | enum SymbolType : uint32_t // st_info & f 104 | { 105 | STT_NOTYPE = 0, // Symbol's type is not specified 106 | STT_OBJECT = 1, // Symbol is a data object (variable, array, etc.) 107 | STT_FUNC = 2, // Symbol is executable code (function, etc.) 108 | STT_SECTION = 3, // Symbol refers to a section 109 | STT_FILE = 4, // Local, absolute symbol that refers to a file 110 | STT_COMMON = 5, // An uninitialized common block 111 | STT_TLS = 6, // Thread local data object 112 | STT_LOOS = 7, // Lowest operating system-specific symbol type 113 | STT_HIOS = 8, // Highest operating system-specific symbol type 114 | STT_GNU_IFUNC = 10, // GNU indirect function 115 | STT_LOPROC = 13, // Lowest processor-specific symbol type 116 | STT_HIPROC = 15 // Highest processor-specific symbol type 117 | }; 118 | 119 | enum SectionIndex : uint16_t // st_shndx 120 | { 121 | SHN_UNDEF = 0, // Undefined 122 | SHN_LORESERVE = 0xff00, // Reserved range 123 | SHN_ABS = 0xfff1, // Absolute symbols 124 | SHN_COMMON = 0xfff2, // Common symbols 125 | SHN_XINDEX = 0xffff, // Escape -- index stored elsewhere 126 | SHN_HIRESERVE = 0xffff 127 | }; 128 | 129 | enum RelocationType : uint32_t // r_info & 0xff 130 | { 131 | R_PPC_NONE = 0, 132 | R_PPC_ADDR32 = 1, 133 | R_PPC_ADDR24 = 2, 134 | R_PPC_ADDR16 = 3, 135 | R_PPC_ADDR16_LO = 4, 136 | R_PPC_ADDR16_HI = 5, 137 | R_PPC_ADDR16_HA = 6, 138 | R_PPC_ADDR14 = 7, 139 | R_PPC_ADDR14_BRTAKEN = 8, 140 | R_PPC_ADDR14_BRNTAKEN = 9, 141 | R_PPC_REL24 = 10, 142 | R_PPC_REL14 = 11, 143 | R_PPC_REL14_BRTAKEN = 12, 144 | R_PPC_REL14_BRNTAKEN = 13, 145 | R_PPC_GOT16 = 14, 146 | R_PPC_GOT16_LO = 15, 147 | R_PPC_GOT16_HI = 16, 148 | R_PPC_GOT16_HA = 17, 149 | R_PPC_PLTREL24 = 18, 150 | R_PPC_JMP_SLOT = 21, 151 | R_PPC_RELATIVE = 22, 152 | R_PPC_LOCAL24PC = 23, 153 | R_PPC_REL32 = 26, 154 | R_PPC_TLS = 67, 155 | R_PPC_DTPMOD32 = 68, 156 | R_PPC_TPREL16 = 69, 157 | R_PPC_TPREL16_LO = 70, 158 | R_PPC_TPREL16_HI = 71, 159 | R_PPC_TPREL16_HA = 72, 160 | R_PPC_TPREL32 = 73, 161 | R_PPC_DTPREL16 = 74, 162 | R_PPC_DTPREL16_LO = 75, 163 | R_PPC_DTPREL16_HI = 76, 164 | R_PPC_DTPREL16_HA = 77, 165 | R_PPC_DTPREL32 = 78, 166 | R_PPC_GOT_TLSGD16 = 79, 167 | R_PPC_GOT_TLSGD16_LO = 80, 168 | R_PPC_GOT_TLSGD16_HI = 81, 169 | R_PPC_GOT_TLSGD16_HA = 82, 170 | R_PPC_GOT_TLSLD16 = 83, 171 | R_PPC_GOT_TLSLD16_LO = 84, 172 | R_PPC_GOT_TLSLD16_HI = 85, 173 | R_PPC_GOT_TLSLD16_HA = 86, 174 | R_PPC_GOT_TPREL16 = 87, 175 | R_PPC_GOT_TPREL16_LO = 88, 176 | R_PPC_GOT_TPREL16_HI = 89, 177 | R_PPC_GOT_TPREL16_HA = 90, 178 | R_PPC_GOT_DTPREL16 = 91, 179 | R_PPC_GOT_DTPREL16_LO = 92, 180 | R_PPC_GOT_DTPREL16_HI = 93, 181 | R_PPC_GOT_DTPREL16_HA = 94, 182 | R_PPC_TLSGD = 95, 183 | R_PPC_TLSLD = 96, 184 | R_PPC_EMB_SDA21 = 109, 185 | R_PPC_EMB_RELSDA = 116, 186 | R_PPC_DIAB_SDA21_LO = 180, 187 | R_PPC_DIAB_SDA21_HI = 181, 188 | R_PPC_DIAB_SDA21_HA = 182, 189 | R_PPC_DIAB_RELSDA_LO = 183, 190 | R_PPC_DIAB_RELSDA_HI = 184, 191 | R_PPC_DIAB_RELSDA_HA = 185, 192 | R_PPC_GHS_REL16_HA = 251, 193 | R_PPC_GHS_REL16_HI = 252, 194 | R_PPC_GHS_REL16_LO = 253, 195 | }; 196 | 197 | enum RplFileInfoFlag : uint32_t 198 | { 199 | RPL_IS_RPX = 0x2, 200 | }; 201 | 202 | static const unsigned HeaderMagic = 0x7f454c46; 203 | 204 | struct Header 205 | { 206 | be_val magic; // File identification. 207 | be_val fileClass; // File class. 208 | be_val encoding; // Data encoding. 209 | be_val elfVersion; // File version. 210 | be_val abi; // OS/ABI identification. (EABI_*) 211 | be_val pad[7]; 212 | 213 | be_val type; // Type of file (ET_*) 214 | be_val machine; // Required architecture for this file (EM_*) 215 | be_val version; // Must be equal to 1 216 | be_val entry; // Address to jump to in order to start program 217 | be_val phoff; // Program header table's file offset, in bytes 218 | be_val shoff; // Section header table's file offset, in bytes 219 | be_val flags; // Processor-specific flags 220 | be_val ehsize; // Size of ELF header, in bytes 221 | be_val phentsize; // Size of an entry in the program header table 222 | be_val phnum; // Number of entries in the program header table 223 | be_val shentsize; // Size of an entry in the section header table 224 | be_val shnum; // Number of entries in the section header table 225 | be_val shstrndx; // Sect hdr table index of sect name string table 226 | }; 227 | CHECK_SIZE(Header, 0x34); 228 | 229 | struct SectionHeader 230 | { 231 | be_val name; // Section name (index into string table) 232 | be_val type; // Section type (SHT_*) 233 | be_val flags; // Section flags (SHF_*) 234 | be_val addr; // Address where section is to be loaded 235 | be_val offset; // File offset of section data, in bytes 236 | be_val size; // Size of section, in bytes 237 | be_val link; // Section type-specific header table index link 238 | be_val info; // Section type-specific extra information 239 | be_val addralign; // Section address alignment 240 | be_val entsize; // Size of records contained within the section 241 | }; 242 | CHECK_SIZE(SectionHeader, 0x28); 243 | 244 | struct Symbol 245 | { 246 | be_val name; // Symbol name (index into string table) 247 | be_val value; // Value or address associated with the symbol 248 | be_val size; // Size of the symbol 249 | be_val info; // Symbol's type and binding attributes 250 | be_val other; // Must be zero; reserved 251 | be_val shndx; // Which section (header table index) it's defined in (SHN_*) 252 | }; 253 | CHECK_SIZE(Symbol, 0x10); 254 | 255 | struct Rela 256 | { 257 | be_val offset; 258 | be_val info; 259 | be_val addend; 260 | }; 261 | CHECK_SIZE(Rela, 0x0C); 262 | 263 | struct RplImport 264 | { 265 | be_val count; 266 | be_val signature; 267 | char name[1]; 268 | }; 269 | 270 | struct RplExport 271 | { 272 | struct Export 273 | { 274 | be_val value; 275 | be_val name; 276 | }; 277 | 278 | be_val count; 279 | be_val signature; 280 | Export exports[1]; 281 | }; 282 | 283 | struct RplCrc 284 | { 285 | be_val crc; 286 | }; 287 | CHECK_SIZE(RplCrc, 0x04); 288 | 289 | struct RplFileInfo 290 | { 291 | be_val version; 292 | be_val textSize; 293 | be_val textAlign; 294 | be_val dataSize; 295 | be_val dataAlign; 296 | be_val loadSize; 297 | be_val loadAlign; 298 | be_val tempSize; 299 | be_val trampAdjust; 300 | be_val sdaBase; 301 | be_val sda2Base; 302 | be_val stackSize; 303 | be_val filename; 304 | be_val flags; 305 | be_val heapSize; 306 | be_val tagOffset; 307 | be_val minVersion; 308 | be_val compressionLevel; 309 | be_val trampAddition; 310 | be_val fileInfoPad; 311 | be_val cafeSdkVersion; 312 | be_val cafeSdkRevision; 313 | be_val tlsModuleIndex; 314 | be_val tlsAlignShift; 315 | be_val runtimeFileInfoSize; 316 | }; 317 | CHECK_SIZE(RplFileInfo, 0x60); 318 | 319 | } // namespace elf 320 | 321 | #pragma pack(pop) 322 | -------------------------------------------------------------------------------- /src/common/rplwrap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RPLWRAP_PREFIX "__rplwrap_" 4 | -------------------------------------------------------------------------------- /src/common/type_traits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // Same as std::underlying_type but works for non-enum Types 5 | template::value> 6 | struct safe_underlying_type : std::underlying_type { }; 7 | 8 | template 9 | struct safe_underlying_type 10 | { 11 | using type = T; 12 | }; 13 | 14 | // Maps bool value to a std::bool_constant type 15 | template 16 | struct is_true; 17 | 18 | template<> 19 | struct is_true : std::false_type { }; 20 | 21 | template<> 22 | struct is_true : std::true_type { }; 23 | -------------------------------------------------------------------------------- /src/common/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if defined(WIN32) || defined(_WIN32) || defined(_MSC_VER) 10 | #define PLATFORM_WINDOWS 11 | #elif __APPLE__ 12 | #define PLATFORM_APPLE 13 | #define PLATFORM_POSIX 14 | #elif __linux__ 15 | #define PLATFORM_LINUX 16 | #define PLATFORM_POSIX 17 | #endif 18 | 19 | #ifdef PLATFORM_LINUX 20 | #include 21 | #endif 22 | 23 | // reinterpret_cast for value types 24 | template 25 | inline DstType 26 | bit_cast(const SrcType& src) 27 | { 28 | static_assert(sizeof(SrcType) == sizeof(DstType), "bit_cast must be between same sized types"); 29 | static_assert(std::is_trivially_copyable::value, "SrcType is not trivially copyable."); 30 | static_assert(std::is_trivially_copyable::value, "DstType is not trivially copyable."); 31 | 32 | DstType dst; 33 | std::memcpy(&dst, &src, sizeof(SrcType)); 34 | return dst; 35 | } 36 | 37 | // Utility class to swap endian for types of size 1, 2, 4, 8 38 | // other type sizes are not supported 39 | template 40 | struct byte_swap_t; 41 | 42 | template 43 | struct byte_swap_t 44 | { 45 | static Type swap(Type src) 46 | { 47 | return src; 48 | } 49 | }; 50 | 51 | template 52 | struct byte_swap_t 53 | { 54 | static Type swap(Type src) 55 | { 56 | #ifdef PLATFORM_WINDOWS 57 | return bit_cast(_byteswap_ushort(bit_cast(src))); 58 | #elif defined(PLATFORM_LINUX) 59 | return bit_cast(bswap_16(bit_cast(src))); 60 | #else 61 | const uint16_t data = bit_cast(src); 62 | return bit_cast(static_cast((data >> 8) | (data << 8))); 63 | #endif 64 | } 65 | }; 66 | 67 | template 68 | struct byte_swap_t 69 | { 70 | static Type swap(Type src) 71 | { 72 | #ifdef PLATFORM_WINDOWS 73 | return bit_cast(_byteswap_ulong(bit_cast(src))); 74 | #elif defined(PLATFORM_APPLE) 75 | return bit_cast(__builtin_bswap32(bit_cast(src))); 76 | #elif defined(PLATFORM_LINUX) 77 | return bit_cast(bswap_32(bit_cast(src))); 78 | #else 79 | const uint32_t data = bit_cast(src); 80 | return bit_cast( 81 | ((data & 0xFF000000u) >> 24) | 82 | ((data & 0x00FF0000u) >> 8) | 83 | ((data & 0x0000FF00u) << 8) | 84 | ((data & 0x000000FFu) << 24) 85 | ); 86 | #endif 87 | } 88 | }; 89 | 90 | template 91 | struct byte_swap_t 92 | { 93 | static Type swap(Type src) 94 | { 95 | #ifdef PLATFORM_WINDOWS 96 | return bit_cast(_byteswap_uint64(bit_cast(src))); 97 | #elif defined(PLATFORM_APPLE) 98 | return bit_cast(__builtin_bswap64(bit_cast(src))); 99 | #elif defined(PLATFORM_LINUX) 100 | return bit_cast(bswap_64(bit_cast(src))); 101 | #else 102 | uint64_t data = bit_cast(src); 103 | data = ((data & 0x00000000FFFFFFFFull) << 32) | ((data & 0xFFFFFFFF00000000ull) >> 32); 104 | data = ((data & 0x0000FFFF0000FFFFull) << 16) | ((data & 0xFFFF0000FFFF0000ull) >> 16); 105 | data = ((data & 0x00FF00FF00FF00FFull) << 8) | ((data & 0xFF00FF00FF00FF00ull) >> 8); 106 | return bit_cast(data); 107 | #endif 108 | } 109 | }; 110 | 111 | // Swaps endian of src 112 | template 113 | inline Type 114 | byte_swap(Type src) 115 | { 116 | return byte_swap_t::swap(src); 117 | } 118 | 119 | // Alignment helpers 120 | template 121 | constexpr inline Type 122 | align_up(Type value, size_t alignment) 123 | { 124 | return static_cast((static_cast(value) + (alignment - 1)) & ~(alignment - 1)); 125 | } 126 | 127 | template 128 | constexpr inline Type 129 | align_down(Type value, size_t alignment) 130 | { 131 | return static_cast(static_cast(value) & ~(alignment - 1)); 132 | } 133 | 134 | template 135 | constexpr bool 136 | align_check(Type value, size_t alignment) 137 | { 138 | return (static_cast(value) & (alignment - 1)) == 0; 139 | } 140 | 141 | #define CHECK_SIZE(Type, Size) \ 142 | static_assert(sizeof(Type) == Size, \ 143 | #Type " must be " #Size " bytes") 144 | 145 | // trim from start 146 | // Taken from https://stackoverflow.com/a/217605 147 | static inline std::string ltrim(std::string s) { 148 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { 149 | return !std::isspace(ch); 150 | })); 151 | return s; 152 | } 153 | 154 | // trim from end (in place) 155 | static inline std::string rtrim(std::string s) { 156 | s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { 157 | return !std::isspace(ch); 158 | }).base(), s.end()); 159 | return s; 160 | } 161 | 162 | // trim from both ends 163 | static inline std::string 164 | trim(std::string s) 165 | { 166 | return rtrim(ltrim(s)); 167 | } 168 | -------------------------------------------------------------------------------- /src/readrpl/generate_exports_def.cpp: -------------------------------------------------------------------------------- 1 | #include "generate_exports_def.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static const char * 8 | sExportBlacklist[] = { 9 | "__get_eh_globals", 10 | "__get_eh_init_block", 11 | "__get_eh_mem_manage", 12 | "__get_eh_store_globals", 13 | "__get_eh_store_globals_tdeh", 14 | "__gh_errno_ptr", 15 | "__gh_get_errno", 16 | "__gh_iob_init", 17 | "__gh_lock_init", 18 | "__gh_set_errno", 19 | "__ghsLock", 20 | "__ghsUnlock", 21 | "__ghs_at_exit", 22 | "__ghs_at_exit_cleanup", 23 | "__ghs_flock_create", 24 | "__ghs_flock_destroy", 25 | "__ghs_flock_file", 26 | "__ghs_flock_ptr", 27 | "__ghs_ftrylock_file", 28 | "__ghs_funlock_file", 29 | "__ghs_mtx_dst", 30 | "__ghs_mtx_init", 31 | "__ghs_mtx_lock", 32 | "__ghs_mtx_unlock", 33 | "__tls_get_addr", 34 | "memclr", 35 | "memcpy", 36 | "memmove", 37 | "memset", 38 | "__atexit_cleanup", 39 | "__cpp_exception_cleanup_ptr", 40 | "__cpp_exception_init_ptr", 41 | "__gh_FOPEN_MAX", 42 | "__ghs_cpp_locks", 43 | "__stdio_cleanup", 44 | "_iob", 45 | "_iob_lock", 46 | "environ", 47 | "errno", 48 | }; 49 | 50 | static bool 51 | inBlacklist(const char *name) 52 | { 53 | for (auto i = 0u; i < sizeof(sExportBlacklist) / sizeof(sExportBlacklist[0]); ++i) { 54 | if (strcmp(name, sExportBlacklist[i]) == 0) { 55 | return true; 56 | } 57 | } 58 | 59 | return false; 60 | } 61 | 62 | bool 63 | generateExportsDef(const Rpl &rpl, 64 | const std::string &rplName, 65 | const std::string &outFileName) 66 | { 67 | FILE *fh = fopen(outFileName.c_str(), "w"); 68 | if (!fh) { 69 | fmt::print("Failed to open {} for writing!", outFileName); 70 | return false; 71 | } 72 | 73 | fmt::print(fh, ":NAME {}\n", rplName); 74 | 75 | for (auto §ion : rpl.sections) { 76 | if (section.header.type == elf::SHT_RPL_EXPORTS) { 77 | auto exports = reinterpret_cast(section.data.data()); 78 | auto strTab = section.data.data(); 79 | 80 | if (section.header.flags & elf::SHF_EXECINSTR) { 81 | fmt::print(fh, "\n:TEXT\n"); 82 | } else { 83 | fmt::print(fh, "\n:DATA\n"); 84 | } 85 | 86 | for (auto i = 0u; i < exports->count; ++i) { 87 | if (exports->exports[i].name & 0x80000000) { 88 | // Skip TLS exports for now. 89 | continue; 90 | } 91 | 92 | auto name = strTab + (exports->exports[i].name & 0x7FFFFFFF); 93 | 94 | if (inBlacklist(name)) { 95 | fmt::print(fh, "//"); 96 | } 97 | 98 | fmt::print(fh, "{}\n", name); 99 | } 100 | 101 | } 102 | } 103 | 104 | fclose(fh); 105 | return true; 106 | } 107 | -------------------------------------------------------------------------------- /src/readrpl/generate_exports_def.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "readrpl.h" 3 | #include 4 | 5 | bool 6 | generateExportsDef(const Rpl &rpl, 7 | const std::string &rplName, 8 | const std::string &outFileName); 9 | -------------------------------------------------------------------------------- /src/readrpl/main.cpp: -------------------------------------------------------------------------------- 1 | #include "elf.h" 2 | #include "generate_exports_def.h" 3 | #include "print.h" 4 | #include "verify.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static std::string 14 | getFileBasename(std::string path) 15 | { 16 | auto pos = path.find_last_of("\\/"); 17 | if (pos != std::string::npos) { 18 | path.erase(0, pos + 1); 19 | } 20 | 21 | pos = path.rfind('.'); 22 | if (pos != std::string::npos) { 23 | path.erase(pos); 24 | } 25 | 26 | return path; 27 | } 28 | 29 | uint32_t 30 | getSectionIndex(const Rpl &rpl, 31 | const Section §ion) 32 | { 33 | return static_cast(§ion - &rpl.sections[0]); 34 | } 35 | 36 | bool 37 | readSection(std::ifstream &fh, 38 | Section §ion, 39 | size_t i) 40 | { 41 | // Read section header 42 | fh.read(reinterpret_cast(§ion.header), sizeof(elf::SectionHeader)); 43 | 44 | if (section.header.type == elf::SHT_NOBITS || !section.header.size) { 45 | return true; 46 | } 47 | 48 | // Read section data 49 | if (section.header.flags & elf::SHF_DEFLATED) { 50 | auto stream = z_stream {}; 51 | auto ret = Z_OK; 52 | 53 | // Read the original size 54 | uint32_t size = 0; 55 | fh.seekg(section.header.offset.value()); 56 | fh.read(reinterpret_cast(&size), sizeof(uint32_t)); 57 | size = byte_swap(size); 58 | section.data.resize(size); 59 | 60 | // Inflate 61 | memset(&stream, 0, sizeof(stream)); 62 | stream.zalloc = Z_NULL; 63 | stream.zfree = Z_NULL; 64 | stream.opaque = Z_NULL; 65 | 66 | ret = inflateInit(&stream); 67 | 68 | if (ret != Z_OK) { 69 | fmt::print("Couldn't decompress .rpx section because inflateInit returned {}\n", ret); 70 | section.data.clear(); 71 | return false; 72 | } else { 73 | std::vector temp; 74 | temp.resize(section.header.size-sizeof(uint32_t)); 75 | fh.read(temp.data(), temp.size()); 76 | 77 | stream.avail_in = section.header.size; 78 | stream.next_in = reinterpret_cast(temp.data()); 79 | stream.avail_out = static_cast(section.data.size()); 80 | stream.next_out = reinterpret_cast(section.data.data()); 81 | 82 | ret = inflate(&stream, Z_FINISH); 83 | 84 | if (ret != Z_OK && ret != Z_STREAM_END) { 85 | fmt::print("Couldn't decompress .rpx section because inflate returned {}\n", ret); 86 | section.data.clear(); 87 | return false; 88 | } 89 | 90 | inflateEnd(&stream); 91 | } 92 | } else { 93 | section.data.resize(section.header.size); 94 | fh.seekg(section.header.offset.value()); 95 | fh.read(section.data.data(), section.header.size); 96 | } 97 | 98 | return true; 99 | } 100 | 101 | int main(int argc, char **argv) 102 | { 103 | excmd::parser parser; 104 | excmd::option_state options; 105 | using excmd::description; 106 | using excmd::value; 107 | 108 | try { 109 | parser.global_options() 110 | .add_option("H,help", 111 | description { "Show help." }) 112 | .add_option("a,all", 113 | description { "Equivalent to: -h -S -s -r -i -x -c -f" }) 114 | .add_option("h,file-header", 115 | description { "Display the ELF file header" }) 116 | .add_option("S,sections", 117 | description { "Display the sections' header" }) 118 | .add_option("s,symbols", 119 | description { "Display the symbol table" }) 120 | .add_option("r,relocs", 121 | description { "Display the relocations" }) 122 | .add_option("i,imports", 123 | description { "Display the RPL imports" }) 124 | .add_option("x,exports", 125 | description { "Display the RPL exports" }) 126 | .add_option("c,crc", 127 | description { "Display the RPL crc" }) 128 | .add_option("f,file-info", 129 | description { "Display the RPL file info" }) 130 | .add_option("exports-def", 131 | description { "Generate exports.def for wut library linking" }, 132 | value {}); 133 | 134 | parser.default_command() 135 | .add_argument("path", 136 | description { "Path to RPL file" }, 137 | value {}); 138 | 139 | options = parser.parse(argc, argv); 140 | } catch (excmd::exception ex) { 141 | std::cout << "Error parsing options: " << ex.what() << std::endl; 142 | return -1; 143 | } 144 | 145 | if (options.empty() || options.has("help") || !options.has("path")) { 146 | fmt::print("{} path\n", argv[0]); 147 | fmt::print("{}\n", parser.format_help(argv[0])); 148 | return 0; 149 | } 150 | 151 | auto all = options.has("all"); 152 | 153 | auto dumpElfHeader = all || options.has("file-header"); 154 | auto dumpSectionSummary = all || options.has("sections"); 155 | auto dumpSectionRela = all || options.has("relocs"); 156 | auto dumpSectionSymtab = all || options.has("symbols"); 157 | auto dumpSectionRplExports = all || options.has("exports"); 158 | auto dumpSectionRplImports = all || options.has("imports"); 159 | auto dumpSectionRplCrcs = all || options.has("crc"); 160 | auto dumpSectionRplFileinfo = all || options.has("file-info"); 161 | auto path = options.get("path"); 162 | 163 | // If no options are set (other than "path"), let's default to a summary 164 | if (options.set_options.size() == 1) { 165 | dumpElfHeader = true; 166 | dumpSectionSummary = true; 167 | dumpSectionRplFileinfo = true; 168 | } 169 | 170 | // Read file 171 | std::ifstream fh { path, std::ifstream::binary }; 172 | if (!fh.is_open()) { 173 | fmt::print("Could not open {} for reading\n", path); 174 | return -1; 175 | } 176 | 177 | Rpl rpl; 178 | fh.read(reinterpret_cast(&rpl.header), sizeof(elf::Header)); 179 | 180 | if (rpl.header.magic != elf::HeaderMagic) { 181 | fmt::print("Invalid ELF magic header\n"); 182 | return -1; 183 | } 184 | 185 | // Read sections 186 | for (auto i = 0u; i < rpl.header.shnum; ++i) { 187 | Section section; 188 | fh.seekg(rpl.header.shoff + rpl.header.shentsize * i); 189 | 190 | if (!readSection(fh, section, i)) { 191 | fmt::print("Error reading section {}", i); 192 | return -1; 193 | } 194 | 195 | rpl.sections.push_back(section); 196 | } 197 | 198 | // Set section names 199 | auto shStrTab = reinterpret_cast(rpl.sections[rpl.header.shstrndx].data.data()); 200 | for (auto §ion : rpl.sections) { 201 | section.name = shStrTab + section.header.name; 202 | } 203 | 204 | // Verify rpl format 205 | verifyFile(rpl); 206 | verifyCrcs(rpl); 207 | verifyFileBounds(rpl); 208 | verifyRelocationTypes(rpl); 209 | verifySectionAlignment(rpl); 210 | verifySectionOrder(rpl); 211 | 212 | // Format shit 213 | if (dumpElfHeader) { 214 | printHeader(rpl); 215 | } 216 | 217 | if (dumpSectionSummary) { 218 | printSectionSummary(rpl); 219 | } 220 | 221 | // Print section data 222 | for (auto i = 0u; i < rpl.sections.size(); ++i) { 223 | auto §ion = rpl.sections[i]; 224 | auto printSectionHeader = [&](){ 225 | fmt::print( 226 | "Section {}: {}, {}, {} bytes\n", 227 | i, formatSHT(section.header.type), section.name, section.data.size()); 228 | }; 229 | 230 | switch (section.header.type) { 231 | case elf::SHT_NULL: 232 | case elf::SHT_NOBITS: 233 | // Print nothing 234 | break; 235 | case elf::SHT_RELA: 236 | if (!dumpSectionRela) { 237 | continue; 238 | } 239 | 240 | printSectionHeader(); 241 | printRela(rpl, section); 242 | break; 243 | case elf::SHT_SYMTAB: 244 | if (!dumpSectionSymtab) { 245 | continue; 246 | } 247 | 248 | printSectionHeader(); 249 | printSymTab(rpl, section); 250 | break; 251 | case elf::SHT_STRTAB: 252 | break; 253 | case elf::SHT_PROGBITS: 254 | break; 255 | case elf::SHT_RPL_EXPORTS: 256 | if (!dumpSectionRplExports) { 257 | continue; 258 | } 259 | 260 | printSectionHeader(); 261 | printRplExports(rpl, section); 262 | break; 263 | case elf::SHT_RPL_IMPORTS: 264 | if (!dumpSectionRplImports) { 265 | continue; 266 | } 267 | 268 | printSectionHeader(); 269 | printRplImports(rpl, section); 270 | break; 271 | case elf::SHT_RPL_CRCS: 272 | if (!dumpSectionRplCrcs) { 273 | continue; 274 | } 275 | 276 | printSectionHeader(); 277 | printRplCrcs(rpl, section); 278 | break; 279 | case elf::SHT_RPL_FILEINFO: 280 | if (!dumpSectionRplFileinfo) { 281 | continue; 282 | } 283 | 284 | printSectionHeader(); 285 | printFileInfo(rpl, section); 286 | break; 287 | } 288 | } 289 | 290 | if (options.has("exports-def")) { 291 | auto output = options.get("exports-def"); 292 | if (!generateExportsDef(rpl, getFileBasename(path), output)) { 293 | return -1; 294 | } 295 | } 296 | 297 | return 0; 298 | } 299 | -------------------------------------------------------------------------------- /src/readrpl/print.cpp: -------------------------------------------------------------------------------- 1 | #include "print.h" 2 | #include 3 | #include 4 | 5 | static std::string 6 | formatET(uint32_t type) 7 | { 8 | switch (type) { 9 | case elf::ET_NONE: 10 | return "ET_NONE"; 11 | case elf::ET_REL: 12 | return "ET_REL"; 13 | case elf::ET_EXEC: 14 | return "ET_EXEC"; 15 | case elf::ET_DYN: 16 | return "ET_DYN"; 17 | case elf::ET_CORE: 18 | return "ET_CORE"; 19 | case elf::ET_CAFE_RPL: 20 | return "ET_CAFE_RPL"; 21 | default: 22 | return fmt::format("{}", type); 23 | } 24 | } 25 | 26 | static std::string 27 | formatEM(uint32_t machine) 28 | { 29 | switch (machine) { 30 | case elf::EM_PPC: 31 | return "EM_PPC"; 32 | default: 33 | return fmt::format("{}", machine); 34 | } 35 | } 36 | 37 | static std::string 38 | formatEABI(uint32_t eabi) 39 | { 40 | switch (eabi) { 41 | case elf::EABI_CAFE: 42 | return "EABI_CAFE"; 43 | default: 44 | return fmt::format("{}", eabi); 45 | } 46 | } 47 | 48 | static std::string 49 | formatSHF(uint32_t flags) 50 | { 51 | std::string result = ""; 52 | 53 | if (flags & elf::SHF_WRITE) { 54 | result += "W"; 55 | } 56 | 57 | if (flags & elf::SHF_ALLOC) { 58 | result += "A"; 59 | } 60 | 61 | if (flags & elf::SHF_EXECINSTR) { 62 | result += "X"; 63 | } 64 | 65 | if (flags & elf::SHF_DEFLATED) { 66 | result += "Z"; 67 | } 68 | 69 | return result; 70 | } 71 | 72 | std::string 73 | formatSHT(uint32_t type) 74 | { 75 | switch (type) { 76 | case elf::SHT_NULL: 77 | return "SHT_NULL"; 78 | case elf::SHT_PROGBITS: 79 | return "SHT_PROGBITS"; 80 | case elf::SHT_SYMTAB: 81 | return "SHT_SYMTAB"; 82 | case elf::SHT_STRTAB: 83 | return "SHT_STRTAB"; 84 | case elf::SHT_RELA: 85 | return "SHT_RELA"; 86 | case elf::SHT_HASH: 87 | return "SHT_HASH"; 88 | case elf::SHT_DYNAMIC: 89 | return "SHT_DYNAMIC"; 90 | case elf::SHT_NOTE: 91 | return "SHT_NOTE"; 92 | case elf::SHT_NOBITS: 93 | return "SHT_NOBITS"; 94 | case elf::SHT_REL: 95 | return "SHT_REL"; 96 | case elf::SHT_SHLIB: 97 | return "SHT_SHLIB"; 98 | case elf::SHT_DYNSYM: 99 | return "SHT_DYNSYM"; 100 | case elf::SHT_INIT_ARRAY: 101 | return "SHT_INIT_ARRAY"; 102 | case elf::SHT_FINI_ARRAY: 103 | return "SHT_FINI_ARRAY"; 104 | case elf::SHT_PREINIT_ARRAY: 105 | return "SHT_PREINIT_ARRAY"; 106 | case elf::SHT_GROUP: 107 | return "SHT_GROUP"; 108 | case elf::SHT_SYMTAB_SHNDX: 109 | return "SHT_SYMTAB_SHNDX"; 110 | case elf::SHT_LOPROC: 111 | return "SHT_LOPROC"; 112 | case elf::SHT_HIPROC: 113 | return "SHT_HIPROC"; 114 | case elf::SHT_LOUSER: 115 | return "SHT_LOUSER"; 116 | case elf::SHT_RPL_EXPORTS: 117 | return "SHT_RPL_EXPORTS"; 118 | case elf::SHT_RPL_IMPORTS: 119 | return "SHT_RPL_IMPORTS"; 120 | case elf::SHT_RPL_CRCS: 121 | return "SHT_RPL_CRCS"; 122 | case elf::SHT_RPL_FILEINFO: 123 | return "SHT_RPL_FILEINFO"; 124 | case elf::SHT_HIUSER: 125 | return "SHT_HIUSER"; 126 | default: 127 | return fmt::format("{}", type); 128 | } 129 | } 130 | 131 | static std::string 132 | formatRelType(uint32_t type) 133 | { 134 | switch (type) { 135 | case elf::R_PPC_NONE: 136 | return "NONE"; 137 | case elf::R_PPC_ADDR32: 138 | return "ADDR32"; 139 | case elf::R_PPC_ADDR16_LO: 140 | return "ADDR16_LO"; 141 | case elf::R_PPC_ADDR16_HI: 142 | return "ADDR16_HI"; 143 | case elf::R_PPC_ADDR16_HA: 144 | return "ADDR16_HA"; 145 | case elf::R_PPC_REL24: 146 | return "REL24"; 147 | case elf::R_PPC_REL14: 148 | return "REL14"; 149 | case elf::R_PPC_DTPMOD32: 150 | return "DTPMOD32"; 151 | case elf::R_PPC_DTPREL32: 152 | return "DTPREL32"; 153 | case elf::R_PPC_EMB_SDA21: 154 | return "EMB_SDA21"; 155 | case elf::R_PPC_EMB_RELSDA: 156 | return "EMB_RELSDA"; 157 | case elf::R_PPC_DIAB_SDA21_LO: 158 | return "DIAB_SDA21_LO"; 159 | case elf::R_PPC_DIAB_SDA21_HI: 160 | return "DIAB_SDA21_HI"; 161 | case elf::R_PPC_DIAB_SDA21_HA: 162 | return "DIAB_SDA21_HA"; 163 | case elf::R_PPC_DIAB_RELSDA_LO: 164 | return "DIAB_RELSDA_LO"; 165 | case elf::R_PPC_DIAB_RELSDA_HI: 166 | return "DIAB_RELSDA_HI"; 167 | case elf::R_PPC_DIAB_RELSDA_HA: 168 | return "DIAB_RELSDA_HA"; 169 | case elf::R_PPC_GHS_REL16_HA: 170 | return "GHS_REL16_HA"; 171 | case elf::R_PPC_GHS_REL16_HI: 172 | return "GHS_REL16_HI"; 173 | case elf::R_PPC_GHS_REL16_LO: 174 | return "GHS_REL16_LO"; 175 | default: 176 | return fmt::format("{}", type); 177 | } 178 | } 179 | 180 | static std::string 181 | formatSymType(uint32_t type) 182 | { 183 | switch (type) { 184 | case elf::STT_NOTYPE: 185 | return "NOTYPE"; 186 | case elf::STT_OBJECT: 187 | return "OBJECT"; 188 | case elf::STT_FUNC: 189 | return "FUNC"; 190 | case elf::STT_SECTION: 191 | return "SECTION"; 192 | case elf::STT_FILE: 193 | return "FILE"; 194 | case elf::STT_COMMON: 195 | return "COMMON"; 196 | case elf::STT_TLS: 197 | return "TLS"; 198 | case elf::STT_LOOS: 199 | return "LOOS"; 200 | case elf::STT_HIOS: 201 | return "HIOS"; 202 | case elf::STT_GNU_IFUNC: 203 | return "GNU_IFUNC"; 204 | default: 205 | return fmt::format("{}", type); 206 | } 207 | } 208 | 209 | static std::string 210 | formatSymBinding(uint32_t type) 211 | { 212 | switch (type) { 213 | case elf::STB_LOCAL: 214 | return "LOCAL"; 215 | case elf::STB_GLOBAL: 216 | return "GLOBAL"; 217 | case elf::STB_WEAK: 218 | return "WEAK"; 219 | case elf::STB_GNU_UNIQUE: 220 | return "UNIQUE"; 221 | default: 222 | return fmt::format("{}", type); 223 | } 224 | } 225 | 226 | static std::string 227 | formatSymShndx(uint32_t type) 228 | { 229 | switch (type) { 230 | case elf::SHN_UNDEF: 231 | return "UND"; 232 | case elf::SHN_ABS: 233 | return "ABS"; 234 | case elf::SHN_COMMON: 235 | return "CMN"; 236 | case elf::SHN_XINDEX: 237 | return "UND"; 238 | default: 239 | return fmt::format("{}", type); 240 | } 241 | } 242 | 243 | void 244 | printHeader(const Rpl &rpl) 245 | { 246 | const auto &header = rpl.header; 247 | fmt::print("ElfHeader\n"); 248 | fmt::print(" {:<20} = 0x{:08X}\n", "magic", header.magic); 249 | fmt::print(" {:<20} = {}\n", "fileClass", header.fileClass); 250 | fmt::print(" {:<20} = {}\n", "encoding", header.encoding); 251 | fmt::print(" {:<20} = {}\n", "elfVersion", header.elfVersion); 252 | fmt::print(" {:<20} = {} 0x{:04x}\n", "abi", formatEABI(header.abi), header.abi); 253 | fmt::print(" {:<20} = {} 0x{:04X}\n", "type", formatET(header.type), header.type); 254 | fmt::print(" {:<20} = {} {}\n", "machine", formatEM(header.machine), header.machine); 255 | fmt::print(" {:<20} = 0x{:X}\n", "version", header.version); 256 | fmt::print(" {:<20} = 0x{:08X}\n", "entry", header.entry); 257 | fmt::print(" {:<20} = 0x{:X}\n", "phoff", header.phoff); 258 | fmt::print(" {:<20} = 0x{:X}\n", "shoff", header.shoff); 259 | fmt::print(" {:<20} = 0x{:X}\n", "flags", header.flags); 260 | fmt::print(" {:<20} = {}\n", "ehsize", header.ehsize); 261 | fmt::print(" {:<20} = {}\n", "phentsize", header.phentsize); 262 | fmt::print(" {:<20} = {}\n", "phnum", header.phnum); 263 | fmt::print(" {:<20} = {}\n", "shentsize", header.shentsize); 264 | fmt::print(" {:<20} = {}\n", "shnum", header.shnum); 265 | fmt::print(" {:<20} = {}\n", "shstrndx", header.shstrndx); 266 | } 267 | 268 | void 269 | printSectionSummary(const Rpl &rpl) 270 | { 271 | fmt::print("Sections:\n"); 272 | fmt::print( 273 | " {:<4} {:<20} {:<16} {:<8} {:<6} {:<6} {:<2} {:<4} {:<2} {:<4} {:<5}\n", 274 | "[Nr]", "Name", "Type", "Addr", "Off", "Size", "ES", "Flag", "Lk", "Info", "Align"); 275 | 276 | for (auto i = 0u; i < rpl.sections.size(); ++i) { 277 | auto §ion = rpl.sections[i]; 278 | auto type = formatSHT(section.header.type); 279 | auto flags = formatSHF(section.header.flags); 280 | 281 | fmt::print( 282 | " [{:>2}] {:<20} {:<16} {:08X} {:06X} {:06X} {:02X} {:>4} {:>2} {:>4} {:>5}\n", 283 | i, 284 | section.name, 285 | type, 286 | section.header.addr, 287 | section.header.offset, 288 | section.header.size, 289 | section.header.entsize, 290 | flags, 291 | section.header.link, 292 | section.header.info, 293 | section.header.addralign); 294 | } 295 | } 296 | 297 | void 298 | printFileInfo(const Rpl &rpl, 299 | const Section §ion) 300 | { 301 | auto &info = *reinterpret_cast(section.data.data()); 302 | fmt::print(" {:<20} = 0x{:08X}\n", "version", info.version); 303 | fmt::print(" {:<20} = 0x{:08X}\n", "textSize", info.textSize); 304 | fmt::print(" {:<20} = 0x{:X}\n", "textAlign", info.textAlign); 305 | fmt::print(" {:<20} = 0x{:08X}\n", "dataSize", info.dataSize); 306 | fmt::print(" {:<20} = 0x{:X}\n", "dataAlign", info.dataAlign); 307 | fmt::print(" {:<20} = 0x{:08X}\n", "loadSize", info.loadSize); 308 | fmt::print(" {:<20} = 0x{:X}\n", "loadAlign", info.loadAlign); 309 | fmt::print(" {:<20} = 0x{:X}\n", "tempSize", info.tempSize); 310 | fmt::print(" {:<20} = 0x{:X}\n", "trampAdjust", info.trampAdjust); 311 | fmt::print(" {:<20} = 0x{:X}\n", "trampAddition", info.trampAddition); 312 | fmt::print(" {:<20} = 0x{:08X}\n", "sdaBase", info.sdaBase); 313 | fmt::print(" {:<20} = 0x{:08X}\n", "sda2Base", info.sda2Base); 314 | fmt::print(" {:<20} = 0x{:08X}\n", "stackSize", info.stackSize); 315 | fmt::print(" {:<20} = 0x{:08X}\n", "heapSize", info.heapSize); 316 | 317 | if (info.filename) { 318 | auto filename = section.data.data() + info.filename; 319 | fmt::print(" {:<20} = {}\n", "filename", filename); 320 | } else { 321 | fmt::print(" {:<20} = 0\n", "filename"); 322 | } 323 | 324 | fmt::print(" {:<20} = 0x{:X}\n", "flags", info.flags); 325 | fmt::print(" {:<20} = 0x{:08X}\n", "minSdkVersion", info.minVersion); 326 | fmt::print(" {:<20} = {}\n", "compressionLevel", info.compressionLevel); 327 | fmt::print(" {:<20} = 0x{:X}\n", "fileInfoPad", info.fileInfoPad); 328 | fmt::print(" {:<20} = 0x{:X}\n", "sdkVersion", info.cafeSdkVersion); 329 | fmt::print(" {:<20} = 0x{:X}\n", "sdkRevision", info.cafeSdkRevision); 330 | fmt::print(" {:<20} = 0x{:X}\n", "tlsModuleIndex", info.tlsModuleIndex); 331 | fmt::print(" {:<20} = 0x{:X}\n", "tlsAlignShift", info.tlsAlignShift); 332 | fmt::print(" {:<20} = 0x{:X}\n", "runtimeFileInfoSize", info.runtimeFileInfoSize); 333 | 334 | if (info.tagOffset) { 335 | const char *tags = section.data.data() + info.tagOffset; 336 | fmt::print(" Tags:\n"); 337 | 338 | while (*tags) { 339 | auto key = tags; 340 | tags += strlen(tags) + 1; 341 | auto value = tags; 342 | tags += strlen(tags) + 1; 343 | 344 | fmt::print(" \"{}\" = \"{}\"\n", key, value); 345 | } 346 | } 347 | } 348 | 349 | void 350 | printRela(const Rpl &rpl, 351 | const Section §ion) 352 | { 353 | fmt::print( 354 | " {:<8} {:<8} {:<16} {:<8} {}\n", "Offset", "Info", "Type", "Value", "Name + Addend"); 355 | 356 | auto &symSec = rpl.sections[section.header.link]; 357 | auto symbols = reinterpret_cast(symSec.data.data()); 358 | auto &symStrTab = rpl.sections[symSec.header.link]; 359 | 360 | auto relas = reinterpret_cast(section.data.data()); 361 | auto count = section.data.size() / sizeof(elf::Rela); 362 | 363 | for (auto i = 0u; i < count; ++i) { 364 | auto &rela = relas[i]; 365 | 366 | auto index = rela.info >> 8; 367 | auto type = rela.info & 0xff; 368 | auto typeName = formatRelType(type); 369 | 370 | auto symbol = symbols[index]; 371 | auto name = reinterpret_cast(symStrTab.data.data()) + symbol.name; 372 | 373 | fmt::print( 374 | " {:08X} {:08X} {:<16} {:08X} {} + {:X}\n", 375 | rela.offset, 376 | rela.info, 377 | typeName, 378 | symbol.value, 379 | name, 380 | rela.addend); 381 | } 382 | } 383 | 384 | void 385 | printSymTab(const Rpl &rpl, 386 | const Section §ion) 387 | { 388 | auto strTab = reinterpret_cast(rpl.sections[section.header.link].data.data()); 389 | 390 | fmt::print( 391 | " {:<4} {:<8} {:<6} {:<8} {:<8} {:<3} {}\n", 392 | "Num", "Value", "Size", "Type", "Bind", "Ndx", "Name"); 393 | 394 | auto id = 0u; 395 | auto symbols = reinterpret_cast(section.data.data()); 396 | auto count = section.data.size() / sizeof(elf::Symbol); 397 | 398 | for (auto i = 0u; i < count; ++i) { 399 | auto &symbol = symbols[i]; 400 | 401 | auto name = strTab + symbol.name; 402 | auto binding = symbol.info >> 4; 403 | auto type = symbol.info & 0xf; 404 | auto typeName = formatSymType(type); 405 | auto bindingName = formatSymBinding(binding); 406 | auto ndx = formatSymShndx(symbol.shndx); 407 | 408 | fmt::print( 409 | " {:>4} {:08X} {:>6} {:<8} {:<8} {:>3} {}\n", 410 | id, symbol.value, symbol.size, typeName, bindingName, ndx, name); 411 | 412 | ++id; 413 | } 414 | } 415 | 416 | void 417 | printRplImports(const Rpl &rpl, 418 | const Section §ion) 419 | { 420 | auto sectionIndex = getSectionIndex(rpl, section); 421 | auto import = reinterpret_cast(section.data.data()); 422 | fmt::print(" {:<20} = {}\n", "name", import->name); 423 | fmt::print(" {:<20} = 0x{:08X}\n", "signature", import->signature); 424 | fmt::print(" {:<20} = {}\n", "count", import->count); 425 | 426 | if (import->count) { 427 | for (auto &symSection : rpl.sections) { 428 | if (symSection.header.type != elf::SHT_SYMTAB) { 429 | continue; 430 | } 431 | 432 | auto symbols = reinterpret_cast(symSection.data.data()); 433 | auto count = symSection.data.size() / sizeof(elf::Symbol); 434 | auto strTab = reinterpret_cast(rpl.sections[symSection.header.link].data.data()); 435 | 436 | for (auto i = 0u; i < count; ++i) { 437 | auto &symbol = symbols[i]; 438 | auto type = symbol.info & 0xf; 439 | 440 | if (symbol.shndx == sectionIndex && 441 | (type == elf::STT_FUNC || type == elf::STT_OBJECT)) { 442 | fmt::print(" {}\n", strTab + symbol.name); 443 | } 444 | } 445 | } 446 | } 447 | } 448 | 449 | void 450 | printRplCrcs(const Rpl &rpl, 451 | const Section §ion) 452 | { 453 | auto crcs = reinterpret_cast(section.data.data()); 454 | auto count = section.data.size() / sizeof(elf::RplCrc); 455 | 456 | for (auto i = 0u; i < count; ++i) { 457 | fmt::print(" [{:>2}] 0x{:08X} {}\n", i, crcs[i].crc, section.name); 458 | } 459 | } 460 | 461 | void 462 | printRplExports(const Rpl &rpl, 463 | const Section §ion) 464 | { 465 | auto exports = reinterpret_cast(section.data.data()); 466 | auto strTab = section.data.data(); 467 | fmt::print(" {:<20} = 0x{:08X}\n", "signature", exports->signature); 468 | fmt::print(" {:<20} = {}\n", "count", exports->count); 469 | 470 | for (auto i = 0u; i < exports->count; ++i) { 471 | // TLS exports have the high bit set in name for some unknown reason... 472 | auto name = strTab + (exports->exports[i].name & 0x7FFFFFFF); 473 | auto value = exports->exports[i].value; 474 | 475 | fmt::print(" 0x{:08X} {}\n", value, name); 476 | } 477 | } 478 | -------------------------------------------------------------------------------- /src/readrpl/print.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "readrpl.h" 3 | 4 | std::string 5 | formatSHT(uint32_t type); 6 | 7 | void 8 | printHeader(const Rpl &rpl); 9 | 10 | void 11 | printSectionSummary(const Rpl &rpl); 12 | 13 | void 14 | printFileInfo(const Rpl &rpl, 15 | const Section §ion); 16 | 17 | void 18 | printRela(const Rpl &rpl, 19 | const Section §ion); 20 | 21 | void 22 | printSymTab(const Rpl &rpl, 23 | const Section §ion); 24 | 25 | void 26 | printRplImports(const Rpl &rpl, 27 | const Section §ion); 28 | 29 | void 30 | printRplCrcs(const Rpl &rpl, 31 | const Section §ion); 32 | 33 | void 34 | printRplExports(const Rpl &rpl, 35 | const Section §ion); 36 | -------------------------------------------------------------------------------- /src/readrpl/readrpl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "elf.h" 3 | #include 4 | #include 5 | 6 | struct Section 7 | { 8 | elf::SectionHeader header; 9 | std::string name; 10 | std::vector data; 11 | }; 12 | 13 | struct Rpl 14 | { 15 | elf::Header header; 16 | uint32_t fileSize; 17 | std::vector
sections; 18 | }; 19 | 20 | uint32_t 21 | getSectionIndex(const Rpl &rpl, 22 | const Section §ion); 23 | -------------------------------------------------------------------------------- /src/readrpl/verify.cpp: -------------------------------------------------------------------------------- 1 | #include "verify.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static bool 8 | sValidateRelocsAddTable(const Rpl &rpl, 9 | const Section §ion) 10 | { 11 | const auto &header = section.header; 12 | if (!header.size) { 13 | return true; 14 | } 15 | 16 | auto entsize = static_cast(header.entsize); 17 | if (!entsize) { 18 | entsize = static_cast(sizeof(elf::Rela)); 19 | } 20 | 21 | if (entsize < sizeof(elf::Rela)) { 22 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002E); 23 | return false; 24 | } 25 | 26 | auto numRelas = (header.size / entsize); 27 | if (!numRelas) { 28 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000A); 29 | return false; 30 | } 31 | 32 | if (!header.link || header.link >= rpl.header.shnum) { 33 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000B); 34 | return false; 35 | } 36 | 37 | const auto &symbolSection = rpl.sections[header.link]; 38 | if (symbolSection.header.type != elf::SHT_SYMTAB) { 39 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000C); 40 | return false; 41 | } 42 | 43 | auto symEntsize = symbolSection.header.entsize ? 44 | static_cast(symbolSection.header.entsize) : 45 | static_cast(sizeof(elf::Symbol)); 46 | if (symEntsize < sizeof(elf::Symbol)) { 47 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002F); 48 | return false; 49 | } 50 | 51 | if (header.info >= rpl.header.shnum) { 52 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000D); 53 | return false; 54 | } 55 | 56 | const auto &targetSection = rpl.sections[header.info]; 57 | if (targetSection.header.type != elf::SHT_NULL) { 58 | auto numSymbols = symbolSection.data.size() / symEntsize; 59 | for (auto i = 0u; i < numRelas; ++i) { 60 | auto rela = reinterpret_cast(section.data.data() + i * entsize); 61 | if (rela->info && (rela->info >> 8) >= numSymbols) { 62 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000F); 63 | return false; 64 | } 65 | } 66 | } 67 | 68 | return true; 69 | } 70 | 71 | static bool 72 | sValidateSymbolTable(const Rpl &rpl, 73 | const Section §ion) 74 | { 75 | auto result = true; 76 | const auto &header = section.header; 77 | if (!header.size) { 78 | return true; 79 | } 80 | 81 | const Section *symStrTabSection = nullptr; 82 | if (header.link) { 83 | if (header.link >= rpl.header.shnum) { 84 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00001); 85 | return false; 86 | } 87 | 88 | symStrTabSection = &rpl.sections[header.link]; 89 | if (symStrTabSection->header.type != elf::SHT_STRTAB) { 90 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00002); 91 | return false; 92 | } 93 | } 94 | 95 | auto entsize = header.entsize ? 96 | static_cast(header.entsize) : 97 | static_cast(sizeof(elf::Symbol)); 98 | if (entsize < sizeof(elf::Symbol)) { 99 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002D); 100 | return false; 101 | } 102 | 103 | auto numSymbols = header.size / entsize; 104 | if (!numSymbols) { 105 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00003); 106 | result = false; 107 | } 108 | 109 | for (auto i = 0u; i < numSymbols; ++i) { 110 | auto symbol = reinterpret_cast(section.data.data() + i * entsize); 111 | 112 | if (symStrTabSection && 113 | symbol->name > symStrTabSection->data.size()) { 114 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00004); 115 | } 116 | 117 | auto type = symbol->info & 0xF; 118 | if (symbol->shndx && 119 | symbol->shndx < elf::SHN_LORESERVE && 120 | type != elf::STT_SECTION && 121 | type != elf::STT_FILE) { 122 | if (symbol->shndx >= rpl.header.shnum) { 123 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00005); 124 | result = false; 125 | } else if (type == elf::STT_OBJECT) { 126 | const auto &targetSection = rpl.sections[symbol->shndx]; 127 | auto targetSectionSize = targetSection.data.size() ? 128 | static_cast(targetSection.data.size()) : 129 | static_cast(targetSection.header.size); 130 | 131 | if (targetSectionSize && 132 | targetSection.header.flags & elf::SHF_ALLOC) { 133 | if (targetSection.header.type == elf::SHT_NULL) { 134 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00006); 135 | result = false; 136 | } 137 | 138 | auto position = symbol->value - targetSection.header.addr; 139 | if (position > targetSectionSize || position + symbol->size > targetSectionSize) { 140 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00007); 141 | result = false; 142 | } 143 | } 144 | } else if (type == elf::STT_FUNC) { 145 | const auto &targetSection = rpl.sections[symbol->shndx]; 146 | auto targetSectionSize = targetSection.data.size() ? 147 | static_cast(targetSection.data.size()) : 148 | static_cast(targetSection.header.size); 149 | 150 | if (targetSectionSize && 151 | targetSection.header.flags & elf::SHF_ALLOC) { 152 | if (targetSection.header.type == elf::SHT_NULL) { 153 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00008); 154 | result = false; 155 | } 156 | 157 | auto position = symbol->value - targetSection.header.addr; 158 | if (position > targetSectionSize || position + symbol->size > targetSectionSize) { 159 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00009); 160 | result = false; 161 | } 162 | } 163 | } 164 | } 165 | } 166 | 167 | return result; 168 | } 169 | 170 | /** 171 | * Equivalent to loader.elf ELFFILE_ValidateAndPrepare 172 | */ 173 | bool 174 | verifyFile(const Rpl &rpl) 175 | { 176 | const auto &header = rpl.header; 177 | auto result = true; 178 | 179 | if (rpl.fileSize < 0x104) { 180 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00018); 181 | return false; 182 | } 183 | 184 | if (header.magic != elf::HeaderMagic) { 185 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00019); 186 | result = false; 187 | } 188 | 189 | if (header.fileClass != elf::ELFCLASS32) { 190 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001A); 191 | result = false; 192 | } 193 | 194 | if (header.elfVersion > elf::EV_CURRENT) { 195 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001B); 196 | result = false; 197 | } 198 | 199 | if (!header.machine) { 200 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001C); 201 | result = false; 202 | } 203 | 204 | if (!header.version != 1) { 205 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001D); 206 | result = false; 207 | } 208 | 209 | auto ehsize = static_cast(header.ehsize); 210 | if (ehsize) { 211 | if (header.ehsize < sizeof(elf::Header)) { 212 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001E); 213 | result = false; 214 | } 215 | } else { 216 | ehsize = static_cast(sizeof(elf::Header)); 217 | } 218 | 219 | auto phoff = header.phoff; 220 | if (phoff && (phoff < ehsize || phoff >= rpl.fileSize)) { 221 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001F); 222 | result = false; 223 | } 224 | 225 | auto shoff = header.shoff; 226 | if (shoff && (shoff < ehsize || shoff >= rpl.fileSize)) { 227 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00020); 228 | result = false; 229 | } 230 | 231 | if (header.shstrndx && header.shstrndx >= header.shnum) { 232 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00021); 233 | result = false; 234 | } 235 | 236 | auto phentsize = header.phentsize ? 237 | static_cast(header.phentsize) : 238 | static_cast(32); 239 | if (header.phoff && 240 | (header.phoff + phentsize * header.phnum) > rpl.fileSize) { 241 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00022); 242 | result = false; 243 | } 244 | 245 | auto shentsize = header.shentsize ? 246 | static_cast(header.shentsize) : 247 | static_cast(sizeof(elf::SectionHeader)); 248 | if (header.shoff && 249 | (header.shoff + shentsize * header.shnum) > rpl.fileSize) { 250 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00023); 251 | result = false; 252 | } 253 | 254 | for (auto §ion : rpl.sections) { 255 | if (section.header.size && 256 | section.header.type != elf::SHT_NOBITS) { 257 | if (section.header.offset < ehsize) { 258 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00024); 259 | result = false; 260 | } 261 | 262 | if (section.header.offset >= shoff && 263 | section.header.offset < (shoff + header.shnum * shentsize)) { 264 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00027); 265 | result = false; 266 | } 267 | } 268 | } 269 | 270 | if (header.shstrndx) { 271 | const auto &shStrTabSection = rpl.sections[header.shstrndx]; 272 | if (shStrTabSection.header.type != elf::SHT_STRTAB) { 273 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002A); 274 | result = false; 275 | } else { 276 | for (auto §ion : rpl.sections) { 277 | if (section.header.name >= shStrTabSection.data.size()) { 278 | fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002B); 279 | result = false; 280 | } 281 | } 282 | } 283 | } 284 | 285 | for (const auto §ion : rpl.sections) { 286 | if (section.header.type == elf::SHT_RELA) { 287 | result = sValidateRelocsAddTable(rpl, section) && result; 288 | } else if (section.header.type == elf::SHT_SYMTAB) { 289 | result = sValidateSymbolTable(rpl, section) && result; 290 | } 291 | } 292 | 293 | return result; 294 | } 295 | 296 | 297 | /** 298 | * Verify values in SHT_RPL_CRCS 299 | */ 300 | bool 301 | verifyCrcs(const Rpl &rpl) 302 | { 303 | const elf::RplCrc *crcs = NULL; 304 | auto result = true; 305 | 306 | for (const auto §ion : rpl.sections) { 307 | if (section.header.type == elf::SHT_RPL_CRCS) { 308 | crcs = reinterpret_cast(section.data.data()); 309 | break; 310 | } 311 | } 312 | 313 | if (!crcs) { 314 | return false; 315 | } 316 | 317 | auto sectionIndex = 0u; 318 | for (const auto §ion : rpl.sections) { 319 | auto crc = uint32_t { 0u }; 320 | if (section.header.type != elf::SHT_RPL_CRCS && 321 | section.data.size()) { 322 | crc = crc32(0, Z_NULL, 0); 323 | crc = crc32(crc, reinterpret_cast(section.data.data()), section.data.size()); 324 | } 325 | 326 | if (crc != crcs[sectionIndex].crc) { 327 | fmt::print("Unexpected crc for section {}, read 0x{:08X} but calculated 0x{:08X}", 328 | sectionIndex, crcs[sectionIndex].crc, crc); 329 | result = false; 330 | } 331 | 332 | sectionIndex++; 333 | } 334 | 335 | return result; 336 | } 337 | 338 | 339 | /** 340 | * Equivalent to loader.elf LiCheckFileBounds 341 | */ 342 | bool 343 | verifyFileBounds(const Rpl &rpl) 344 | { 345 | auto result = true; 346 | auto dataMin = 0xFFFFFFFFu; 347 | auto dataMax = 0u; 348 | 349 | auto readMin = 0xFFFFFFFFu; 350 | auto readMax = 0u; 351 | 352 | auto textMin = 0xFFFFFFFFu; 353 | auto textMax = 0u; 354 | 355 | auto tempMin = 0xFFFFFFFFu; 356 | auto tempMax = 0u; 357 | 358 | for (const auto §ion : rpl.sections) { 359 | if (section.header.size == 0 || 360 | section.header.type == elf::SHT_RPL_FILEINFO || 361 | section.header.type == elf::SHT_RPL_CRCS || 362 | section.header.type == elf::SHT_NOBITS || 363 | section.header.type == elf::SHT_RPL_IMPORTS) { 364 | continue; 365 | } 366 | 367 | if ((section.header.flags & elf::SHF_EXECINSTR) && 368 | section.header.type != elf::SHT_RPL_EXPORTS) { 369 | textMin = std::min(textMin, section.header.offset); 370 | textMax = std::max(textMax, section.header.offset + section.header.size); 371 | } else { 372 | if (section.header.flags & elf::SHF_ALLOC) { 373 | if (section.header.flags & elf::SHF_WRITE) { 374 | dataMin = std::min(dataMin, section.header.offset); 375 | dataMax = std::max(dataMax, section.header.offset + section.header.size); 376 | } else { 377 | readMin = std::min(readMin, section.header.offset); 378 | readMax = std::max(readMax, section.header.offset + section.header.size); 379 | } 380 | } else { 381 | tempMin = std::min(tempMin, section.header.offset); 382 | tempMax = std::max(tempMax, section.header.offset + section.header.size); 383 | } 384 | } 385 | } 386 | 387 | if (dataMin == 0xFFFFFFFFu) { 388 | dataMin = (rpl.header.shnum * rpl.header.shentsize) + rpl.header.shoff; 389 | dataMax = dataMin; 390 | } 391 | 392 | if (readMin == 0xFFFFFFFFu) { 393 | readMin = dataMax; 394 | readMax = dataMax; 395 | } 396 | 397 | if (textMin == 0xFFFFFFFFu) { 398 | textMin = readMax; 399 | textMax = readMax; 400 | } 401 | 402 | if (tempMin == 0xFFFFFFFFu) { 403 | tempMin = textMax; 404 | tempMax = textMax; 405 | } 406 | 407 | if (dataMin < rpl.header.shoff) { 408 | fmt::print("*** SecHrs, FileInfo, or CRCs in bad spot in file. Return %d.\n", -470026); 409 | result = false; 410 | } 411 | 412 | // Data 413 | if (dataMin > dataMax) { 414 | fmt::print("*** DataMin > DataMax. break.\n"); 415 | result = false; 416 | } 417 | 418 | if (dataMin > readMin) { 419 | fmt::print("*** DataMin > ReadMin. break.\n"); 420 | result = false; 421 | } 422 | 423 | if (dataMax > readMin) { 424 | fmt::print("*** DataMax > ReadMin, break.\n"); 425 | result = false; 426 | } 427 | 428 | // Read 429 | if (readMin > readMax) { 430 | fmt::print("*** ReadMin > ReadMax. break.\n"); 431 | result = false; 432 | } 433 | 434 | if (readMin > textMin) { 435 | fmt::print("*** ReadMin > TextMin. break.\n"); 436 | result = false; 437 | } 438 | 439 | if (readMax > textMin) { 440 | fmt::print("*** ReadMax > TextMin. break.\n"); 441 | result = false; 442 | } 443 | 444 | // Text 445 | if (textMin > textMax) { 446 | fmt::print("*** TextMin > TextMax. break.\n"); 447 | result = false; 448 | } 449 | 450 | if (textMin > tempMin) { 451 | fmt::print("*** TextMin > TempMin. break.\n"); 452 | result = false; 453 | } 454 | 455 | if (textMax > tempMin) { 456 | fmt::print("*** TextMax > TempMin. break.\n"); 457 | result = false; 458 | } 459 | 460 | // Temp 461 | if (tempMin > tempMax) { 462 | fmt::print("*** TempMin > TempMax. break.\n"); 463 | result = false; 464 | } 465 | 466 | if (!result) { 467 | fmt::print("dataMin = 0x{:08X}\n", dataMin); 468 | fmt::print("dataMax = 0x{:08X}\n", dataMax); 469 | fmt::print("readMin = 0x{:08X}\n", readMin); 470 | fmt::print("readMax = 0x{:08X}\n", readMax); 471 | fmt::print("textMin = 0x{:08X}\n", textMin); 472 | fmt::print("textMax = 0x{:08X}\n", textMax); 473 | fmt::print("tempMin = 0x{:08X}\n", tempMin); 474 | fmt::print("tempMax = 0x{:08X}\n", tempMax); 475 | } 476 | 477 | return result; 478 | } 479 | 480 | 481 | /** 482 | * Check that the rpl only uses relocation types which are supported by 483 | * loader.elf 484 | */ 485 | bool 486 | verifyRelocationTypes(const Rpl &rpl) 487 | { 488 | std::set unsupportedTypes; 489 | 490 | for (auto §ion : rpl.sections) { 491 | if (section.header.type != elf::SHT_RELA) { 492 | continue; 493 | } 494 | 495 | auto &symbolSection = rpl.sections[section.header.link]; 496 | auto &targetSection = rpl.sections[section.header.info]; 497 | auto rels = reinterpret_cast(section.data.data()); 498 | auto numRels = section.data.size() / sizeof(elf::Rela); 499 | 500 | for (auto i = 0u; i < numRels; ++i) { 501 | auto info = rels[i].info; 502 | auto addend = rels[i].addend; 503 | auto offset = rels[i].offset; 504 | auto index = info >> 8; 505 | auto type = info & 0xFF; 506 | 507 | switch (type) { 508 | case elf::R_PPC_NONE: 509 | case elf::R_PPC_ADDR32: 510 | case elf::R_PPC_ADDR16_LO: 511 | case elf::R_PPC_ADDR16_HI: 512 | case elf::R_PPC_ADDR16_HA: 513 | case elf::R_PPC_REL24: 514 | case elf::R_PPC_REL14: 515 | case elf::R_PPC_DTPMOD32: 516 | case elf::R_PPC_DTPREL32: 517 | case elf::R_PPC_EMB_SDA21: 518 | case elf::R_PPC_EMB_RELSDA: 519 | case elf::R_PPC_DIAB_SDA21_LO: 520 | case elf::R_PPC_DIAB_SDA21_HI: 521 | case elf::R_PPC_DIAB_SDA21_HA: 522 | case elf::R_PPC_DIAB_RELSDA_LO: 523 | case elf::R_PPC_DIAB_RELSDA_HI: 524 | case elf::R_PPC_DIAB_RELSDA_HA: 525 | case elf::R_PPC_GHS_REL16_HA: 526 | case elf::R_PPC_GHS_REL16_HI: 527 | case elf::R_PPC_GHS_REL16_LO: 528 | // All valid relocations on Wii U, do nothing 529 | break; 530 | default: 531 | // Only print error once per type 532 | if (!unsupportedTypes.count(type)) { 533 | fmt::print("Unsupported relocation type {}\n", type); 534 | unsupportedTypes.insert(type); 535 | } 536 | } 537 | } 538 | } 539 | 540 | return unsupportedTypes.empty(); 541 | } 542 | 543 | 544 | /** 545 | * Verify that section.addr is aligned by section.addralign 546 | */ 547 | bool 548 | verifySectionAlignment(const Rpl &rpl) 549 | { 550 | auto result = true; 551 | for (auto §ion : rpl.sections) { 552 | if (!align_check(section.header.addr, section.header.addralign)) { 553 | fmt::print("Unaligned section {}, addr {}, addralign {}", 554 | getSectionIndex(rpl, section), 555 | section.header.addr, 556 | section.header.addralign); 557 | result = false; 558 | } 559 | } 560 | return result; 561 | } 562 | 563 | 564 | bool 565 | verifySectionOrder(const Rpl &rpl) 566 | { 567 | auto lastSection = rpl.sections[rpl.header.shnum - 1]; 568 | auto penultimateSection = rpl.sections[rpl.header.shnum - 2]; 569 | 570 | 571 | if (lastSection.header.type != elf::SHT_RPL_FILEINFO || 572 | (lastSection.header.flags & elf::SHF_DEFLATED)) { 573 | fmt::print("***shnum-1 section type = 0x{:08X}, flags=0x{:08X}\n", 574 | lastSection.header.type, 575 | lastSection.header.flags); 576 | } 577 | 578 | if (penultimateSection.header.type != elf::SHT_RPL_CRCS || 579 | (penultimateSection.header.flags & elf::SHF_DEFLATED)) { 580 | fmt::print("***shnum-2 section type = 0x{:08X}, flags=0x{:08X}\n", 581 | penultimateSection.header.type, 582 | penultimateSection.header.flags); 583 | } 584 | 585 | return true; 586 | } 587 | -------------------------------------------------------------------------------- /src/readrpl/verify.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "readrpl.h" 3 | 4 | bool 5 | verifyFile(const Rpl &rpl); 6 | 7 | bool 8 | verifyCrcs(const Rpl &rpl); 9 | 10 | bool 11 | verifyFileBounds(const Rpl &rpl); 12 | 13 | bool 14 | verifyRelocationTypes(const Rpl &rpl); 15 | 16 | bool 17 | verifySectionAlignment(const Rpl &rpl); 18 | 19 | bool 20 | verifySectionOrder(const Rpl &rpl); 21 | -------------------------------------------------------------------------------- /src/rplexportgen/rplexportgen.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | /* 17 | .extern __preinit_user 18 | 19 | .section .fexports, "", @0x80000001 20 | .align 4 21 | 22 | .long 1 23 | .long 0x13371337 24 | 25 | .long __preinit_user 26 | .long 0x10 27 | 28 | .string "__preinit_user" 29 | .byte 0 30 | */ 31 | 32 | enum class ReadMode 33 | { 34 | INVALID, 35 | TEXT, 36 | DATA, 37 | NAME 38 | }; 39 | 40 | void 41 | writeExports(std::ofstream &out, 42 | bool isData, 43 | const std::vector &exports) 44 | { 45 | // Calculate signature 46 | uint32_t signature = crc32(0, Z_NULL, 0); 47 | for (const auto &name : exports) { 48 | signature = crc32(signature, reinterpret_cast(name.data()), name.size() + 1); 49 | } 50 | 51 | // Write out .extern to declare the symbols 52 | for (const auto &name : exports) { 53 | out << ".extern " << name << std::endl; 54 | } 55 | out << std::endl; 56 | 57 | // Write out header 58 | if (isData) { 59 | out << ".section .dexports, \"a\", @0x80000001" << std::endl; 60 | } else { 61 | out << ".section .fexports, \"ax\", @0x80000001" << std::endl; 62 | } 63 | 64 | out << ".align 4" << std::endl; 65 | out << std::endl; 66 | 67 | out << ".long " << exports.size() << std::endl; 68 | out << ".long 0x" << std::hex << signature << std::endl; 69 | out << std::endl; 70 | 71 | // Write out each export 72 | auto nameOffset = 8 + 8 * exports.size(); 73 | for (const auto &name : exports) { 74 | out << ".long " << name << std::endl; 75 | out << ".long 0x" << std::hex << nameOffset << std::endl; 76 | nameOffset += name.size() + 1; 77 | } 78 | out << std::endl; 79 | 80 | // Write out the strings 81 | for (const auto &name : exports) { 82 | out << ".string \"" << name << "\"" << std::endl; 83 | nameOffset += name.size() + 1; 84 | } 85 | out << std::endl; 86 | } 87 | 88 | int main(int argc, char **argv) 89 | { 90 | std::vector funcExports, dataExports; 91 | ReadMode readMode = ReadMode::INVALID; 92 | 93 | if (argc < 3) { 94 | std::cout << argv[0] << " " << std::endl; 95 | return 0; 96 | } 97 | 98 | { 99 | std::ifstream in; 100 | in.open(argv[1]); 101 | 102 | if (!in.is_open()) { 103 | std::cout << "Could not open file " << argv[1] << " for reading" << std::endl; 104 | return -1; 105 | } 106 | 107 | std::string line; 108 | while (std::getline(in, line)) { 109 | // Trim comments 110 | std::size_t commentOffset = line.find("//"); 111 | if (commentOffset != std::string::npos) { 112 | line = line.substr(0, commentOffset); 113 | } 114 | 115 | // Trim whitespace 116 | line = trim(line); 117 | 118 | // Skip blank lines 119 | if (line.length() == 0) { 120 | continue; 121 | } 122 | 123 | // Look for section headers 124 | if (line[0] == ':') { 125 | if (line.substr(1) == "TEXT") { 126 | readMode = ReadMode::TEXT; 127 | } else if (line.substr(1) == "DATA") { 128 | readMode = ReadMode::DATA; 129 | } else if (line.substr(1, 4) == "NAME") { 130 | readMode = ReadMode::NAME; 131 | } else { 132 | std::cout << "Unexpected section type" << std::endl; 133 | return -1; 134 | } 135 | continue; 136 | } 137 | 138 | if (readMode == ReadMode::TEXT) { 139 | funcExports.push_back(line); 140 | } else if (readMode == ReadMode::DATA) { 141 | dataExports.push_back(line); 142 | } else if (readMode == ReadMode::NAME) { 143 | // We can ignore name in rplexportgen 144 | } else { 145 | std::cout << "Unexpected section data" << std::endl; 146 | return -1; 147 | } 148 | } 149 | } 150 | 151 | // Exports must be in alphabetical order because loader.elf uses binary search 152 | std::sort(funcExports.begin(), funcExports.end()); 153 | std::sort(dataExports.begin(), dataExports.end()); 154 | 155 | { 156 | std::ofstream out; 157 | out.open(argv[2]); 158 | 159 | if (!out.is_open()) { 160 | std::cout << "Could not open file " << argv[2] << " for writing" << std::endl; 161 | return -1; 162 | } 163 | 164 | if (funcExports.size() > 0) { 165 | writeExports(out, false, funcExports); 166 | } 167 | 168 | if (dataExports.size() > 0) { 169 | writeExports(out, true, dataExports); 170 | } 171 | } 172 | 173 | return 0; 174 | } 175 | -------------------------------------------------------------------------------- /src/rplimportgen/rplimportgen.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "rplwrap.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | enum class ReadMode 18 | { 19 | INVALID, 20 | TEXT, 21 | TEXT_WRAP, 22 | DATA, 23 | DATA_WRAP, 24 | }; 25 | 26 | static void 27 | writeExports(std::ofstream &out, 28 | const std::string &moduleName, 29 | bool isData, 30 | const std::vector &exports) 31 | { 32 | if (isData) { 33 | out << ".section .dimport_" << moduleName << ", \"a\", @0x80000002" << std::endl; 34 | } else { 35 | out << ".section .fimport_" << moduleName << ", \"ax\", @0x80000002" << std::endl; 36 | } 37 | 38 | out << ".align 4" << std::endl; 39 | out << std::endl; 40 | 41 | // Usually the symbol count, but isn't checked on hardware. 42 | // Spoofed to allow ld to garbage-collect later. 43 | out << ".long 1" << std::endl; 44 | // Supposed to be a crc32 of the imports. Again, not actually checked. 45 | out << ".long 0x00000000" << std::endl; 46 | out << std::endl; 47 | 48 | // Align module name up to 8 bytes 49 | auto moduleNameSize = (moduleName.length() + 1 + 7) & ~7; 50 | 51 | // Setup name data 52 | std::vector secData; 53 | secData.resize(moduleNameSize / 4, 0); 54 | memcpy(secData.data(), moduleName.c_str(), moduleName.length()); 55 | 56 | // Add name data 57 | for (uint32_t data : secData) { 58 | out << ".long 0x" << std::hex << byte_swap(data) << std::endl; 59 | } 60 | out << std::endl; 61 | 62 | const char *type = isData ? "@object" : "@function"; 63 | 64 | for (auto i = 0; i < exports.size(); ++i) { 65 | if (i < exports.size()) { 66 | // Basically do -ffunction-sections 67 | if (isData) { 68 | out << ".section .dimport_" << moduleName << "." << exports[i] << ", \"a\", @0x80000002" << std::endl; 69 | } else { 70 | out << ".section .fimport_" << moduleName << "." << exports[i] << ", \"ax\", @0x80000002" << std::endl; 71 | } 72 | out << ".global " << exports[i] << std::endl; 73 | out << ".type " << exports[i] << ", " << type << std::endl; 74 | out << exports[i] << ":" << std::endl; 75 | } 76 | out << ".long 0x0" << std::endl; 77 | out << ".long 0x0" << std::endl; 78 | out << std::endl; 79 | } 80 | } 81 | 82 | static void 83 | writeLinkerScript(std::ofstream &out, 84 | const std::string &name) 85 | { 86 | out << "SECTIONS" << std::endl; 87 | out << "{" << std::endl; 88 | out << " .fimport_" << name << " ALIGN(16) : {" << std::endl; 89 | out << " KEEP ( *(.fimport_" << name << ") )" << std::endl; 90 | out << " *(.fimport_" << name << ".*)" << std::endl; 91 | out << " } > loadmem" << std::endl; 92 | out << " .dimport_" << name << " ALIGN(16) : {" << std::endl; 93 | out << " KEEP ( *(.dimport_" << name << ") )" << std::endl; 94 | out << " *(.dimport_" << name << ".*)" << std::endl; 95 | out << " } > loadmem" << std::endl; 96 | out << "}" << std::endl; 97 | } 98 | 99 | int 100 | main(int argc, char **argv) 101 | { 102 | std::string moduleName; 103 | std::vector funcExports, dataExports; 104 | ReadMode readMode = ReadMode::INVALID; 105 | 106 | if (argc < 3) { 107 | std::cout << argv[0] << " []" << std::endl; 108 | return 0; 109 | } 110 | 111 | { 112 | std::ifstream in; 113 | in.open(argv[1]); 114 | 115 | if (!in.is_open()) { 116 | std::cout << "Could not open file " << argv[1] << " for reading" << std::endl; 117 | return -1; 118 | } 119 | 120 | std::string line; 121 | while (std::getline(in, line)) { 122 | // Trim comments 123 | std::size_t commentOffset = line.find("//"); 124 | if (commentOffset != std::string::npos) { 125 | line = line.substr(0, commentOffset); 126 | } 127 | 128 | // Trim whitespace 129 | line = trim(line); 130 | 131 | // Skip blank lines 132 | if (line.length() == 0) { 133 | continue; 134 | } 135 | 136 | // Look for section headers 137 | if (line[0] == ':') { 138 | if (line.substr(1) == "TEXT") { 139 | readMode = ReadMode::TEXT; 140 | } else if (line.substr(1) == "TEXT_WRAP") { 141 | readMode = ReadMode::TEXT_WRAP; 142 | } else if (line.substr(1) == "DATA") { 143 | readMode = ReadMode::DATA; 144 | } else if (line.substr(1) == "DATA_WRAP") { 145 | readMode = ReadMode::DATA_WRAP; 146 | } else if (line.substr(1, 4) == "NAME") { 147 | moduleName = line.substr(6); 148 | } else { 149 | std::cout << "Unexpected section type" << std::endl; 150 | return -1; 151 | } 152 | continue; 153 | } 154 | 155 | if (readMode == ReadMode::TEXT) { 156 | funcExports.push_back(line); 157 | } else if (readMode == ReadMode::TEXT_WRAP) { 158 | funcExports.push_back(std::string(RPLWRAP_PREFIX) + line); 159 | } else if (readMode == ReadMode::DATA) { 160 | dataExports.push_back(line); 161 | } else if (readMode == ReadMode::DATA_WRAP) { 162 | dataExports.push_back(std::string(RPLWRAP_PREFIX) + line); 163 | } else { 164 | std::cout << "Unexpected section data" << std::endl; 165 | return -1; 166 | } 167 | } 168 | } 169 | 170 | { 171 | std::ofstream out; 172 | out.open(argv[2]); 173 | 174 | if (!out.is_open()) { 175 | std::cout << "Could not open file " << argv[2] << " for writing" << std::endl; 176 | return -1; 177 | } 178 | 179 | if (funcExports.size() > 0) { 180 | writeExports(out, moduleName, false, funcExports); 181 | } 182 | 183 | if (dataExports.size() > 0) { 184 | writeExports(out, moduleName, true, dataExports); 185 | } 186 | } 187 | 188 | if (argc > 3) { 189 | std::ofstream out; 190 | out.open(argv[3]); 191 | 192 | if (!out.is_open()) { 193 | std::cout << "Could not open file " << argv[3] << " for writing" << std::endl; 194 | return -1; 195 | } 196 | 197 | writeLinkerScript(out, moduleName); 198 | } 199 | 200 | return 0; 201 | } 202 | -------------------------------------------------------------------------------- /src/udplogserver/main.cpp: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | #include 3 | #else 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #define SERVER_PORT 4405 17 | 18 | int main(int argc, char **argv) 19 | { 20 | struct sockaddr_in addr; 21 | unsigned short port = SERVER_PORT; 22 | 23 | if (argc == 2) { 24 | port = atoi(argv[1]); 25 | } 26 | 27 | #ifdef _WIN32 28 | WSADATA wsaData; 29 | if (WSAStartup(MAKEWORD(2, 2), &wsaData) == SOCKET_ERROR) { 30 | return -1; 31 | } 32 | #endif 33 | 34 | // Create socket 35 | auto fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 36 | #ifdef _WIN32 37 | if (fd == INVALID_SOCKET) { 38 | WSACleanup(); 39 | #else 40 | if (fd < 0) { 41 | #endif 42 | return -1; 43 | } 44 | 45 | // Set non blocking 46 | #ifdef _WIN32 47 | u_long mode = 1; 48 | ioctlsocket(fd, FIONBIO, &mode); 49 | #else 50 | int flags = fcntl(fd, F_GETFL, 0); 51 | fcntl(fd, F_SETFL, flags | O_NONBLOCK); 52 | #endif 53 | 54 | // Bind socket 55 | memset(&addr, 0, sizeof(addr)); 56 | addr.sin_family = AF_INET; 57 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 58 | addr.sin_port = htons(port); 59 | if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 60 | #ifdef _WIN32 61 | closesocket(fd); 62 | WSACleanup(); 63 | #else 64 | close(fd); 65 | #endif 66 | return -1; 67 | } 68 | 69 | // Receive data 70 | char buffer[2048]; 71 | bool running = true; 72 | 73 | while (running) { 74 | fd_set fdsRead; 75 | FD_ZERO(&fdsRead); 76 | FD_SET(fd, &fdsRead); 77 | 78 | struct timeval tv; 79 | tv.tv_sec = 0; 80 | tv.tv_usec = 10000; 81 | 82 | if (select(fd + 1, &fdsRead, NULL, NULL, &tv) == 1) { 83 | struct sockaddr_in from; 84 | #ifdef _WIN32 85 | int fromLen = sizeof(from); 86 | #else 87 | socklen_t fromLen = sizeof(from); 88 | #endif 89 | int recvd = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *) &from, &fromLen); 90 | 91 | if (recvd > 0) { 92 | buffer[recvd] = 0; 93 | std::cout << buffer; 94 | std::cout.flush(); 95 | } 96 | } 97 | } 98 | 99 | #ifdef _WIN32 100 | closesocket(fd); 101 | WSACleanup(); 102 | #else 103 | close(fd); 104 | #endif 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/BufferFileEntry.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "BufferFileEntry.h" 3 | 4 | void BufferFileEntry::write(FILE *f_out, off_t base_offset) { 5 | printf("Writing %s...\n", getFullPath().c_str()); 6 | 7 | if(fseeko64(f_out, base_offset + this->offset + ROMFS_FILEPARTITION_OFS, SEEK_SET) != 0){ 8 | fprintf(stderr, "Failed to seek!\n"); 9 | exit(EXIT_FAILURE); 10 | } 11 | 12 | if (fwrite(&this->buffer[0], 1, this->size , f_out) != this->size) { 13 | fprintf(stderr, "Failed to write to output!\n"); 14 | exit(EXIT_FAILURE); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/BufferFileEntry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FileEntry.h" 4 | 5 | class BufferFileEntry final : public FileEntry { 6 | public: 7 | BufferFileEntry(std::string &&name, std::vector &&data) : FileEntry(std::move(name)) { 8 | this->buffer = std::move(data); 9 | this->size = buffer.size(); 10 | } 11 | 12 | void write(FILE *f_out, off_t base_offset) override; 13 | 14 | private: 15 | std::vector buffer; 16 | }; 17 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/DirectoryEntry.cpp: -------------------------------------------------------------------------------- 1 | #include "DirectoryEntry.h" 2 | #include 3 | #include "../utils/utils.h" 4 | #include "../services/RomFSService.h" 5 | 6 | bool DirectoryEntry::addChild(NodeEntry *file) { 7 | if (file) { 8 | children.push_back(file); 9 | file->setParent(this); 10 | return true; 11 | } 12 | return false; 13 | } 14 | 15 | void DirectoryEntry::calculateDirOffsets(romfs_ctx_t *romfs_ctx, uint32_t *entry_offset) { 16 | if (!this->entry_offset_set) { 17 | this->updateEntryOffset(entry_offset); 18 | this->entry_offset_set = true; 19 | } 20 | 21 | for (auto const &e : children) { 22 | if (e->isDirNode()) { 23 | auto curDirEntry = static_cast(e); 24 | curDirEntry->calculateDirOffsets(romfs_ctx, entry_offset); 25 | } 26 | } 27 | } 28 | 29 | void DirectoryEntry::calculateFileOffsets(romfs_ctx_t *romfs_ctx, uint32_t *entry_offset) { 30 | for (auto const &e : children) { 31 | if (e->isDirNode()) { 32 | e->calculateFileOffsets(romfs_ctx, entry_offset); 33 | } 34 | } 35 | for (auto const &e : children) { 36 | if (e->isFileNode()) { 37 | e->calculateFileOffsets(romfs_ctx, entry_offset); 38 | } 39 | } 40 | } 41 | 42 | void DirectoryEntry::updateEntryOffset(uint32_t *entry_offset) { 43 | if (entry_offset) { 44 | this->entry_offset = *entry_offset; 45 | *entry_offset += 0x18 + align(getName().size(), 4); 46 | } 47 | } 48 | 49 | void DirectoryEntry::populate(romfs_infos_t *romfs_infos) { 50 | romfs_direntry_t *cur_entry = romfs::GetDirEntry(romfs_infos->dir_table, this->entry_offset); 51 | cur_entry->parent = be_word(this->getParent()->entry_offset); 52 | cur_entry->sibling = be_word(this->sibling == nullptr ? ROMFS_ENTRY_EMPTY : this->sibling->entry_offset); 53 | cur_entry->child = be_word(this->dirChild == nullptr ? ROMFS_ENTRY_EMPTY : this->dirChild->entry_offset); 54 | cur_entry->file = be_word(this->fileChild == nullptr ? ROMFS_ENTRY_EMPTY : this->fileChild->entry_offset); 55 | 56 | uint32_t name_size = getName().size(); 57 | uint32_t hash = romfs::CalcPathHash(this->getParent()->entry_offset, reinterpret_cast(("/" + getName()).c_str()), 1, name_size); 58 | 59 | cur_entry->hash = romfs_infos->dir_hash_table[hash % romfs_infos->dir_hash_table_entry_count]; 60 | romfs_infos->dir_hash_table[hash % romfs_infos->dir_hash_table_entry_count] = be_word(this->entry_offset); 61 | 62 | cur_entry->name_size = name_size; 63 | 64 | memcpy(cur_entry->name, getName().c_str(), name_size); 65 | 66 | cur_entry->name_size = be_word(cur_entry->name_size); 67 | 68 | for (auto const &e : children) { 69 | e->populate(romfs_infos); 70 | } 71 | } 72 | 73 | void DirectoryEntry::updateSiblingAndChildEntries() { 74 | FileEntry *lastChild = nullptr; 75 | DirectoryEntry *lastDir = nullptr; 76 | for (auto const &e : children) { 77 | if (e->isDirNode()) { 78 | auto curDirEntry = static_cast(e); 79 | 80 | if (lastDir == nullptr) { 81 | this->dirChild = curDirEntry; 82 | } else { 83 | lastDir->sibling = curDirEntry; 84 | } 85 | lastDir = curDirEntry; 86 | curDirEntry->updateSiblingAndChildEntries(); 87 | } else if (e->isFileNode()) { 88 | auto curFileEntry = static_cast(e); 89 | if (lastChild == nullptr) { 90 | this->fileChild = curFileEntry; 91 | } else { 92 | lastChild->sibling = curFileEntry; 93 | } 94 | lastChild = curFileEntry; 95 | } 96 | } 97 | } 98 | 99 | std::string DirectoryEntry::getFullPath() { 100 | return NodeEntry::getFullPath() + OS_PATH_SEPARATOR; 101 | } 102 | 103 | void DirectoryEntry::write(FILE *pIobuf, off_t offset) { 104 | for (auto const &e : children) { 105 | e->write(pIobuf, offset); 106 | } 107 | } 108 | 109 | void DirectoryEntry::clearChildren() { 110 | this->children.clear(); 111 | } 112 | 113 | void DirectoryEntry::moveChildren(DirectoryEntry &dirInput) { 114 | for (auto const &e : dirInput.getChildren()) { 115 | this->addChild(e); 116 | } 117 | dirInput.clearChildren(); 118 | } 119 | 120 | void DirectoryEntry::fillRomFSInformation(romfs_ctx_t *romfs_ctx) { 121 | romfs_ctx->num_dirs++; 122 | romfs_ctx->dir_table_size += 0x18 + align(getName().size(), 4); 123 | 124 | for (auto const &e : getChildren()) { 125 | e->fillRomFSInformation(romfs_ctx); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/DirectoryEntry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "NodeEntry.h" 4 | #include "FileEntry.h" 5 | #include "../services/RomFSStructs.h" 6 | 7 | class DirectoryEntry : public NodeEntry { 8 | public: 9 | ~DirectoryEntry() override { 10 | for (auto const &e : children) { 11 | delete e; 12 | } 13 | children.clear(); 14 | } 15 | 16 | explicit DirectoryEntry(std::string &&name) : NodeEntry(std::move(name), true) { 17 | 18 | } 19 | 20 | const std::vector &getChildren() const { 21 | return children; 22 | } 23 | 24 | std::string getFullPath() override; 25 | 26 | bool addChild(NodeEntry *file); 27 | 28 | void printRecursive(int indentation) override { 29 | NodeEntry::printRecursive(indentation); 30 | 31 | for (auto const &e : children) { 32 | e->printRecursive(indentation + 3); 33 | } 34 | } 35 | 36 | void calculateFileOffsets(romfs_ctx_t *ptr, uint32_t *entry_offset) override; 37 | 38 | virtual void calculateDirOffsets(romfs_ctx_t *ptr, uint32_t *entry_offset); 39 | 40 | void populate(romfs_infos_t *romfs_infos) override; 41 | 42 | virtual void updateSiblingAndChildEntries(); 43 | 44 | void write(FILE *pIobuf, off_t offset) override; 45 | 46 | virtual void moveChildren(DirectoryEntry &dirInput); 47 | 48 | void clearChildren(); 49 | 50 | void fillRomFSInformation(romfs_ctx_t *romfs_ctx) override; 51 | protected: 52 | virtual void updateEntryOffset(uint32_t *entry_offset); 53 | 54 | std::vector children; 55 | 56 | DirectoryEntry *sibling = nullptr; 57 | DirectoryEntry *dirChild = nullptr; 58 | FileEntry *fileChild = nullptr; 59 | 60 | bool entry_offset_set = false; 61 | }; 62 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/FileEntry.cpp: -------------------------------------------------------------------------------- 1 | #include "FileEntry.h" 2 | #include "DirectoryEntry.h" 3 | #include "../utils/utils.h" 4 | #include "../services/RomFSService.h" 5 | #include 6 | 7 | void FileEntry::calculateFileOffsets(romfs_ctx_t *romfs_ctx, uint32_t *entry_offset) { 8 | romfs_ctx->file_partition_size = align(romfs_ctx->file_partition_size, 0x10); 9 | this->offset = romfs_ctx->file_partition_size; 10 | romfs_ctx->file_partition_size += this->size; 11 | if (entry_offset) { 12 | this->entry_offset = *entry_offset; 13 | *entry_offset += 0x20 + align(getName().size(), 4); 14 | } 15 | } 16 | 17 | void FileEntry::populate(romfs_infos_t * romfs_infos) { 18 | romfs_fentry_t *cur_entry = romfs::GetFileEntry(romfs_infos->file_table, this->entry_offset); 19 | 20 | cur_entry->parent = be_word(this->getParent()->entry_offset); 21 | cur_entry->sibling = be_word(this->sibling == nullptr ? ROMFS_ENTRY_EMPTY : this->sibling->entry_offset); 22 | cur_entry->offset = be_dword(this->offset); 23 | cur_entry->size = be_dword(this->size); 24 | 25 | uint32_t name_size = getName().length(); 26 | uint32_t hash = romfs::CalcPathHash(this->getParent()->entry_offset, reinterpret_cast(("/" + getName()).c_str()), 1, name_size); 27 | cur_entry->hash = romfs_infos->file_hash_table[hash % romfs_infos->file_hash_table_entry_count]; 28 | romfs_infos->file_hash_table[hash % romfs_infos->file_hash_table_entry_count] = be_word(this->entry_offset); 29 | 30 | cur_entry->name_size = name_size; 31 | memcpy(cur_entry->name, getName().c_str(), name_size); 32 | cur_entry->name_size = be_word(cur_entry->name_size); 33 | } 34 | 35 | void FileEntry::fillRomFSInformation(romfs_ctx_t *romfs_ctx) { 36 | romfs_ctx->num_files++; 37 | romfs_ctx->file_table_size += 0x20 + align(getName().size(), 4); 38 | } 39 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/FileEntry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "NodeEntry.h" 4 | #include "../services/RomFSStructs.h" 5 | 6 | class FileEntry : public NodeEntry { 7 | public: 8 | explicit FileEntry(std::string &&name) : NodeEntry(std::move(name), false) { 9 | } 10 | 11 | void calculateFileOffsets(romfs_ctx_t *ptr, uint32_t *entry_offset) override; 12 | 13 | void populate(romfs_infos_t *romfs_infos) override; 14 | 15 | void fillRomFSInformation(romfs_ctx_t *romfs_ctx) override; 16 | 17 | FileEntry *sibling = nullptr; 18 | uint64_t size = 0; 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/NodeEntry.cpp: -------------------------------------------------------------------------------- 1 | #include "NodeEntry.h" 2 | #include "DirectoryEntry.h" 3 | #include "../utils/utils.h" 4 | 5 | DirectoryEntry * NodeEntry::getParent() { 6 | if (this->parent && this->parent->isDirNode()) { 7 | return static_cast(this->parent); 8 | } 9 | return nullptr; 10 | } 11 | 12 | std::string NodeEntry::getPath() { 13 | if (parent) { 14 | return parent->getPath() + parent->getName() + OS_PATH_SEPARATOR; 15 | } 16 | return OS_PATH_SEPARATOR; 17 | } 18 | 19 | std::string NodeEntry::getFullPath() { 20 | return getPath() + getName(); 21 | } 22 | 23 | void NodeEntry::setParent(DirectoryEntry *_parent) { 24 | if (_parent->isDirNode()) { 25 | this->parent = static_cast(_parent); 26 | } else { 27 | this->parent = nullptr; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/NodeEntry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../services/RomFSStructs.h" 9 | 10 | class DirectoryEntry; 11 | 12 | class NodeEntry { 13 | public: 14 | virtual ~NodeEntry() = default; 15 | 16 | explicit NodeEntry(std::string &&name, bool is_dir_node) { 17 | this->name = std::move(name); 18 | this->is_dir_node = is_dir_node; 19 | } 20 | 21 | virtual const std::string &getName() const { 22 | return name; 23 | } 24 | 25 | bool isDirNode() const { 26 | return is_dir_node; 27 | } 28 | 29 | bool isFileNode() const { 30 | return !is_dir_node; 31 | } 32 | 33 | void setParent(DirectoryEntry *_parent); 34 | 35 | DirectoryEntry * getParent(); 36 | 37 | virtual void printRecursive(int indentation) { 38 | printf("%s%s\n", std::string(indentation, ' ').c_str(), getName().c_str()); 39 | } 40 | 41 | virtual std::string getPath(); 42 | 43 | virtual std::string getFullPath(); 44 | 45 | virtual void populate(romfs_infos_t *romfs_infos) = 0; 46 | 47 | virtual void calculateFileOffsets(romfs_ctx_t *romfs_ctx, uint32_t *entry_offset) = 0; 48 | 49 | virtual void write(FILE *pIobuf, off_t offset) = 0; 50 | 51 | virtual void fillRomFSInformation(romfs_ctx_t *romfs_ctx) = 0; 52 | 53 | 54 | uint64_t offset = 0; 55 | uint64_t entry_offset = 0; 56 | 57 | private: 58 | NodeEntry *parent = nullptr; 59 | std::string name; 60 | bool is_dir_node; 61 | }; 62 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/OSFileEntry.cpp: -------------------------------------------------------------------------------- 1 | #include "../utils/filepath.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include "OSFileEntry.h" 7 | 8 | void OSFileEntry::write(FILE *f_out, off_t base_offset) { 9 | printf("Writing %s...\n", getFullPath().c_str()); 10 | 11 | FILE *f_in = os_fopen(this->osPath.os_path, OS_MODE_READ); 12 | 13 | if (f_in == nullptr) { 14 | fprintf(stderr, "Failed to open %s!\n", getFullPath().c_str()); 15 | exit(EXIT_FAILURE); 16 | } 17 | 18 | /* Write files. */ 19 | auto *buffer = static_cast(malloc(0x400000)); 20 | if (buffer == nullptr) { 21 | fprintf(stderr, "Failed to allocate work buffer!\n"); 22 | exit(EXIT_FAILURE); 23 | } 24 | 25 | if(fseeko64(f_out, base_offset + this->offset + ROMFS_FILEPARTITION_OFS, SEEK_SET) != 0){ 26 | fprintf(stderr, "Failed to seek!\n"); 27 | exit(EXIT_FAILURE); 28 | } 29 | uint64_t offset = 0; 30 | uint64_t read_size = 0x400000; 31 | while (offset < this->size) { 32 | if (this->size - offset < read_size) { 33 | read_size = this->size - offset; 34 | } 35 | 36 | if (fread(buffer, 1, read_size, f_in) != read_size) { 37 | fprintf(stderr, "Failed to read from %s!\n", this->osPath.char_path); 38 | exit(EXIT_FAILURE); 39 | } 40 | 41 | if (fwrite(buffer, 1, read_size, f_out) != read_size) { 42 | fprintf(stderr, "Failed to write to output!\n"); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | offset += read_size; 47 | } 48 | 49 | os_fclose(f_in); 50 | free(buffer); 51 | } 52 | 53 | FileEntry *OSFileEntry::fromPath(const char* inputPath, const char* filename) { 54 | filepath_t cur_path; 55 | filepath_init(&cur_path); 56 | filepath_set(&cur_path, inputPath); 57 | os_stat64_t cur_stats; 58 | 59 | if (os_stat(cur_path.os_path, &cur_stats) == -1) { 60 | fprintf(stderr, "Failed to stat %s\n", cur_path.char_path); 61 | exit(EXIT_FAILURE); 62 | } 63 | 64 | auto res = new OSFileEntry(cur_path, filename); 65 | res->size = cur_stats.st_size; 66 | 67 | return res; 68 | } 69 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/OSFileEntry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FileEntry.h" 4 | 5 | class OSFileEntry final : public FileEntry { 6 | public: 7 | OSFileEntry(filepath_t &path, std::string &&name) : FileEntry(std::move(name)) { 8 | this->osPath = path; 9 | } 10 | 11 | void write(FILE *pIobuf, off_t offset) override; 12 | 13 | static FileEntry* fromPath(const char* inputPath, const char* filename); 14 | 15 | private: 16 | filepath_t osPath; 17 | }; 18 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/RootEntry.cpp: -------------------------------------------------------------------------------- 1 | #include "RootEntry.h" 2 | #include "../services/RomFSService.h" 3 | #include 4 | 5 | void RootEntry::updateEntryOffset(uint32_t *entry_offset) { 6 | if (entry_offset) { 7 | this->entry_offset = *entry_offset; 8 | *entry_offset += 0x18; 9 | } 10 | } 11 | 12 | void RootEntry::populate(romfs_infos_t *romfs_infos) { 13 | romfs_direntry_t *cur_entry = romfs::GetDirEntry(romfs_infos->dir_table, this->entry_offset); 14 | 15 | cur_entry->parent = be_word(this->entry_offset); 16 | cur_entry->sibling = be_word(this->sibling == nullptr ? ROMFS_ENTRY_EMPTY : this->sibling->entry_offset); 17 | cur_entry->child = be_word(this->dirChild == nullptr ? ROMFS_ENTRY_EMPTY : this->dirChild->entry_offset); 18 | cur_entry->file = be_word(this->fileChild == nullptr ? ROMFS_ENTRY_EMPTY : this->fileChild->entry_offset); 19 | 20 | uint32_t name_size = 0; 21 | uint32_t hash = romfs::CalcPathHash(0, (unsigned char *) this->getName().length(), 1, name_size); 22 | cur_entry->hash = romfs_infos->dir_hash_table[hash % romfs_infos->dir_hash_table_entry_count]; 23 | romfs_infos->dir_hash_table[hash % romfs_infos->dir_hash_table_entry_count] = be_word(this->entry_offset); 24 | 25 | cur_entry->name_size = name_size; 26 | memcpy(cur_entry->name, this->getName().c_str(), name_size); 27 | 28 | cur_entry->name_size = be_word(cur_entry->name_size); 29 | 30 | for (auto const &e : children) { 31 | e->populate(romfs_infos); 32 | } 33 | } 34 | 35 | std::string RootEntry::getPath() { 36 | return ""; 37 | } 38 | -------------------------------------------------------------------------------- /src/wuhbtool/entities/RootEntry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DirectoryEntry.h" 4 | #include "../services/RomFSStructs.h" 5 | 6 | class RootEntry final : public DirectoryEntry { 7 | public: 8 | explicit RootEntry() : DirectoryEntry("") { 9 | } 10 | 11 | std::string getPath() override; 12 | 13 | void updateEntryOffset(uint32_t *entry_offset) override; 14 | 15 | void populate(romfs_infos_t *romfs_infos) override; 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /src/wuhbtool/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "entities/RootEntry.h" 6 | #include "entities/OSFileEntry.h" 7 | #include "entities/BufferFileEntry.h" 8 | 9 | #include "services/RomFSService.h" 10 | #include "services/TgaGzService.h" 11 | 12 | static void deinitializeFreeImage() { 13 | FreeImage_DeInitialise(); 14 | } 15 | 16 | static inline void addFolderIfNotEmpty(DirectoryEntry *parent, DirectoryEntry *child) { 17 | if (!child->getChildren().empty()) { 18 | parent->addChild(child); 19 | } else { 20 | delete child; 21 | } 22 | } 23 | 24 | static void addImageResource(DirectoryEntry *parent, const char* name, int width, int height, int bpp, excmd::option_state &options, const char *optName) { 25 | if (!options.has(optName)) 26 | return; 27 | 28 | std::string path = options.get(optName); 29 | FileEntry *file = createTgaGzFileEntry(path.c_str(), width, height, bpp, name); 30 | if (file) { 31 | parent->addChild(file); 32 | } 33 | } 34 | 35 | int main(int argc, char **argv) { 36 | excmd::parser parser; 37 | excmd::option_state options; 38 | using excmd::description; 39 | using excmd::value; 40 | 41 | try { 42 | parser.global_options() 43 | .add_option("H,help", 44 | description{"Show help."}) 45 | .add_option("content", 46 | description{"Path to the /content directory"}, 47 | value{}) 48 | .add_option("name", 49 | description{"Long name of the application"}, 50 | value{}) 51 | .add_option("short-name", 52 | description{"Short name of the application"}, 53 | value{}) 54 | .add_option("author", 55 | description{"Author of the application"}, 56 | value{}) 57 | .add_option("icon", 58 | description{"Application icon (128x128)"}, 59 | value{}) 60 | .add_option("tv-image", 61 | description{"Splash Screen image shown on the TV (1280x720)"}, 62 | value{}) 63 | .add_option("drc-image", 64 | description{"Splash Screen image shown on the DRC (854x480)"}, 65 | value{}); 66 | 67 | parser.default_command() 68 | .add_argument("rpx-file", 69 | description{"Path to RPX file"}, 70 | value{}) 71 | .add_argument("output", 72 | description{"Path to WUHB file"}, 73 | value{}); 74 | 75 | options = parser.parse(argc, argv); 76 | } catch (excmd::exception &ex) { 77 | fprintf(stderr, "Error parsing options: %s\n", ex.what()); 78 | return EXIT_FAILURE; 79 | } 80 | 81 | if (options.empty() || options.has("help")) { 82 | printf("%s [options]\n\n", argv[0]); 83 | printf("%s\n", parser.format_help(argv[0]).c_str()); 84 | return EXIT_SUCCESS; 85 | } 86 | 87 | // Set up FreeImage 88 | FreeImage_Initialise(); 89 | atexit(deinitializeFreeImage); 90 | 91 | auto root = new RootEntry(); 92 | 93 | auto codeFolder = new DirectoryEntry("code"); 94 | auto metaFolder = new DirectoryEntry("meta"); 95 | 96 | std::string rpxFilePath = options.get("rpx-file"); 97 | auto rpxFile = OSFileEntry::fromPath(rpxFilePath.c_str(), "root.rpx"); 98 | codeFolder->addChild(rpxFile); 99 | 100 | { 101 | std::string long_name = options.has("name") ? options.get("name") : ( 102 | options.has("short-name") ? options.get("short-name") : "" 103 | ); 104 | std::string short_name = options.has("short-name") ? options.get("short-name") : ( 105 | options.has("name") ? options.get("name") : "" 106 | ); 107 | std::string author = options.has("author") ? options.get("author") : "Built with devkitPPC & wut"; 108 | 109 | if (long_name.empty() || short_name.empty()) { 110 | size_t startpos = 0, endpos = rpxFilePath.length(); 111 | 112 | #ifndef _WIN32 113 | size_t slash = rpxFilePath.find_last_of('/'); 114 | #else 115 | size_t slash = rpxFilePath.find_last_of("/\\"); 116 | #endif 117 | 118 | if (slash != std::string::npos) { 119 | startpos = slash+1; 120 | } 121 | 122 | size_t dot = rpxFilePath.find_last_of('.'); 123 | if (dot != std::string::npos) { 124 | endpos = dot; 125 | } 126 | 127 | long_name = short_name = rpxFilePath.substr(startpos, endpos-startpos); 128 | } 129 | 130 | #define MAKE_META_INI(_buf,_size) snprintf((_buf),(_size), \ 131 | "[menu]\n" \ 132 | "longname=%s\n" \ 133 | "shortname=%s\n" \ 134 | "author=%s\n", \ 135 | long_name.c_str(), \ 136 | short_name.c_str(), \ 137 | author.c_str()) 138 | 139 | std::vector metaIniData; 140 | metaIniData.reserve(MAKE_META_INI(NULL, 0)+1); 141 | metaIniData.resize(metaIniData.capacity()-1); 142 | MAKE_META_INI(reinterpret_cast(metaIniData.data()), metaIniData.capacity()); 143 | 144 | #undef MAKE_META_INI 145 | 146 | FileEntry * metaIni = new BufferFileEntry("meta.ini", std::move(metaIniData)); 147 | if(!metaIni){ 148 | return EXIT_FAILURE; 149 | } 150 | 151 | metaFolder->addChild(metaIni); 152 | } 153 | 154 | addImageResource(metaFolder, "iconTex.tga.gz", 128, 128, 32, options, "icon"); 155 | addImageResource(metaFolder, "bootTvTex.tga.gz", 1280, 720, 24, options, "tv-image"); 156 | addImageResource(metaFolder, "bootDrcTex.tga.gz", 854, 480, 24, options, "drc-image"); 157 | 158 | addFolderIfNotEmpty(root, codeFolder); 159 | addFolderIfNotEmpty(root, metaFolder); 160 | 161 | if (options.has("content")) { 162 | std::string contentPath = options.get("content"); 163 | 164 | filepath_t dirpath; 165 | filepath_init(&dirpath); 166 | filepath_set(&dirpath, contentPath.c_str()); 167 | 168 | auto contentFolder = romfs::CreateFolderFromPath(dirpath, "content"); 169 | addFolderIfNotEmpty(root, contentFolder); 170 | } 171 | 172 | std::string outputPath = options.get("output"); 173 | romfs::CreateArchive(root, outputPath.c_str()); 174 | 175 | delete root; 176 | 177 | return EXIT_SUCCESS; 178 | } 179 | -------------------------------------------------------------------------------- /src/wuhbtool/services/RomFSService.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "RomFSService.h" 5 | #include "../utils/utils.h" 6 | #include "../entities/OSFileEntry.h" 7 | 8 | namespace romfs { 9 | 10 | namespace { 11 | 12 | inline unsigned char NormalizeChar(unsigned char c) { 13 | if (c >= 'a' && c <= 'z') { 14 | return c + 'A' - 'a'; 15 | } else { 16 | return c; 17 | } 18 | } 19 | 20 | uint32_t GetHashTableCount(uint32_t num_entries) { 21 | if (num_entries < 3) { 22 | return 3; 23 | } else if (num_entries < 19) { 24 | return num_entries | 1; 25 | } 26 | uint32_t count = num_entries; 27 | while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0) { 28 | count++; 29 | } 30 | return count; 31 | } 32 | 33 | } 34 | 35 | uint32_t CalcPathHash(uint32_t parent, const unsigned char *path, uint32_t start, size_t path_len) { 36 | uint32_t hash = parent ^ 123456789; 37 | for (uint32_t i = 0; i < path_len; i++) { 38 | hash = (hash >> 5) | (hash << 27); 39 | hash ^= NormalizeChar(path[start + i]); 40 | } 41 | 42 | return hash; 43 | } 44 | 45 | DirectoryEntry *CreateFolderFromPath(filepath_t &dirpath, const char *name) { 46 | osdirent_t *cur_dirent = nullptr; 47 | filepath_t cur_path; 48 | filepath_t cur_sum_path; 49 | os_stat64_t cur_stats; 50 | 51 | auto *curDir = new DirectoryEntry(name); 52 | 53 | osdir_t *dir = nullptr; 54 | if ((dir = os_opendir(dirpath.os_path)) == nullptr) { 55 | fprintf(stderr, "Failed to open directory %s!\n", dirpath.char_path); 56 | exit(EXIT_FAILURE); 57 | } 58 | 59 | while ((cur_dirent = os_readdir(dir))) { 60 | filepath_init(&cur_path); 61 | filepath_set(&cur_path, ""); 62 | filepath_os_set(&cur_path, cur_dirent->d_name); 63 | 64 | if (strcmp(cur_path.char_path, ".") == 0 || strcmp(cur_path.char_path, "..") == 0) { 65 | /* Special case . and .. */ 66 | continue; 67 | } 68 | 69 | filepath_copy(&cur_sum_path, &dirpath); 70 | filepath_os_append(&cur_sum_path, cur_dirent->d_name); 71 | 72 | if (os_stat(cur_sum_path.os_path, &cur_stats) == -1) { 73 | fprintf(stderr, "Failed to stat %s\n", cur_sum_path.char_path); 74 | exit(EXIT_FAILURE); 75 | } 76 | 77 | if ((cur_stats.st_mode & S_IFMT) == S_IFDIR) { 78 | auto directoryEntry = CreateFolderFromPath(cur_sum_path, cur_path.char_path); 79 | curDir->addChild(directoryEntry); 80 | } else if ((cur_stats.st_mode & S_IFMT) == S_IFREG) { 81 | auto fileEntry = new OSFileEntry(cur_sum_path, cur_path.char_path); 82 | fileEntry->size = cur_stats.st_size; 83 | curDir->addChild(fileEntry); 84 | } else { 85 | fprintf(stderr, "Invalid FS object type for %s!\n", cur_path.char_path); 86 | exit(EXIT_FAILURE); 87 | } 88 | } 89 | 90 | os_closedir(dir); 91 | return curDir; 92 | } 93 | 94 | void CreateArchive(DirectoryEntry *root, const char *outputFilePath) { 95 | romfs_ctx_t romfs_ctx; 96 | memset(&romfs_ctx, 0, sizeof(romfs_ctx)); 97 | 98 | romfs_ctx.dir_table_size = 0x18; /* Root directory. */ 99 | romfs_ctx.num_dirs = 1; 100 | 101 | root->fillRomFSInformation(&romfs_ctx); 102 | 103 | uint32_t dir_hash_table_entry_count = GetHashTableCount(romfs_ctx.num_dirs); 104 | uint32_t file_hash_table_entry_count = GetHashTableCount(romfs_ctx.num_files); 105 | romfs_ctx.dir_hash_table_size = 4 * dir_hash_table_entry_count; 106 | romfs_ctx.file_hash_table_size = 4 * file_hash_table_entry_count; 107 | 108 | auto dir_hash_table = static_cast(malloc(romfs_ctx.dir_hash_table_size)); 109 | if (dir_hash_table == nullptr) { 110 | fprintf(stderr, "Failed to allocate directory hash table!\n"); 111 | exit(EXIT_FAILURE); 112 | } 113 | 114 | for (uint32_t i = 0; i < dir_hash_table_entry_count; i++) { 115 | dir_hash_table[i] = be_word(ROMFS_ENTRY_EMPTY); 116 | } 117 | 118 | auto file_hash_table = static_cast(malloc(romfs_ctx.file_hash_table_size)); 119 | if (file_hash_table == nullptr) { 120 | fprintf(stderr, "Failed to allocate file hash table!\n"); 121 | exit(EXIT_FAILURE); 122 | } 123 | 124 | for (uint32_t i = 0; i < file_hash_table_entry_count; i++) { 125 | file_hash_table[i] = be_word(ROMFS_ENTRY_EMPTY); 126 | } 127 | 128 | auto dir_table = static_cast(calloc(1, romfs_ctx.dir_table_size)); 129 | if (dir_table == nullptr) { 130 | fprintf(stderr, "Failed to allocate directory table!\n"); 131 | exit(EXIT_FAILURE); 132 | } 133 | 134 | auto file_table = static_cast(calloc(1, romfs_ctx.file_table_size)); 135 | if (file_table == nullptr) { 136 | fprintf(stderr, "Failed to allocate file table!\n"); 137 | exit(EXIT_FAILURE); 138 | } 139 | 140 | romfs_infos_t infos; 141 | infos.file_table = file_table; 142 | infos.dir_table = dir_table; 143 | infos.dir_hash_table = dir_hash_table; 144 | infos.file_hash_table = file_hash_table; 145 | infos.dir_hash_table_entry_count = dir_hash_table_entry_count; 146 | infos.file_hash_table_entry_count = file_hash_table_entry_count; 147 | 148 | printf("Calculating metadata...\n"); 149 | uint32_t entry_offset = 0; 150 | root->calculateDirOffsets(&romfs_ctx, &entry_offset); 151 | entry_offset = 0; 152 | root->calculateFileOffsets(&romfs_ctx, &entry_offset); 153 | printf("Updating sibling and child entries...\n"); 154 | root->updateSiblingAndChildEntries(); 155 | printf("Populating data...\n"); 156 | root->populate(&infos); 157 | 158 | romfs_header_t header; 159 | memset(&header, 0, sizeof(header)); 160 | 161 | memcpy(&header.header_magic, "WUHB", sizeof(header.header_magic)); 162 | header.header_size = be_word(sizeof(header)); 163 | header.file_hash_table_size = be_dword(romfs_ctx.file_hash_table_size); 164 | header.file_table_size = be_dword(romfs_ctx.file_table_size); 165 | header.dir_hash_table_size = be_dword(romfs_ctx.dir_hash_table_size); 166 | header.dir_table_size = be_dword(romfs_ctx.dir_table_size); 167 | header.file_partition_ofs = be_dword(ROMFS_FILEPARTITION_OFS); 168 | 169 | /* Abuse of endianness follows. */ 170 | uint64_t dir_hash_table_ofs = align(romfs_ctx.file_partition_size + ROMFS_FILEPARTITION_OFS, 4); 171 | header.dir_hash_table_ofs = dir_hash_table_ofs; 172 | header.dir_table_ofs = header.dir_hash_table_ofs + romfs_ctx.dir_hash_table_size; 173 | header.file_hash_table_ofs = header.dir_table_ofs + romfs_ctx.dir_table_size; 174 | header.file_table_ofs = header.file_hash_table_ofs + romfs_ctx.file_hash_table_size; 175 | header.dir_hash_table_ofs = be_dword(header.dir_hash_table_ofs); 176 | header.dir_table_ofs = be_dword(header.dir_table_ofs); 177 | header.file_hash_table_ofs = be_dword(header.file_hash_table_ofs); 178 | header.file_table_ofs = be_dword(header.file_table_ofs); 179 | 180 | filepath_t outpath; 181 | filepath_init(&outpath); 182 | filepath_set(&outpath, outputFilePath); 183 | 184 | off_t base_offset = 0; 185 | FILE *f_out = nullptr; 186 | 187 | if ((f_out = os_fopen(outpath.os_path, OS_MODE_WRITE)) == NULL) { 188 | fprintf(stderr, "Failed to open %s!\n", outpath.char_path); 189 | exit(EXIT_FAILURE); 190 | } 191 | 192 | printf("Writing header...\n"); 193 | if(fseeko64(f_out, base_offset, SEEK_SET) != 0){ 194 | fprintf(stderr, "Failed to seek!\n"); 195 | exit(EXIT_FAILURE); 196 | } 197 | fwrite(&header, 1, sizeof(header), f_out); 198 | 199 | root->write(f_out, base_offset); 200 | 201 | printf("Writing dir_hash_table...\n"); 202 | if(fseeko64(f_out, base_offset + dir_hash_table_ofs, SEEK_SET) != 0){ 203 | fprintf(stderr, "Failed to seek!\n"); 204 | exit(EXIT_FAILURE); 205 | } 206 | if (fwrite(dir_hash_table, 1, romfs_ctx.dir_hash_table_size, f_out) != romfs_ctx.dir_hash_table_size) { 207 | fprintf(stderr, "Failed to write dir hash table!\n"); 208 | exit(EXIT_FAILURE); 209 | } 210 | free(dir_hash_table); 211 | 212 | printf("Writing dir_table...\n"); 213 | if (fwrite(dir_table, 1, romfs_ctx.dir_table_size, f_out) != romfs_ctx.dir_table_size) { 214 | fprintf(stderr, "Failed to write dir table!\n"); 215 | exit(EXIT_FAILURE); 216 | } 217 | free(dir_table); 218 | 219 | printf("Writing file_hash_table...\n"); 220 | if (fwrite(file_hash_table, 1, romfs_ctx.file_hash_table_size, f_out) != romfs_ctx.file_hash_table_size) { 221 | fprintf(stderr, "Failed to write file hash table!\n"); 222 | exit(EXIT_FAILURE); 223 | } 224 | free(file_hash_table); 225 | 226 | printf("Writing file_table...\n"); 227 | if (fwrite(file_table, 1, romfs_ctx.file_table_size, f_out) != romfs_ctx.file_table_size) { 228 | fprintf(stderr, "Failed to write file table!\n"); 229 | exit(EXIT_FAILURE); 230 | } 231 | free(file_table); 232 | fclose(f_out); 233 | } 234 | 235 | } 236 | -------------------------------------------------------------------------------- /src/wuhbtool/services/RomFSService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "RomFSStructs.h" 5 | #include "../entities/DirectoryEntry.h" 6 | 7 | namespace romfs { 8 | 9 | inline romfs_direntry_t *GetDirEntry(romfs_direntry_t *directories, uint32_t offset) { 10 | return (romfs_direntry_t *) ((char *) directories + offset); 11 | } 12 | 13 | inline romfs_fentry_t *GetFileEntry(romfs_fentry_t *files, uint32_t offset) { 14 | return (romfs_fentry_t *) ((char *) files + offset); 15 | } 16 | 17 | uint32_t CalcPathHash(uint32_t parent, const unsigned char *path, uint32_t start, size_t path_len); 18 | DirectoryEntry *CreateFolderFromPath(filepath_t &dirpath, const char *name); 19 | void CreateArchive(DirectoryEntry *root, const char *outputFilePath); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/wuhbtool/services/RomFSStructs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../utils/filepath.h" 4 | 5 | struct romfs_fent_ctx; 6 | 7 | typedef struct romfs_dirent_ctx { 8 | filepath_t sum_path; 9 | filepath_t cur_path; 10 | uint32_t entry_offset; 11 | struct romfs_dirent_ctx *parent; /* Parent node */ 12 | struct romfs_dirent_ctx *child; /* Child node */ 13 | struct romfs_dirent_ctx *sibling; /* Sibling node */ 14 | struct romfs_fent_ctx *file; /* File node */ 15 | struct romfs_dirent_ctx *next; /* Next node */ 16 | } romfs_dirent_ctx_t; 17 | 18 | typedef struct romfs_fent_ctx { 19 | filepath_t sum_path; 20 | filepath_t cur_path; 21 | uint32_t entry_offset; 22 | uint64_t offset; 23 | uint64_t size; 24 | romfs_dirent_ctx_t *parent; /* Parent dir */ 25 | struct romfs_fent_ctx *sibling; /* Sibling file */ 26 | struct romfs_fent_ctx *next; /* Logical next file */ 27 | } romfs_fent_ctx_t; 28 | 29 | typedef struct { 30 | romfs_fent_ctx_t *files; 31 | uint64_t num_dirs; 32 | uint64_t num_files; 33 | uint64_t dir_table_size; 34 | uint64_t file_table_size; 35 | uint64_t dir_hash_table_size; 36 | uint64_t file_hash_table_size; 37 | uint64_t file_partition_size; 38 | } romfs_ctx_t; 39 | 40 | typedef struct { 41 | uint32_t header_magic; 42 | uint32_t header_size; 43 | uint64_t dir_hash_table_ofs; 44 | uint64_t dir_hash_table_size; 45 | uint64_t dir_table_ofs; 46 | uint64_t dir_table_size; 47 | uint64_t file_hash_table_ofs; 48 | uint64_t file_hash_table_size; 49 | uint64_t file_table_ofs; 50 | uint64_t file_table_size; 51 | uint64_t file_partition_ofs; 52 | } romfs_header_t; 53 | 54 | typedef struct { 55 | uint32_t parent; 56 | uint32_t sibling; 57 | uint32_t child; 58 | uint32_t file; 59 | uint32_t hash; 60 | uint32_t name_size; 61 | char name[]; 62 | } romfs_direntry_t; 63 | 64 | typedef struct { 65 | uint32_t parent; 66 | uint32_t sibling; 67 | uint64_t offset; 68 | uint64_t size; 69 | uint32_t hash; 70 | uint32_t name_size; 71 | char name[]; 72 | } romfs_fentry_t; 73 | 74 | typedef struct romfs_infos { 75 | uint32_t * dir_hash_table; 76 | uint32_t * file_hash_table; 77 | 78 | romfs_direntry_t * dir_table; 79 | romfs_fentry_t * file_table; 80 | 81 | uint32_t file_hash_table_entry_count; 82 | uint32_t dir_hash_table_entry_count; 83 | } romfs_infos_t; 84 | 85 | #define ROMFS_ENTRY_EMPTY 0xFFFFFFFF 86 | #define ROMFS_FILEPARTITION_OFS 0x200 87 | -------------------------------------------------------------------------------- /src/wuhbtool/services/TgaGzService.cpp: -------------------------------------------------------------------------------- 1 | #include "TgaGzService.h" 2 | #include "../entities/BufferFileEntry.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace { 9 | 10 | struct FiMemoryFile { 11 | std::vector data; 12 | long pos; 13 | 14 | FiMemoryFile() : data{}, pos{} { } 15 | 16 | long getSize() { 17 | return pos > data.size() ? pos : data.size(); 18 | } 19 | 20 | void ensureSize() { 21 | if (pos > data.size()) { 22 | data.resize(pos); 23 | } 24 | } 25 | 26 | /* 27 | unsigned read(void *buffer, unsigned size, unsigned count) { 28 | // etc 29 | } 30 | */ 31 | 32 | unsigned write(const void *buffer, unsigned size, unsigned count) { 33 | long writepos = pos; 34 | long writesize = size*count; 35 | 36 | pos += writesize; 37 | ensureSize(); 38 | 39 | memcpy(&data[writepos], buffer, writesize); 40 | return count; 41 | } 42 | 43 | int seek(long offset, int origin) { 44 | if (origin == SEEK_SET) { 45 | pos = offset; 46 | } else if (origin == SEEK_CUR) { 47 | pos += offset; 48 | } else if (origin == SEEK_END) { 49 | pos = getSize() + offset; 50 | } 51 | 52 | if (pos < 0) { 53 | pos = 0; 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | long tell() { 60 | return pos; 61 | } 62 | }; 63 | 64 | /* 65 | unsigned DLL_CALLCONV FiMemoryFileReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { 66 | FiMemoryFile *f = static_cast(handle); 67 | return f->read(buffer, size, count); 68 | } 69 | */ 70 | 71 | unsigned DLL_CALLCONV FiMemoryFileWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { 72 | FiMemoryFile *f = static_cast(handle); 73 | return f->write(buffer, size, count); 74 | } 75 | 76 | int DLL_CALLCONV FiMemoryFileSeekProc(fi_handle handle, long offset, int origin) { 77 | FiMemoryFile *f = static_cast(handle); 78 | return f->seek(offset, origin); 79 | } 80 | 81 | long DLL_CALLCONV FiMemoryFileTellProc(fi_handle handle) { 82 | FiMemoryFile *f = static_cast(handle); 83 | return f->tell(); 84 | } 85 | 86 | constexpr FreeImageIO FiMemoryFileIO = { 87 | NULL, //FiMemoryFileReadProc, 88 | FiMemoryFileWriteProc, 89 | FiMemoryFileSeekProc, 90 | FiMemoryFileTellProc, 91 | }; 92 | 93 | std::vector gzCompress(const void* data, size_t size) { 94 | std::vector buffer; 95 | uint8_t chunk[16*1024]; 96 | 97 | z_stream z = {}; 98 | deflateInit2(&z, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS | 16, 8, Z_DEFAULT_STRATEGY); 99 | 100 | z.avail_in = size; 101 | z.next_in = static_cast(const_cast(data)); 102 | 103 | do { 104 | z.avail_out = sizeof(chunk); 105 | z.next_out = static_cast(chunk); 106 | 107 | int ret = deflate(&z, Z_FINISH); 108 | if (ret == Z_STREAM_ERROR) { 109 | deflateEnd(&z); 110 | fprintf(stderr, "Zlib compression error\n"); 111 | exit(EXIT_FAILURE); 112 | } 113 | 114 | buffer.insert(buffer.end(), chunk, static_cast(z.next_out)); 115 | } while (z.avail_out == 0); 116 | 117 | deflateEnd(&z); 118 | return buffer; 119 | } 120 | 121 | } 122 | 123 | FileEntry* createTgaGzFileEntry(const char* inputFile, int width, int height, int bpp, const char* filename) { 124 | FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(inputFile); 125 | if (fif == FIF_UNKNOWN || !FreeImage_FIFSupportsReading(fif)) { 126 | fprintf(stderr, "Unknown or unsupported image format: %s\n", inputFile); 127 | return nullptr; 128 | } 129 | 130 | FIBITMAP* bmp = FreeImage_Load(fif, inputFile, 0); 131 | 132 | if (bmp && (FreeImage_GetImageType(bmp) != FIT_BITMAP || FreeImage_GetBPP(bmp) != bpp)) { 133 | FIBITMAP* newbmp = bpp == 24 ? FreeImage_ConvertTo24Bits(bmp) : FreeImage_ConvertTo32Bits(bmp); 134 | FreeImage_Unload(bmp); 135 | bmp = newbmp; 136 | } 137 | 138 | if (bmp && (FreeImage_GetWidth(bmp) != width || FreeImage_GetHeight(bmp) != height)) { 139 | fprintf(stderr, "Warning: Image %s has incorrect size (expected %dx%d), resizing...\n", inputFile, width, height); 140 | FIBITMAP* newbmp = FreeImage_Rescale(bmp, width, height, FILTER_BILINEAR); 141 | FreeImage_Unload(bmp); 142 | bmp = newbmp; 143 | } 144 | 145 | if (!bmp) { 146 | fprintf(stderr, "Failed to load image: %s\n", inputFile); 147 | return nullptr; 148 | } 149 | 150 | FiMemoryFile tga; 151 | FreeImage_SaveToHandle(FIF_TARGA, bmp, const_cast(&FiMemoryFileIO), &tga, TARGA_DEFAULT); 152 | FreeImage_Unload(bmp); 153 | tga.ensureSize(); 154 | 155 | std::vector data = gzCompress(tga.data.data(), tga.data.size()); 156 | 157 | return new BufferFileEntry(filename, std::move(data)); 158 | } 159 | -------------------------------------------------------------------------------- /src/wuhbtool/services/TgaGzService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "../entities/FileEntry.h" 6 | 7 | FileEntry* createTgaGzFileEntry(const char* inputFile, int width, int height, int bpp, const char* filename); 8 | -------------------------------------------------------------------------------- /src/wuhbtool/utils/filepath.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef _WIN32 8 | #define WIN32_LEAN_AND_MEAN 9 | #include 10 | #endif 11 | 12 | #include "types.h" 13 | #include "filepath.h" 14 | 15 | void os_strncpy(oschar_t *dst, const char *src, size_t size) { 16 | #ifdef _WIN32 17 | MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size); 18 | #else 19 | strncpy(dst, src, size); 20 | #endif 21 | } 22 | 23 | void os_strncpy_to_char(char *dst, const oschar_t *src, size_t size) { 24 | #ifdef _WIN32 25 | WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, size, NULL, NULL); 26 | #else 27 | strncpy(dst, src, size); 28 | #endif 29 | } 30 | 31 | int os_makedir(const oschar_t *dir) { 32 | #ifdef _WIN32 33 | return _wmkdir(dir); 34 | #else 35 | return mkdir(dir, 0777); 36 | #endif 37 | } 38 | 39 | int os_rmdir(const oschar_t *dir) { 40 | #ifdef _WIN32 41 | return _wrmdir(dir); 42 | #else 43 | return remove(dir); 44 | #endif 45 | } 46 | 47 | void filepath_update(filepath_t *fpath) { 48 | memset(fpath->os_path, 0, MAX_OSPATH * sizeof(oschar_t)); 49 | os_strncpy(fpath->os_path, fpath->char_path, MAX_OSPATH); 50 | } 51 | 52 | void filepath_init(filepath_t *fpath) { 53 | fpath->valid = VALIDITY_INVALID; 54 | } 55 | 56 | void filepath_copy(filepath_t *fpath, filepath_t *copy) { 57 | if (copy != NULL && copy->valid == VALIDITY_VALID) 58 | memcpy(fpath, copy, sizeof(filepath_t)); 59 | else 60 | memset(fpath, 0, sizeof(filepath_t)); 61 | } 62 | 63 | void filepath_os_append(filepath_t *fpath, oschar_t *path) { 64 | char tmppath[MAX_OSPATH]; 65 | if (fpath->valid == VALIDITY_INVALID) 66 | return; 67 | 68 | memset(tmppath, 0, MAX_OSPATH); 69 | 70 | os_strncpy_to_char(tmppath, path, MAX_OSPATH); 71 | strcat(fpath->char_path, OS_PATH_SEPARATOR); 72 | strcat(fpath->char_path, tmppath); 73 | filepath_update(fpath); 74 | } 75 | 76 | void filepath_append(filepath_t *fpath, const char *format, ...) { 77 | char tmppath[MAX_OSPATH]; 78 | va_list args; 79 | 80 | if (fpath->valid == VALIDITY_INVALID) 81 | return; 82 | 83 | memset(tmppath, 0, MAX_OSPATH); 84 | 85 | va_start(args, format); 86 | vsnprintf(tmppath, sizeof(tmppath), format, args); 87 | va_end(args); 88 | 89 | strcat(fpath->char_path, OS_PATH_SEPARATOR); 90 | strcat(fpath->char_path, tmppath); 91 | filepath_update(fpath); 92 | } 93 | 94 | void filepath_append_n(filepath_t *fpath, uint32_t n, const char *format, ...) { 95 | char tmppath[MAX_OSPATH]; 96 | va_list args; 97 | 98 | if (fpath->valid == VALIDITY_INVALID || n > MAX_OSPATH) 99 | return; 100 | 101 | memset(tmppath, 0, MAX_OSPATH); 102 | 103 | va_start(args, format); 104 | vsnprintf(tmppath, sizeof(tmppath), format, args); 105 | va_end(args); 106 | 107 | strcat(fpath->char_path, OS_PATH_SEPARATOR); 108 | strncat(fpath->char_path, tmppath, n); 109 | filepath_update(fpath); 110 | } 111 | 112 | void filepath_set(filepath_t *fpath, const char *path) { 113 | if (strlen(path) < MAX_OSPATH) { 114 | fpath->valid = VALIDITY_VALID; 115 | memset(fpath->char_path, 0, MAX_OSPATH); 116 | strncpy(fpath->char_path, path, MAX_OSPATH); 117 | filepath_update(fpath); 118 | } else { 119 | fpath->valid = VALIDITY_INVALID; 120 | } 121 | } 122 | 123 | void filepath_os_set(filepath_t *fpath, oschar_t *path) { 124 | char tmppath[MAX_OSPATH]; 125 | memset(tmppath, 0, MAX_OSPATH); 126 | os_strncpy_to_char(tmppath, path, MAX_OSPATH); 127 | 128 | filepath_set(fpath, tmppath); 129 | } 130 | 131 | oschar_t *filepath_get(filepath_t *fpath) { 132 | if (fpath->valid == VALIDITY_INVALID) 133 | return NULL; 134 | else 135 | return fpath->os_path; 136 | } 137 | -------------------------------------------------------------------------------- /src/wuhbtool/utils/filepath.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "types.h" 4 | #include 5 | #ifdef _WIN32 6 | #include 7 | #include 8 | #endif 9 | 10 | #define MAX_OSPATH 0x300 /* 0x100 BMP Unicode codepoints encoded as UTF-8 codeunits (max. 3 codeunits per codepoint) */ 11 | 12 | typedef enum { 13 | VALIDITY_UNCHECKED = 0, 14 | VALIDITY_INVALID, 15 | VALIDITY_VALID 16 | } validity_t; 17 | 18 | #ifndef _WIN32 19 | /* off_t is 64-bit with large file support */ 20 | #define fseeko64 fseek 21 | #endif 22 | 23 | #ifdef _WIN32 24 | typedef wchar_t oschar_t; /* utf-16 */ 25 | typedef _WDIR osdir_t; 26 | typedef struct _wdirent osdirent_t; 27 | typedef struct _stati64 os_stat64_t; 28 | 29 | #define os_fopen _wfopen 30 | #define os_opendir _wopendir 31 | #define os_closedir _wclosedir 32 | #define os_readdir _wreaddir 33 | #define os_stat _wstati64 34 | #define os_fclose fclose 35 | 36 | #define OS_MODE_READ L"rb" 37 | #define OS_MODE_WRITE L"wb" 38 | #define OS_MODE_EDIT L"rb+" 39 | #else 40 | typedef char oschar_t; /* utf-8 */ 41 | typedef DIR osdir_t; 42 | typedef struct dirent osdirent_t; 43 | typedef struct stat os_stat64_t; 44 | 45 | #define os_fopen fopen 46 | #define os_opendir opendir 47 | #define os_closedir closedir 48 | #define os_readdir readdir 49 | #define os_stat stat 50 | #define os_fclose fclose 51 | 52 | #define OS_MODE_READ "rb" 53 | #define OS_MODE_WRITE "wb" 54 | #define OS_MODE_EDIT "rb+" 55 | #endif 56 | 57 | #define OS_PATH_SEPARATOR "/" 58 | 59 | typedef struct filepath { 60 | char char_path[MAX_OSPATH]; 61 | oschar_t os_path[MAX_OSPATH]; 62 | validity_t valid; 63 | } filepath_t; 64 | 65 | void os_strncpy(oschar_t *dst, const char *src, size_t size); 66 | void os_strncpy_to_char(char *dst, const oschar_t *src, size_t size); 67 | int os_makedir(const oschar_t *dir); 68 | int os_rmdir(const oschar_t *dir); 69 | 70 | void filepath_init(filepath_t *fpath); 71 | void filepath_copy(filepath_t *fpath, filepath_t *copy); 72 | void filepath_os_append(filepath_t *fpath, oschar_t *path); 73 | void filepath_append(filepath_t *fpath, const char *format, ...); 74 | void filepath_append_n(filepath_t *fpath, uint32_t n, const char *format, ...); 75 | void filepath_set(filepath_t *fpath, const char *path); 76 | void filepath_os_set(filepath_t *fpath, oschar_t *path); 77 | oschar_t *filepath_get(filepath_t *fpath); 78 | -------------------------------------------------------------------------------- /src/wuhbtool/utils/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | typedef uint64_t dword_t; 5 | typedef uint32_t word_t; 6 | typedef uint16_t hword_t; 7 | typedef uint8_t byte_t; 8 | typedef int64_t dlong_t; 9 | typedef int32_t long_t; 10 | typedef int16_t short_t; 11 | typedef int8_t char_t; 12 | typedef uint64_t u64; 13 | typedef uint32_t u32; 14 | typedef uint16_t u16; 15 | typedef uint8_t u8; 16 | 17 | #define BIT(n) (1U << (n)) 18 | 19 | static inline uint16_t __local_bswap16(uint16_t x) { 20 | return ((x << 8) & 0xff00) | ((x >> 8) & 0x00ff); 21 | } 22 | 23 | 24 | static inline uint32_t __local_bswap32(uint32_t x) { 25 | return ((x << 24) & 0xff000000 ) | 26 | ((x << 8) & 0x00ff0000 ) | 27 | ((x >> 8) & 0x0000ff00 ) | 28 | ((x >> 24) & 0x000000ff ); 29 | } 30 | 31 | static inline uint64_t __local_bswap64(uint64_t x) 32 | { 33 | return (uint64_t)__local_bswap32(x>>32) | 34 | ((uint64_t)__local_bswap32(x&0xFFFFFFFF) << 32); 35 | } 36 | 37 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 38 | #define be_dword(a) __local_bswap64(a) 39 | #define be_word(a) __local_bswap32(a) 40 | #define be_hword(a) __local_bswap16(a) 41 | #define le_dword(a) (a) 42 | #define le_word(a) (a) 43 | #define le_hword(a) (a) 44 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 45 | #define be_dword(a) (a) 46 | #define be_word(a) (a) 47 | #define be_hword(a) (a) 48 | #define le_dword(a) __local_bswap64(a) 49 | #define le_word(a) __local_bswap32(a) 50 | #define le_hword(a) __local_bswap16(a) 51 | #else 52 | #error "What's the endianness of the platform you're targeting?" 53 | #endif 54 | 55 | -------------------------------------------------------------------------------- /src/wuhbtool/utils/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | template 5 | static inline constexpr T align(T offset, T alignment) { 6 | T mask = ~static_cast(alignment-1); 7 | 8 | return (offset + (alignment-1)) & mask; 9 | } 10 | --------------------------------------------------------------------------------