├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── assets ├── config.toml ├── declareMap.json └── predefine.h ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src ├── main ├── kotlin │ └── com │ │ └── liteldev │ │ └── headeroutput │ │ ├── HeaderGenerator.kt │ │ ├── HeaderOutput.kt │ │ ├── TypeManager.kt │ │ ├── ast │ │ └── template │ │ │ ├── Nodes.kt │ │ │ ├── ParseException.kt │ │ │ └── Parser.kt │ │ ├── config │ │ ├── GeneratorConfig.kt │ │ └── OutputConfig.kt │ │ ├── data │ │ ├── EnumType.kt │ │ ├── MemberTypeData.kt │ │ ├── TypeData.kt │ │ └── VariableTypeData.kt │ │ ├── entity │ │ ├── BaseType.kt │ │ ├── ClassType.kt │ │ ├── EnumType.kt │ │ ├── NamespaceType.kt │ │ └── UnionType.kt │ │ └── utils.kt └── resources │ └── log4j2.xml └── test └── kotlin ├── TestEnum.kt ├── TestTemplateAst.kt └── main.kt /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Gradle 2 | env: 3 | JAR_SHA256: '' 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/*.yml" 8 | - "**.kts" 9 | - "**.kt" 10 | - "**.java" 11 | pull_request: 12 | release: 13 | types: 14 | - created 15 | workflow_dispatch: 16 | 17 | jobs: 18 | build: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Set up JDK 17 23 | uses: actions/setup-java@v3 24 | with: 25 | java-version: '17' 26 | distribution: 'temurin' 27 | 28 | - name: Build with Gradle 29 | uses: gradle/gradle-build-action@v2.4.2 30 | with: 31 | arguments: build 32 | - name: Upload a Build Artifact 33 | uses: actions/upload-artifact@v3.1.0 34 | with: 35 | path: build/libs 36 | 37 | upload-to-release: 38 | name: Upload to Release 39 | needs: 40 | - build 41 | runs-on: ubuntu-latest 42 | if: github.event_name == 'release' 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v3 46 | with: 47 | fetch-depth: 0 48 | 49 | - name: Get Version 50 | run: echo "VERSION=${{ github.ref_name }}" >> $GITHUB_ENV 51 | 52 | - name: Download Artifacts 53 | uses: actions/download-artifact@v3 54 | 55 | - name: Compute Artifacts SHA256 56 | run: | 57 | echo "JAR_SHA256=$(sha256sum artifact/HeaderOutput-${{ env.VERSION }}-all.jar | cut -d ' ' -f 1)" >> $GITHUB_ENV 58 | shell: bash 59 | 60 | - name: Upload Release Assets 61 | uses: softprops/action-gh-release@v1 62 | with: 63 | append_body: true 64 | files: | 65 | artifact/HeaderOutput-${{ env.VERSION }}-all.jar 66 | body: | 67 | --- 68 | | File | SHA256 | 69 | | :--------------------------------------------- | :-------------------- | 70 | | HeaderOutput-${{ env.VERSION }}-all.jar | ${{ env.JAR_SHA256 }} | 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | **/build/ 3 | !src/**/build/ 4 | 5 | # Ignore Gradle GUI config 6 | gradle-app.setting 7 | 8 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 9 | !gradle-wrapper.jar 10 | 11 | # Avoid ignore Gradle wrappper properties 12 | !gradle-wrapper.properties 13 | 14 | # Cache of project 15 | .gradletasknamecache 16 | 17 | # Eclipse Gradle plugin generated files 18 | # Eclipse Core 19 | .project 20 | # JDT-specific (Eclipse Java Development Tools) 21 | .classpath 22 | 23 | # IDEA 24 | .idea 25 | 26 | # vscode 27 | .vscode 28 | 29 | bin 30 | 31 | # HeaderOutput files 32 | /header/ 33 | /originalData*.json 34 | /config.toml 35 | /config.json 36 | /declareMap.json 37 | /.vs 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU Affero General Public License 2 | ================================= 3 | 4 | _Version 3, 19 November 2007_ 5 | _Copyright © 2007 Free Software Foundation, Inc. <>_ 6 | 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | ## Preamble 11 | 12 | The GNU Affero General Public License is a free, copyleft license for 13 | software and other kinds of works, specifically designed to ensure 14 | cooperation with the community in the case of network server software. 15 | 16 | The licenses for most software and other practical works are designed 17 | to take away your freedom to share and change the works. By contrast, 18 | our General Public Licenses are intended to guarantee your freedom to 19 | share and change all versions of a program--to make sure it remains free 20 | software for all its users. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | Developers that use our General Public Licenses protect your rights 30 | with two steps: **(1)** assert copyright on the software, and **(2)** offer 31 | you this License which gives you legal permission to copy, distribute 32 | and/or modify the software. 33 | 34 | A secondary benefit of defending all users' freedom is that 35 | improvements made in alternate versions of the program, if they 36 | receive widespread use, become available for other developers to 37 | incorporate. Many developers of free software are heartened and 38 | encouraged by the resulting cooperation. However, in the case of 39 | software used on network servers, this result may fail to come about. 40 | The GNU General Public License permits making a modified version and 41 | letting the public access it on a server without ever releasing its 42 | source code to the public. 43 | 44 | The GNU Affero General Public License is designed specifically to 45 | ensure that, in such cases, the modified source code becomes available 46 | to the community. It requires the operator of a network server to 47 | provide the source code of the modified version running there to the 48 | users of that server. Therefore, public use of a modified version, on 49 | a publicly accessible server, gives the public access to the source 50 | code of the modified version. 51 | 52 | An older license, called the Affero General Public License and 53 | published by Affero, was designed to accomplish similar goals. This is 54 | a different license, not a version of the Affero GPL, but Affero has 55 | released a new version of the Affero GPL which permits relicensing under 56 | this license. 57 | 58 | The precise terms and conditions for copying, distribution and 59 | modification follow. 60 | 61 | ## TERMS AND CONDITIONS 62 | 63 | ### 0. Definitions 64 | 65 | “This License” refers to version 3 of the GNU Affero General Public License. 66 | 67 | “Copyright” also means copyright-like laws that apply to other kinds of 68 | works, such as semiconductor masks. 69 | 70 | “The Program” refers to any copyrightable work licensed under this 71 | License. Each licensee is addressed as “you”. “Licensees” and 72 | “recipients” may be individuals or organizations. 73 | 74 | To “modify” a work means to copy from or adapt all or part of the work 75 | in a fashion requiring copyright permission, other than the making of an 76 | exact copy. The resulting work is called a “modified version” of the 77 | earlier work or a work “based on” the earlier work. 78 | 79 | A “covered work” means either the unmodified Program or a work based 80 | on the Program. 81 | 82 | To “propagate” a work means to do anything with it that, without 83 | permission, would make you directly or secondarily liable for 84 | infringement under applicable copyright law, except executing it on a 85 | computer or modifying a private copy. Propagation includes copying, 86 | distribution (with or without modification), making available to the 87 | public, and in some countries other activities as well. 88 | 89 | To “convey” a work means any kind of propagation that enables other 90 | parties to make or receive copies. Mere interaction with a user through 91 | a computer network, with no transfer of a copy, is not conveying. 92 | 93 | An interactive user interface displays “Appropriate Legal Notices” 94 | to the extent that it includes a convenient and prominently visible 95 | feature that **(1)** displays an appropriate copyright notice, and **(2)** 96 | tells the user that there is no warranty for the work (except to the 97 | extent that warranties are provided), that licensees may convey the 98 | work under this License, and how to view a copy of this License. If 99 | the interface presents a list of user commands or options, such as a 100 | menu, a prominent item in the list meets this criterion. 101 | 102 | ### 1. Source Code 103 | 104 | The “source code” for a work means the preferred form of the work 105 | for making modifications to it. “Object code” means any non-source 106 | form of a work. 107 | 108 | A “Standard Interface” means an interface that either is an official 109 | standard defined by a recognized standards body, or, in the case of 110 | interfaces specified for a particular programming language, one that 111 | is widely used among developers working in that language. 112 | 113 | The “System Libraries” of an executable work include anything, other 114 | than the work as a whole, that **(a)** is included in the normal form of 115 | packaging a Major Component, but which is not part of that Major 116 | Component, and **(b)** serves only to enable use of the work with that 117 | Major Component, or to implement a Standard Interface for which an 118 | implementation is available to the public in source code form. A 119 | “Major Component”, in this context, means a major essential component 120 | (kernel, window system, and so on) of the specific operating system 121 | (if any) on which the executable work runs, or a compiler used to 122 | produce the work, or an object code interpreter used to run it. 123 | 124 | The “Corresponding Source” for a work in object code form means all 125 | the source code needed to generate, install, and (for an executable 126 | work) run the object code and to modify the work, including scripts to 127 | control those activities. However, it does not include the work's 128 | System Libraries, or general-purpose tools or generally available free 129 | programs which are used unmodified in performing those activities but 130 | which are not part of the work. For example, Corresponding Source 131 | includes interface definition files associated with source files for 132 | the work, and the source code for shared libraries and dynamically 133 | linked subprograms that the work is specifically designed to require, 134 | such as by intimate data communication or control flow between those 135 | subprograms and other parts of the work. 136 | 137 | The Corresponding Source need not include anything that users 138 | can regenerate automatically from other parts of the Corresponding 139 | Source. 140 | 141 | The Corresponding Source for a work in source code form is that 142 | same work. 143 | 144 | ### 2. Basic Permissions 145 | 146 | All rights granted under this License are granted for the term of 147 | copyright on the Program, and are irrevocable provided the stated 148 | conditions are met. This License explicitly affirms your unlimited 149 | permission to run the unmodified Program. The output from running a 150 | covered work is covered by this License only if the output, given its 151 | content, constitutes a covered work. This License acknowledges your 152 | rights of fair use or other equivalent, as provided by copyright law. 153 | 154 | You may make, run and propagate covered works that you do not 155 | convey, without conditions so long as your license otherwise remains 156 | in force. You may convey covered works to others for the sole purpose 157 | of having them make modifications exclusively for you, or provide you 158 | with facilities for running those works, provided that you comply with 159 | the terms of this License in conveying all material for which you do 160 | not control copyright. Those thus making or running the covered works 161 | for you must do so exclusively on your behalf, under your direction 162 | and control, on terms that prohibit them from making any copies of 163 | your copyrighted material outside their relationship with you. 164 | 165 | Conveying under any other circumstances is permitted solely under 166 | the conditions stated below. Sublicensing is not allowed; section 10 167 | makes it unnecessary. 168 | 169 | ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law 170 | 171 | No covered work shall be deemed part of an effective technological 172 | measure under any applicable law fulfilling obligations under article 173 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 174 | similar laws prohibiting or restricting circumvention of such 175 | measures. 176 | 177 | When you convey a covered work, you waive any legal power to forbid 178 | circumvention of technological measures to the extent such circumvention 179 | is effected by exercising rights under this License with respect to 180 | the covered work, and you disclaim any intention to limit operation or 181 | modification of the work as a means of enforcing, against the work's 182 | users, your or third parties' legal rights to forbid circumvention of 183 | technological measures. 184 | 185 | ### 4. Conveying Verbatim Copies 186 | 187 | You may convey verbatim copies of the Program's source code as you 188 | receive it, in any medium, provided that you conspicuously and 189 | appropriately publish on each copy an appropriate copyright notice; 190 | keep intact all notices stating that this License and any 191 | non-permissive terms added in accord with section 7 apply to the code; 192 | keep intact all notices of the absence of any warranty; and give all 193 | recipients a copy of this License along with the Program. 194 | 195 | You may charge any price or no price for each copy that you convey, 196 | and you may offer support or warranty protection for a fee. 197 | 198 | ### 5. Conveying Modified Source Versions 199 | 200 | You may convey a work based on the Program, or the modifications to 201 | produce it from the Program, in the form of source code under the 202 | terms of section 4, provided that you also meet all of these conditions: 203 | 204 | * **a)** The work must carry prominent notices stating that you modified 205 | it, and giving a relevant date. 206 | * **b)** The work must carry prominent notices stating that it is 207 | released under this License and any conditions added under section 7. 208 | This requirement modifies the requirement in section 4 to 209 | “keep intact all notices”. 210 | * **c)** You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | * **d)** If the work has interactive user interfaces, each must display 218 | Appropriate Legal Notices; however, if the Program has interactive 219 | interfaces that do not display Appropriate Legal Notices, your 220 | work need not make them do so. 221 | 222 | A compilation of a covered work with other separate and independent 223 | works, which are not by their nature extensions of the covered work, 224 | and which are not combined with it such as to form a larger program, 225 | in or on a volume of a storage or distribution medium, is called an 226 | “aggregate” if the compilation and its resulting copyright are not 227 | used to limit the access or legal rights of the compilation's users 228 | beyond what the individual works permit. Inclusion of a covered work 229 | in an aggregate does not cause this License to apply to the other 230 | parts of the aggregate. 231 | 232 | ### 6. Conveying Non-Source Forms 233 | 234 | You may convey a covered work in object code form under the terms 235 | of sections 4 and 5, provided that you also convey the 236 | machine-readable Corresponding Source under the terms of this License, 237 | in one of these ways: 238 | 239 | * **a)** Convey the object code in, or embodied in, a physical product 240 | (including a physical distribution medium), accompanied by the 241 | Corresponding Source fixed on a durable physical medium 242 | customarily used for software interchange. 243 | * **b)** Convey the object code in, or embodied in, a physical product 244 | (including a physical distribution medium), accompanied by a 245 | written offer, valid for at least three years and valid for as 246 | long as you offer spare parts or customer support for that product 247 | model, to give anyone who possesses the object code either **(1)** a 248 | copy of the Corresponding Source for all the software in the 249 | product that is covered by this License, on a durable physical 250 | medium customarily used for software interchange, for a price no 251 | more than your reasonable cost of physically performing this 252 | conveying of source, or **(2)** access to copy the 253 | Corresponding Source from a network server at no charge. 254 | * **c)** Convey individual copies of the object code with a copy of the 255 | written offer to provide the Corresponding Source. This 256 | alternative is allowed only occasionally and noncommercially, and 257 | only if you received the object code with such an offer, in accord 258 | with subsection 6b. 259 | * **d)** Convey the object code by offering access from a designated 260 | place (gratis or for a charge), and offer equivalent access to the 261 | Corresponding Source in the same way through the same place at no 262 | further charge. You need not require recipients to copy the 263 | Corresponding Source along with the object code. If the place to 264 | copy the object code is a network server, the Corresponding Source 265 | may be on a different server (operated by you or a third party) 266 | that supports equivalent copying facilities, provided you maintain 267 | clear directions next to the object code saying where to find the 268 | Corresponding Source. Regardless of what server hosts the 269 | Corresponding Source, you remain obligated to ensure that it is 270 | available for as long as needed to satisfy these requirements. 271 | * **e)** Convey the object code using peer-to-peer transmission, provided 272 | you inform other peers where the object code and Corresponding 273 | Source of the work are being offered to the general public at no 274 | charge under subsection 6d. 275 | 276 | A separable portion of the object code, whose source code is excluded 277 | from the Corresponding Source as a System Library, need not be 278 | included in conveying the object code work. 279 | 280 | A “User Product” is either **(1)** a “consumer product”, which means any 281 | tangible personal property which is normally used for personal, family, 282 | or household purposes, or **(2)** anything designed or sold for incorporation 283 | into a dwelling. In determining whether a product is a consumer product, 284 | doubtful cases shall be resolved in favor of coverage. For a particular 285 | product received by a particular user, “normally used” refers to a 286 | typical or common use of that class of product, regardless of the status 287 | of the particular user or of the way in which the particular user 288 | actually uses, or expects or is expected to use, the product. A product 289 | is a consumer product regardless of whether the product has substantial 290 | commercial, industrial or non-consumer uses, unless such uses represent 291 | the only significant mode of use of the product. 292 | 293 | “Installation Information” for a User Product means any methods, 294 | procedures, authorization keys, or other information required to install 295 | and execute modified versions of a covered work in that User Product from 296 | a modified version of its Corresponding Source. The information must 297 | suffice to ensure that the continued functioning of the modified object 298 | code is in no case prevented or interfered with solely because 299 | modification has been made. 300 | 301 | If you convey an object code work under this section in, or with, or 302 | specifically for use in, a User Product, and the conveying occurs as 303 | part of a transaction in which the right of possession and use of the 304 | User Product is transferred to the recipient in perpetuity or for a 305 | fixed term (regardless of how the transaction is characterized), the 306 | Corresponding Source conveyed under this section must be accompanied 307 | by the Installation Information. But this requirement does not apply 308 | if neither you nor any third party retains the ability to install 309 | modified object code on the User Product (for example, the work has 310 | been installed in ROM). 311 | 312 | The requirement to provide Installation Information does not include a 313 | requirement to continue to provide support service, warranty, or updates 314 | for a work that has been modified or installed by the recipient, or for 315 | the User Product in which it has been modified or installed. Access to a 316 | network may be denied when the modification itself materially and 317 | adversely affects the operation of the network or violates the rules and 318 | protocols for communication across the network. 319 | 320 | Corresponding Source conveyed, and Installation Information provided, 321 | in accord with this section must be in a format that is publicly 322 | documented (and with an implementation available to the public in 323 | source code form), and must require no special password or key for 324 | unpacking, reading or copying. 325 | 326 | ### 7. Additional Terms 327 | 328 | “Additional permissions” are terms that supplement the terms of this 329 | License by making exceptions from one or more of its conditions. 330 | Additional permissions that are applicable to the entire Program shall 331 | be treated as though they were included in this License, to the extent 332 | that they are valid under applicable law. If additional permissions 333 | apply only to part of the Program, that part may be used separately 334 | under those permissions, but the entire Program remains governed by 335 | this License without regard to the additional permissions. 336 | 337 | When you convey a copy of a covered work, you may at your option 338 | remove any additional permissions from that copy, or from any part of 339 | it. (Additional permissions may be written to require their own 340 | removal in certain cases when you modify the work.) You may place 341 | additional permissions on material, added by you to a covered work, 342 | for which you have or can give appropriate copyright permission. 343 | 344 | Notwithstanding any other provision of this License, for material you 345 | add to a covered work, you may (if authorized by the copyright holders of 346 | that material) supplement the terms of this License with terms: 347 | 348 | * **a)** Disclaiming warranty or limiting liability differently from the 349 | terms of sections 15 and 16 of this License; or 350 | * **b)** Requiring preservation of specified reasonable legal notices or 351 | author attributions in that material or in the Appropriate Legal 352 | Notices displayed by works containing it; or 353 | * **c)** Prohibiting misrepresentation of the origin of that material, or 354 | requiring that modified versions of such material be marked in 355 | reasonable ways as different from the original version; or 356 | * **d)** Limiting the use for publicity purposes of names of licensors or 357 | authors of the material; or 358 | * **e)** Declining to grant rights under trademark law for use of some 359 | trade names, trademarks, or service marks; or 360 | * **f)** Requiring indemnification of licensors and authors of that 361 | material by anyone who conveys the material (or modified versions of 362 | it) with contractual assumptions of liability to the recipient, for 363 | any liability that these contractual assumptions directly impose on 364 | those licensors and authors. 365 | 366 | All other non-permissive additional terms are considered “further 367 | restrictions” within the meaning of section 10. If the Program as you 368 | received it, or any part of it, contains a notice stating that it is 369 | governed by this License along with a term that is a further 370 | restriction, you may remove that term. If a license document contains 371 | a further restriction but permits relicensing or conveying under this 372 | License, you may add to a covered work material governed by the terms 373 | of that license document, provided that the further restriction does 374 | not survive such relicensing or conveying. 375 | 376 | If you add terms to a covered work in accord with this section, you 377 | must place, in the relevant source files, a statement of the 378 | additional terms that apply to those files, or a notice indicating 379 | where to find the applicable terms. 380 | 381 | Additional terms, permissive or non-permissive, may be stated in the 382 | form of a separately written license, or stated as exceptions; 383 | the above requirements apply either way. 384 | 385 | ### 8. Termination 386 | 387 | You may not propagate or modify a covered work except as expressly 388 | provided under this License. Any attempt otherwise to propagate or 389 | modify it is void, and will automatically terminate your rights under 390 | this License (including any patent licenses granted under the third 391 | paragraph of section 11). 392 | 393 | However, if you cease all violation of this License, then your 394 | license from a particular copyright holder is reinstated **(a)** 395 | provisionally, unless and until the copyright holder explicitly and 396 | finally terminates your license, and **(b)** permanently, if the copyright 397 | holder fails to notify you of the violation by some reasonable means 398 | prior to 60 days after the cessation. 399 | 400 | Moreover, your license from a particular copyright holder is 401 | reinstated permanently if the copyright holder notifies you of the 402 | violation by some reasonable means, this is the first time you have 403 | received notice of violation of this License (for any work) from that 404 | copyright holder, and you cure the violation prior to 30 days after 405 | your receipt of the notice. 406 | 407 | Termination of your rights under this section does not terminate the 408 | licenses of parties who have received copies or rights from you under 409 | this License. If your rights have been terminated and not permanently 410 | reinstated, you do not qualify to receive new licenses for the same 411 | material under section 10. 412 | 413 | ### 9. Acceptance Not Required for Having Copies 414 | 415 | You are not required to accept this License in order to receive or 416 | run a copy of the Program. Ancillary propagation of a covered work 417 | occurring solely as a consequence of using peer-to-peer transmission 418 | to receive a copy likewise does not require acceptance. However, 419 | nothing other than this License grants you permission to propagate or 420 | modify any covered work. These actions infringe copyright if you do 421 | not accept this License. Therefore, by modifying or propagating a 422 | covered work, you indicate your acceptance of this License to do so. 423 | 424 | ### 10. Automatic Licensing of Downstream Recipients 425 | 426 | Each time you convey a covered work, the recipient automatically 427 | receives a license from the original licensors, to run, modify and 428 | propagate that work, subject to this License. You are not responsible 429 | for enforcing compliance by third parties with this License. 430 | 431 | An “entity transaction” is a transaction transferring control of an 432 | organization, or substantially all assets of one, or subdividing an 433 | organization, or merging organizations. If propagation of a covered 434 | work results from an entity transaction, each party to that 435 | transaction who receives a copy of the work also receives whatever 436 | licenses to the work the party's predecessor in interest had or could 437 | give under the previous paragraph, plus a right to possession of the 438 | Corresponding Source of the work from the predecessor in interest, if 439 | the predecessor has it or can get it with reasonable efforts. 440 | 441 | You may not impose any further restrictions on the exercise of the 442 | rights granted or affirmed under this License. For example, you may 443 | not impose a license fee, royalty, or other charge for exercise of 444 | rights granted under this License, and you may not initiate litigation 445 | (including a cross-claim or counterclaim in a lawsuit) alleging that 446 | any patent claim is infringed by making, using, selling, offering for 447 | sale, or importing the Program or any portion of it. 448 | 449 | ### 11. Patents 450 | 451 | A “contributor” is a copyright holder who authorizes use under this 452 | License of the Program or a work on which the Program is based. The 453 | work thus licensed is called the contributor's “contributor version”. 454 | 455 | A contributor's “essential patent claims” are all patent claims 456 | owned or controlled by the contributor, whether already acquired or 457 | hereafter acquired, that would be infringed by some manner, permitted 458 | by this License, of making, using, or selling its contributor version, 459 | but do not include claims that would be infringed only as a 460 | consequence of further modification of the contributor version. For 461 | purposes of this definition, “control” includes the right to grant 462 | patent sublicenses in a manner consistent with the requirements of 463 | this License. 464 | 465 | Each contributor grants you a non-exclusive, worldwide, royalty-free 466 | patent license under the contributor's essential patent claims, to 467 | make, use, sell, offer for sale, import and otherwise run, modify and 468 | propagate the contents of its contributor version. 469 | 470 | In the following three paragraphs, a “patent license” is any express 471 | agreement or commitment, however denominated, not to enforce a patent 472 | (such as an express permission to practice a patent or covenant not to 473 | sue for patent infringement). To “grant” such a patent license to a 474 | party means to make such an agreement or commitment not to enforce a 475 | patent against the party. 476 | 477 | If you convey a covered work, knowingly relying on a patent license, 478 | and the Corresponding Source of the work is not available for anyone 479 | to copy, free of charge and under the terms of this License, through a 480 | publicly available network server or other readily accessible means, 481 | then you must either **(1)** cause the Corresponding Source to be so 482 | available, or **(2)** arrange to deprive yourself of the benefit of the 483 | patent license for this particular work, or **(3)** arrange, in a manner 484 | consistent with the requirements of this License, to extend the patent 485 | license to downstream recipients. “Knowingly relying” means you have 486 | actual knowledge that, but for the patent license, your conveying the 487 | covered work in a country, or your recipient's use of the covered work 488 | in a country, would infringe one or more identifiable patents in that 489 | country that you have reason to believe are valid. 490 | 491 | If, pursuant to or in connection with a single transaction or 492 | arrangement, you convey, or propagate by procuring conveyance of, a 493 | covered work, and grant a patent license to some of the parties 494 | receiving the covered work authorizing them to use, propagate, modify 495 | or convey a specific copy of the covered work, then the patent license 496 | you grant is automatically extended to all recipients of the covered 497 | work and works based on it. 498 | 499 | A patent license is “discriminatory” if it does not include within 500 | the scope of its coverage, prohibits the exercise of, or is 501 | conditioned on the non-exercise of one or more of the rights that are 502 | specifically granted under this License. You may not convey a covered 503 | work if you are a party to an arrangement with a third party that is 504 | in the business of distributing software, under which you make payment 505 | to the third party based on the extent of your activity of conveying 506 | the work, and under which the third party grants, to any of the 507 | parties who would receive the covered work from you, a discriminatory 508 | patent license **(a)** in connection with copies of the covered work 509 | conveyed by you (or copies made from those copies), or **(b)** primarily 510 | for and in connection with specific products or compilations that 511 | contain the covered work, unless you entered into that arrangement, 512 | or that patent license was granted, prior to 28 March 2007. 513 | 514 | Nothing in this License shall be construed as excluding or limiting 515 | any implied license or other defenses to infringement that may 516 | otherwise be available to you under applicable patent law. 517 | 518 | ### 12. No Surrender of Others' Freedom 519 | 520 | If conditions are imposed on you (whether by court order, agreement or 521 | otherwise) that contradict the conditions of this License, they do not 522 | excuse you from the conditions of this License. If you cannot convey a 523 | covered work so as to satisfy simultaneously your obligations under this 524 | License and any other pertinent obligations, then as a consequence you may 525 | not convey it at all. For example, if you agree to terms that obligate you 526 | to collect a royalty for further conveying from those to whom you convey 527 | the Program, the only way you could satisfy both those terms and this 528 | License would be to refrain entirely from conveying the Program. 529 | 530 | ### 13. Remote Network Interaction; Use with the GNU General Public License 531 | 532 | Notwithstanding any other provision of this License, if you modify the 533 | Program, your modified version must prominently offer all users 534 | interacting with it remotely through a computer network (if your version 535 | supports such interaction) an opportunity to receive the Corresponding 536 | Source of your version by providing access to the Corresponding Source 537 | from a network server at no charge, through some standard or customary 538 | means of facilitating copying of software. This Corresponding Source 539 | shall include the Corresponding Source for any work covered by version 3 540 | of the GNU General Public License that is incorporated pursuant to the 541 | following paragraph. 542 | 543 | Notwithstanding any other provision of this License, you have 544 | permission to link or combine any covered work with a work licensed 545 | under version 3 of the GNU General Public License into a single 546 | combined work, and to convey the resulting work. The terms of this 547 | License will continue to apply to the part which is the covered work, 548 | but the work with which it is combined will remain governed by version 549 | 3 of the GNU General Public License. 550 | 551 | ### 14. Revised Versions of this License 552 | 553 | The Free Software Foundation may publish revised and/or new versions of 554 | the GNU Affero General Public License from time to time. Such new versions 555 | will be similar in spirit to the present version, but may differ in detail to 556 | address new problems or concerns. 557 | 558 | Each version is given a distinguishing version number. If the 559 | Program specifies that a certain numbered version of the GNU Affero General 560 | Public License “or any later version” applies to it, you have the 561 | option of following the terms and conditions either of that numbered 562 | version or of any later version published by the Free Software 563 | Foundation. If the Program does not specify a version number of the 564 | GNU Affero General Public License, you may choose any version ever published 565 | by the Free Software Foundation. 566 | 567 | If the Program specifies that a proxy can decide which future 568 | versions of the GNU Affero General Public License can be used, that proxy's 569 | public statement of acceptance of a version permanently authorizes you 570 | to choose that version for the Program. 571 | 572 | Later license versions may give you additional or different 573 | permissions. However, no additional obligations are imposed on any 574 | author or copyright holder as a result of your choosing to follow a 575 | later version. 576 | 577 | ### 15. Disclaimer of Warranty 578 | 579 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 580 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 581 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY 582 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 583 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 584 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 585 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 586 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 587 | 588 | ### 16. Limitation of Liability 589 | 590 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 591 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 592 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 593 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 594 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 595 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 596 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 597 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 598 | SUCH DAMAGES. 599 | 600 | ### 17. Interpretation of Sections 15 and 16 601 | 602 | If the disclaimer of warranty and limitation of liability provided 603 | above cannot be given local legal effect according to their terms, 604 | reviewing courts shall apply local law that most closely approximates 605 | an absolute waiver of all civil liability in connection with the 606 | Program, unless a warranty or assumption of liability accompanies a 607 | copy of the Program in return for a fee. 608 | 609 | _END OF TERMS AND CONDITIONS_ 610 | 611 | ## How to Apply These Terms to Your New Programs 612 | 613 | If you develop a new program, and you want it to be of the greatest 614 | possible use to the public, the best way to achieve this is to make it 615 | free software which everyone can redistribute and change under these terms. 616 | 617 | To do so, attach the following notices to the program. It is safest 618 | to attach them to the start of each source file to most effectively 619 | state the exclusion of warranty; and each file should have at least 620 | the “copyright” line and a pointer to where the full notice is found. 621 | 622 | 623 | Copyright (C) 624 | 625 | This program is free software: you can redistribute it and/or modify 626 | it under the terms of the GNU Affero General Public License as published by 627 | the Free Software Foundation, either version 3 of the License, or 628 | (at your option) any later version. 629 | 630 | This program is distributed in the hope that it will be useful, 631 | but WITHOUT ANY WARRANTY; without even the implied warranty of 632 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 633 | GNU Affero General Public License for more details. 634 | 635 | You should have received a copy of the GNU Affero General Public License 636 | along with this program. If not, see . 637 | 638 | Also add information on how to contact you by electronic and paper mail. 639 | 640 | If your software can interact with users remotely through a computer 641 | network, you should also make sure that it provides a way for users to 642 | get its source. For example, if your program is a web application, its 643 | interface could display a “Source” link that leads users to an archive 644 | of the code. There are many ways you could offer source, and different 645 | solutions will be better for different programs; see section 13 for the 646 | specific requirements. 647 | 648 | You should also get your employer (if you work as a programmer) or school, 649 | if any, to sign a “copyright disclaimer” for the program, if necessary. 650 | For more information on this, and how to apply and follow the GNU AGPL, see 651 | <>. 652 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HeaderOutput 2 | 3 | _part of LeviLamina Toolchain, to generate BDS header._ 4 | 5 | ___ 6 | 7 | #### Important Contributors 8 | 9 | | ![RimuruChan](https://github.com/RimuruChan.png?size=96) | ![OEOTYAN](https://github.com/OEOTYAN.png?size=96) | ![JasonZYT](https://github.com/Jasonzyt.png?size=96) | ![WangYneos](https://github.com/KawaiiNahida.png?size=96) | 10 | |:--------------------------------------------------------:|:--------------------------------------------------:|:----------------------------------------------------:|:---------------------------------------------------------:| 11 | | [@RimuruChan](https://github.com/RimuruChan) | [@OEOTYAN](https://github.com/OEOTYAN) | [@JasonZYT](https://github.com/Jasonzyt) | [@KawaiiNahida](https://github.com/KawaiiNahida) | 12 | 13 | ___ 14 | 15 | Copyright [LiteLDev](https://github.com/LiteLDev), All Rights Reserved. 16 | 17 | _This project is licensed under the terms of the GNU Affero General Public License v3.0 license._ 18 | 19 | This project has been archived since we have a new private header generation tool. 20 | --- 21 | -------------------------------------------------------------------------------- /assets/config.toml: -------------------------------------------------------------------------------- 1 | [config] 2 | rootPath = "mc" 3 | enableRelativePath = false 4 | 5 | [replacement] 6 | 7 | regex = [ 8 | { regex = "\\(void\\)", to = "()" }, 9 | { regex = "(?", to = "DimensionType" }, 23 | { regex = "Bedrock::Result<(.*), ?std::error_code>", to = "Bedrock::Result<$1>" }, 24 | { regex = "Bedrock::Result<(.*), ?std::error_code>", to = "Bedrock::Result<$1>" }, 25 | { regex = "gsl::not_null< ?class Bedrock::NonOwnerPointer<(.*?)> ?>", to = "Bedrock::NotNullNonOwnerPtr<$1>" }, 26 | { regex = "Bedrock::Threading::ThreadLocalObject<(.*), *std::allocator< *\\1 *> *>", to = "Bedrock::Threading::ThreadLocalObject<$1>" }, 27 | 28 | { regex = "class TypedServerNetId", to = "RecipeNetId" }, 29 | { regex = "class TypedServerNetId", to = "CreativeItemNetId" }, 30 | { regex = "class TypedRuntimeId", to = "ContainerRuntimeId" }, 31 | { regex = "class TypedServerNetId", to = "ItemStackNetId" }, 32 | { regex = "class TypedClientNetId", to = "ItemStackRequestId" }, 33 | { regex = "class TypedClientNetId", to = "ItemStackLegacyRequestId" }, 34 | 35 | { regex = "std::chrono::duration ?>", to = "std::chrono::nanoseconds" }, 36 | { regex = "std::chrono::duration ?>", to = "std::chrono::microseconds" }, 37 | { regex = "std::chrono::duration ?>", to = "std::chrono::milliseconds" }, 38 | { regex = "std::chrono::duration ?>", to = "std::chrono::seconds" }, 39 | { regex = "std::chrono::duration ?>", to = "std::chrono::minutes" }, 40 | { regex = "std::chrono::time_point", to = "std::chrono::steady_clock::time_point" }, 41 | 42 | { regex = "std::basic_string, ?class http_stl_allocator ?>", to = "http_wstring" }, 43 | { regex = "std::basic_string, ?class http_stl_allocator ?>", to = "http_string" }, 44 | { regex = "std::basic_([a-z]*?))?(, ?std::allocator ?)?>", to = "std::w$1" }, 45 | { regex = "std::basic_([a-z]*?))?(, ?std::allocator ?)?>", to = "std::$1" }, 46 | { regex = "std::basic_string_view ?>", to = "std::string_view" }, 47 | 48 | { regex = "std::unordered_(.*?)map<(.*), *(.*), *std::hash< *\\2 *>, *std::equal_to< *\\2 *>, *std::allocator< *std::pair< *\\2 *const *, *\\3 *> *> *>", to = "std::unordered_$1map<$2,$3>" }, 49 | { regex = "std::unordered_map<(.*), *(.*), *std::hash< *\\1 *>, *std::equal_to< *\\1 *>, *std::allocator< *std::pair< *\\1 *const *, *\\2 *> *> *>", to = "std::unordered_map<$1,$2>" }, 50 | { regex = "std::(.*?)map<(.*), *(.*), *std::less< *\\2 *>, *std::allocator< *std::pair< *\\2 *const *, *\\3 *> *> *>", to = "std::$1map<$2,$3>" }, 51 | { regex = "std::map<(.*), *(.*), *std::less< *\\1 *>, *std::allocator< *std::pair< *\\1 *const *, *\\2 *> *> *>", to = "std::map<$1,$2>" }, 52 | { regex = "std::set<(.*), *std::less< *\\1 *>, *std::allocator< *\\1 *> *>", to = "std::set<$1>" }, 53 | { regex = "std::unordered_set<(.*), *std::hash< *\\1 *>, *std::equal_to< *\\1 *>, *std::allocator< *\\1 *> *>", to = "std::unordered_set<$1>" }, 54 | { regex = "std::set<(.*), *std::less< *\\1 *>, *std::allocator< *\\1 *> *>", to = "std::set<$1>" }, 55 | { regex = "std::unordered_set<(.*), *std::hash< *\\1 *>, *std::equal_to< *\\1 *>, *std::allocator< *\\1 *> *>", to = "std::unordered_set<$1>" }, 56 | 57 | { regex = "std::(\\w*)<(.*), *std::default_delete< *\\2 *> *>", to = "std::$1<$2>" }, 58 | { regex = "std::(\\w*)<(.*), *std::allocator< *\\2 *> *>", to = "std::$1<$2>" }, 59 | { regex = "std::(\\w*)<(.*), *std::allocator< *\\2 *> *>", to = "std::$1<$2>" }, 60 | { regex = "gsl::span<(.*), *-1>", to = "gsl::span<$1>" }, 61 | { regex = "gsl::span<(.*), *-1>", to = "gsl::span<$1>" }, 62 | 63 | { regex = "entt::dense_map<(.*), *(.*), *std::hash< *\\1 *>, *std::equal_to< *\\1 *>, *std::allocator< *std::pair< *\\1 *const *, *\\2 *> *> *>", to = "entt::dense_map<$1,$2>" }, 64 | { regex = "entt::dense_set<(.*), *std::hash< *\\1 *>, *std::equal_to< *\\1 *>, *std::allocator< *\\1 *> *>", to = "entt::dense_set<$1>" }, 65 | { regex = "entt::basic_registry<(.*), *std::allocator< *\\1 *> *>", to = "entt::basic_registry<$1>" }, 66 | { regex = "entt::basic_dispatcher ?>", to = "entt::dispatcher" }, 67 | 68 | { regex = "std::map, std::allocator>>", to = "std::map>" }, 69 | { regex = "std::set, std::allocator>", to = "std::set>" }, 70 | { regex = "std::unordered_map<::Enchant::Slot, std::string, struct EnchantSlotEnumHasher, std::equal_to<::Enchant::Slot>, std::allocator>>", to = "std::unordered_map<::Enchant::Slot, std::string, struct EnchantSlotEnumHasher>" }, 71 | { regex = "entt::dense_set, std::allocator>", to = "entt::dense_set" }, 72 | ] 73 | 74 | [exclusion] 75 | 76 | regex = [ 77 | "^$", 78 | "^.*\\(.*$", 79 | "^struct .*$", 80 | "^class .*$", 81 | ".*(@| |<).*", 82 | """^(?:std|gsl|entt|rapidjson|type_info|leveldb|asio|websocketpp|fmt|magic_enum|nonstd).*""", 83 | """^(?:NetherNet|webrtc|rtc|absl|cricket|dcsctp|sigslot|websocketpp|fmt|magic_enum).*""", # NetherNet 84 | "^unity_.*$", # preview 85 | "^Concurrency(?:::.*)*$", # microsft Concurrency, no usage. pplinterface.h 86 | "_WSABUF", # ws2def.h 87 | "addrinfo", # ws2def.h 88 | "^sockaddr.*$", # ws2def.h ws2ipdef.h 89 | "^.*_addr$", # inaddr.h in6addr.h 90 | "^_IP_ADAPTER_PREFIX_XP", # iptypes.h 91 | "^_iobuf", # corecrt_wstdio.h 92 | "^tm", # corecrt_wtime.h 93 | "^_Mbstatet", # corecrt.h 94 | "^HWND__", # DirectX 95 | "^_WINHTTP_WEB_SOCKET_BUFFER_TYPE", # winhttp.h 96 | "^__std_win_error", # xfilesystem_abi.h 97 | "^srtp_event_data_t", # libsrtp 98 | "^wspp_websocket_impl", # websocketpp? 99 | "^shared_ptr_cache", # websocketpp? 100 | "^static_string", # for mc 101 | "^.*VariantBuilder", # for mc 102 | "^ServerNetherNetConnector", # for mc 103 | ] 104 | 105 | [ignore] 106 | 107 | regex = [] 108 | 109 | [sort] 110 | parent = [ 111 | # { parent = "Actor", dst = "Actor" }, 112 | # { parent = "ActorBlock", dst = "ActorBlock" }, 113 | # { parent = "BehaviorDefinition", dst = "BehaviorDefinition" }, 114 | # { parent = "Block", dst = "Block" }, 115 | # { parent = "BlockActor", dst = "BlockActor" }, 116 | # { parent = "BlockLegacy", dst = "BlockLegacy" }, 117 | # { parent = "Item", dst = "Item" }, 118 | # { parent = "Packet", dst = "Packet" }, 119 | # { parent = "Command", dst = "Command" } 120 | ] 121 | 122 | regex = [ 123 | # { regex = "^NetherNet::.*", dst = "network/nethernet", override = true }, 124 | # { regex = "^webrtc.*", dst = "deps/webrtc", override = true }, 125 | # { regex = "^rtc::webrtc_logging_impl::ToStringVal$", dst = "deps/webrtc", override = true }, 126 | # { regex = "^absl.*", dst = "deps/webrtc/absl", override = true }, 127 | # { regex = "^rtc.*", dst = "deps/webrtc/rtc", override = true }, 128 | # { regex = "^cricket::.*ContentDescription", dst = "deps/webrtc/cricket/description", override = true }, 129 | # { regex = "^cricket.*", dst = "deps/webrtc/cricket", override = true }, 130 | # { regex = "^dcsctp.*", dst = "deps/webrtc/dcsctp", override = true }, 131 | # { regex = "^sigslot.*", dst = "deps/webrtc/sigslot", override = true }, 132 | { regex = ".*RakNet.*", dst = "deps/raknet", override = false }, 133 | { regex = "OreUI::.*", dst = "client/gui/oreui", override = false }, 134 | { regex = ".*DataStructures.*", dst = "deps/raknet/data_structures", override = false }, 135 | { regex = "^Has.*Filter$", dst = "world/actor/selectors", override = false }, 136 | { regex = "^CameraPresetsInternals::.*", dst = "world/actor/common", override = false }, 137 | { regex = "^Crypto::Hash::.*", dst = "deps/crypto/hash", override = false }, 138 | { regex = "^Crypto::Random::.*", dst = "deps/crypto/random", override = false }, 139 | { regex = "^Crypto::Symmetric::.*", dst = "deps/crypto/symmetric", override = false }, 140 | { regex = "^Crypto::Asymmetric::.*", dst = "deps/crypto/asymmetric", override = false }, 141 | { regex = "^BlockGeometrySerializer::.*", dst = "world/level/block/utils", override = true }, 142 | { regex = "^Crypto::.*", dst = "deps/crypto", override = false }, 143 | { regex = "^AgentCommands::.*", dst = "world/actor/agent", override = true }, 144 | { regex = ".*Packet$", dst = "network/packet", override = false }, 145 | { regex = ".*PacketHandler.*$", dst = "network", override = false }, 146 | { regex = "^mce::.*", dst = "deps/core/mce", override = false }, 147 | { regex = "^ScriptModuleMinecraftServerUI.*", dst = "scripting/modules/minecraft_ui", override = false }, 148 | { regex = "^ScriptModuleMinecraftServerAdmin.*", dst = "scripting/modules/minecraft_server_admin", override = false }, 149 | { regex = "^ScriptModuleMinecraftNet.*", dst = "scripting/modules/minecraft_net", override = false }, 150 | { regex = "^ScriptModuleMinecraft.*", dst = "scripting/modules/minecraft", override = false }, 151 | { regex = "^ScriptModuleIdentity.*", dst = "scripting/modules/identity", override = false }, 152 | { regex = "^Script.*GameTest.*", dst = "external/scripting/gametest", override = false }, 153 | { regex = "^Scripting::QuickJS.*", dst = "external/scripting/quickjs", override = false }, 154 | { regex = "^Script.*", dst = "external/scripting", override = false }, 155 | { regex = "^[Ii]mgui.*", dst = "external/imgui", override = false }, 156 | { regex = "^Core::.*", dst = "deps/core", override = false }, 157 | { regex = "^Util::CodeBuilder.*", dst = "codebuilder/utils", override = false }, 158 | { regex = "^CodeBuilder.*", dst = "codebuilder", override = false }, 159 | { regex = "^Bedrock::PubSub::.*", dst = "deps/core/common/bedrock/pubsub", override = false }, 160 | { regex = "^Bedrock::.*", dst = "deps/core/common/bedrock", override = false }, 161 | { regex = "^BedrockLog::.*", dst = "deps/core/common/debug", override = false }, 162 | { regex = "^BedrockBlock.*", dst = "world/level/block/utils", override = false }, 163 | { regex = "VanillaComplexAliasBlockUtils.*", dst = "world/level/block/utils/updater", override = false }, 164 | { regex = ".*VanillaBlock.*", dst = "world/level/block/utils", override = false }, 165 | { regex = ".*Json.*", dst = "deps/json", override = false }, 166 | { regex = "brstd.*", dst = "deps/brstd", override = false }, 167 | { regex = "^Item.*", dst = "world/item/components", override = false }, 168 | { regex = "^ComponentItem.*", dst = "world/item/components", override = false }, 169 | { regex = ".*ItemComponent.*", dst = "world/item/components", override = false }, 170 | { regex = ".*Block.*Component.*", dst = "world/level/block/components", override = false }, 171 | { regex = "^AgentComponents::Actions::.*", dst = "entity/components/agent/actions", override = false }, 172 | { regex = "^AgentComponents.*", dst = "entity/components/agent", override = false }, 173 | { regex = "^RideableComponentHelpers::.*", dst = "entity/components", override = false }, 174 | { regex = "^RepairableItem.*Versioning.*", dst = "world/item/components/repairable", override = true }, 175 | { regex = "^DurabilityItem.*Versioning.*", dst = "world/item/components/durability", override = true }, 176 | { regex = "^ArmorItem.*Versioning.*", dst = "world/item/components/armor", override = true }, 177 | { regex = "^WearableItem.*Versioning.*", dst = "world/item/components/wearable", override = true }, 178 | { regex = "^EquipItem.*Versioning.*", dst = "world/item/components/equip", override = true }, 179 | { regex = "^Shareable.*Versioning.*", dst = "world/item/components/shareable", override = true }, 180 | { regex = "^BlockSelectionBoxComponentDescriptor.*", dst = "world/level/block/components/selectionbox", override = true }, 181 | { regex = "^BlockCollisionBoxComponentDescriptor.*", dst = "world/level/block/components/collisionbox", override = true }, 182 | { regex = "^BlockQueuedTickingComponentDescriptor.*", dst = "world/level/block/components/queuedticking", override = true }, 183 | { regex = ".*Item.*Versioning::.*", dst = "world/item/components", override = false }, 184 | { regex = "^ExplorationMap.*", dst = "world/item/components", override = false }, 185 | { regex = ".*Component$", dst = "world/components", override = false }, 186 | { regex = ".*BlockActor$", dst = "world/level/block/actor", override = false }, 187 | { regex = "^(?!.*::).*Block$", dst = "world/level/block", override = false }, 188 | { regex = "^Block.*", dst = "world/level/block/utils", override = false }, 189 | { regex = "^[Gg]ame[Tt]est.*", dst = "gametest/framework", override = false }, 190 | { regex = ".*Event$", dst = "events", override = false }, 191 | { regex = ".*Command$", dst = "server/commands", override = false }, 192 | { regex = ".*websocket.*", dst = "websockets", override = false }, 193 | { regex = "^Social::.*", dst = "client/social", override = false }, 194 | { regex = "^persona.*", dst = "client/services/persona", override = false }, 195 | { regex = "^PlayerCapabilities.*", dst = "config", override = false }, 196 | { regex = ".*Container.*", dst = "world/containers", override = false }, 197 | { regex = ".*[Nn]pc.*", dst = "world/actor/npc", override = false }, 198 | { regex = "^Command.*Flag$", dst = "server/commands/flags", override = false }, 199 | { regex = ".*Flag$", dst = "entity/flags", override = false }, 200 | { regex = ".*Test$", dst = "world/filters", override = false }, 201 | { regex = "Network.*", dst = "network", override = false }, 202 | { regex = ".*NetworkSystem.*", dst = "network", override = false }, 203 | { regex = "^UpdateBoundingBox.*", dst = "entity/systems/common/boundingbox", override = false }, 204 | { regex = ".*System.*", dst = "entity/systems/common", override = false }, 205 | { regex = "^Util::.*", dst = "deps/core/utility", override = false }, 206 | { regex = "^Actor.*", dst = "entity/utilities", override = false }, 207 | { regex = ".*Utility$", dst = "deps/core/utility", override = false }, 208 | { regex = "^Level.*", dst = "world/level", override = false }, 209 | { regex = "^Editor::Cursor::.*", dst = "server/editor/cursor", override = false }, 210 | { regex = "^Editor::.*UI.*", dst = "server/editor/ui", override = false }, 211 | { regex = "^Editor::.*Server.*", dst = "server/editor/server", override = false }, 212 | { regex = "^Editor::.*", dst = "server/editor", override = false }, 213 | { regex = ".*Command.*", dst = "server/commands", override = false }, 214 | { regex = "^Automation.*", dst = "network", override = false }, 215 | { regex = "^[Cc]ereal::internal.*", dst = "deps/cereal/internal", override = false }, 216 | { regex = "^[Cc]ereal.*", dst = "deps/cereal", override = false }, 217 | { regex = "^VanillaSurfaceBuilders.*", dst = "world/level/biome/surface", override = false }, 218 | { regex = "^xbox.*", dst = "client/network", override = false }, 219 | { regex = "^BiomeSourceUtil.*", dst = "world/level/biome", override = false }, 220 | { regex = "^ClimateUtils.*", dst = "world/level/biome", override = false }, 221 | { regex = "^ColumnUtils.*", dst = "world/level/levelgen/feature/helpers", override = false }, 222 | { regex = "^CanyonFeatureUtils.*", dst = "world/level/levelgen/feature", override = false }, 223 | { regex = "^CaveFeatureUtils.*", dst = "world/level/levelgen/feature", override = false }, 224 | { regex = "^BlendingDataFactoryUtil.*", dst = "world/level/storage", override = false }, 225 | { regex = "^Puv.*", dst = "deps/puv", override = false }, 226 | { regex = "^Puv::v1_20_50.*", dst = "deps/puv/v1_20_50", override = true }, 227 | { regex = "^Puv::v1_20_60.*", dst = "deps/puv/v1_20_60", override = true }, 228 | { regex = "^Puv::v1_20_80.*", dst = "deps/puv/v1_20_80", override = true }, 229 | { regex = "^snappy.*", dst = "deps/snappy", override = false }, 230 | { regex = "^VolumeInstanceData.*", dst = "volume", override = false }, 231 | { regex = "^VanillaStates.*", dst = "world/level/block/utils", override = false }, 232 | { regex = "^TreeHelper.*", dst = "world/level/levelgen/feature/helpers", override = false }, 233 | { regex = "^SplitBlockUtils.*", dst = "world/level/block/utils", override = false }, 234 | { regex = ".*BlockState.*", dst = "world/level/block/states", override = false }, 235 | { regex = "^Sculk.*", dst = "world/level/block/utils", override = false }, 236 | { regex = "^PackCapabilities.*", dst = "resources", override = false }, 237 | { regex = "^Resource.*", dst = "resources", override = false }, 238 | { regex = "^GameLightingChecker.*", dst = "world/level/chunk", override = false }, 239 | { regex = "^ExpressionNodeSerializer.*", dst = "util", override = false }, 240 | { regex = "^DripstoneUtils.*", dst = "world/level/levelgen/feature/helpers", override = false }, 241 | { regex = "^DebugUtils.*", dst = "deps/core/common/debug", override = false }, 242 | { regex = "^Concurrency.*", dst = "deps", override = false }, 243 | { regex = "^Common.*", dst = "common", override = false }, 244 | { regex = "^HC.*", dst = "network", override = false }, 245 | { regex = "^http.*", dst = "network", override = false }, 246 | { regex = "^Web.*", dst = "network", override = false }, 247 | { regex = "^Legacy.*", dst = "deps/core/resource", override = false }, 248 | { regex = ".*span.*", dst = "deps/core/utility", override = false }, 249 | { regex = "^Entity.*", dst = "entity", override = false }, 250 | { regex = "^Service.*", dst = "services", override = false }, 251 | { regex = ".*Net.*", dst = "network", override = false }, 252 | { regex = ".*_TP_.*", dst = "network", override = false }, 253 | { regex = ".*Async.*", dst = "network", override = false }, 254 | { regex = ".*Event.*", dst = "world/events", override = false }, 255 | { regex = ".*[Oo]ptional.*", dst = "common/wrapper", override = false }, 256 | { regex = ".*Ptr.*", dst = "common/wrapper", override = false }, 257 | { regex = ".*_ptr$", dst = "common/wrapper", override = false }, 258 | { regex = ".*Ref.*", dst = "common/wrapper", override = false }, 259 | { regex = ".*Result.*", dst = "common/wrapper", override = false }, 260 | { regex = ".*Trim.*", dst = "world/trim", override = false }, 261 | { regex = ".*Achievement.*", dst = "client/achievements", override = false }, 262 | { regex = ".*Biome.*", dst = "world/level/biome", override = false }, 263 | { regex = ".*Chunk.*", dst = "world/level/chunk", override = false }, 264 | { regex = ".*External.*", dst = "world/level", override = false }, 265 | { regex = ".*Player.*", dst = "world/actor/player", override = false }, 266 | { regex = "^VanillaActor.*", dst = "world/actor", override = false }, 267 | { regex = ".*GameMode.*", dst = "world/gamemode", override = false }, 268 | { regex = "^Pack.*", dst = "resources", override = false }, 269 | { regex = "^StructurePool.*", dst = "world/level/levelgen/structure/structurepools", override = false }, 270 | { regex = ".*Structure.*", dst = "world/level/levelgen/structure", override = false }, 271 | { regex = ".*Dimension.*", dst = "world/level/dimension", override = false }, 272 | { regex = ".*GameVersion.*", dst = "resources", override = false }, 273 | { regex = ".*Goal.*", dst = "world/actor/ai/goal", override = false }, 274 | { regex = ".*Block.*", dst = "world/level/block/utils", override = false }, 275 | { regex = ".*Util.*", dst = "util", override = false }, 276 | { regex = ".*Actor.*", dst = "world/actor/components", override = false }, 277 | { regex = ".*Generator.*", dst = "world/level/levelgen", override = false }, 278 | { regex = ".*TerrainAdjustment.*", dst = "world/level/levelgen/adjustment", override = false }, 279 | { regex = "^br::.*", dst = "world/level/levelgen", override = false }, 280 | { regex = "^sim::.*", dst = "server/sim", override = false }, 281 | { regex = ".*Tree.*", dst = "world/level/levelgen/feature/helpers", override = false }, 282 | { regex = "^JS[A-Z].*", dst = "external/scripting", override = false }, 283 | { regex = "^rapidjson.*", dst = "external/rapidjson", override = false }, 284 | { regex = "^glm.*", dst = "external/glm", override = false }, 285 | { regex = "^MicroProfile.*", dst = "external/microprofile", override = false }, 286 | { regex = "^moodycamel.*", dst = "external/concurrentqueue", override = false }, 287 | { regex = "^Im[A-Z].*", dst = "external/imgui", override = false }, 288 | { regex = "^OS::.*", dst = "external/lib_http_client", override = false }, 289 | { regex = "^cg::.*", dst = "deps/core_graphics/common", override = false }, 290 | { regex = "^SFAT::.*", dst = "external/sfat", override = false }, 291 | { regex = "^qrcodegen::.*", dst = "external/qrcodegen", override = false }, 292 | { regex = "^storeSearch::.*", dst = "client/store", override = false }, 293 | { regex = "^SFAT::.*", dst = "external/sfat", override = false }, 294 | { regex = "^ClientBlobCache::.*", dst = "server/blob_cache", override = false }, 295 | { regex = "^Molang.*", dst = "util/molang", override = false }, 296 | { regex = "^TextureSetHelpers::.*", dst = "util", override = false }, 297 | { regex = "^ValueProviders::.*", dst = "util", override = false }, 298 | { regex = ".*OperationNode.*", dst = "world/level/newbiome", override = false }, 299 | { regex = "^MobMovement.*", dst = "entity/systems/movement", override = false }, 300 | { regex = "^PositionTrackingDB::.*", dst = "world/level", override = false }, 301 | { regex = "^FeatureLoading::.*", dst = "world/level/levelgen/feature", override = false }, 302 | { regex = "^.*Feature.*", dst = "world/level/levelgen/feature", override = false }, 303 | { regex = "^librarySearch::.*", dst = "common", override = false }, 304 | { regex = ".*GameplayHandler.*", dst = "gameplayhandlers", override = false }, 305 | { regex = "^Realms.*", dst = "client/network/realms", override = false }, 306 | { regex = ".*Transformation.*", dst = "world/level/biome/components", override = false }, 307 | { regex = ".*_st$", dst = "external/openssl", override = false }, 308 | { regex = "^I[A-Z].*", dst = "common", override = false }, 309 | { regex = "^ScriptModuleMinecraftDebugUtilities::.*", dst = "scripting/modules/minecraft/debug", override = true }, 310 | { regex = "^BlockEvents::.*", dst = "world/level/block/events", override = true }, 311 | ] 312 | -------------------------------------------------------------------------------- /assets/predefine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma warning(disable : 4099) // for MOJANG : type name first seen using 'class' now seen using 'struct' 4 | #pragma warning(disable : 4201) // for MOJANG : nonstandard extension used : nameless struct/union 5 | 6 | #define MCAPI __declspec(dllimport) 7 | #define MCVAPI MCAPI 8 | 9 | #define MCTAPI \ 10 | template <> \ 11 | MCAPI 12 | 13 | #include // STL general algorithms 14 | #include // STL array container 15 | #include // STL bitset container 16 | #include // Character handling functions 17 | #include // C Error codes 18 | #include // C++11 Time library 19 | #include // C localization library 20 | #include // Common mathematics functions 21 | #include // Complex number type 22 | #include // C Standard Input/Output library 23 | #include // C Standard General Utilities Library 24 | #include // C string handling 25 | #include // C Time library 26 | #include // Wide character type 27 | #include // Wide character classification 28 | #include // STL double ended queue container 29 | #include // Exception handling classes 30 | #include // STL forward list container 31 | #include // File stream classes 32 | #include // STL Function objects 33 | #include // Input/Output manipulators 34 | #include // Base input/output stream classes 35 | #include // Input/Output forward declarations 36 | #include // Standard Input/Output stream objects 37 | #include // Basic input stream classes 38 | #include // Numeric limits 39 | #include // STL linear list container 40 | #include // STL map container 41 | #include // STL unique_ptr, shared_ptr, weak_ptr 42 | #include // STL mutual exclusion primitives 43 | #include // numbers standard header 44 | #include // STL optional type 45 | #include // Basic output stream classes 46 | #include // STL queue and priority_queue container 47 | #include // C++11 Random numbers 48 | #include // STL set and multiset container 49 | #include // STL shared mutual exclusion primitives 50 | #include // String stream classes 51 | #include // STL stack container 52 | #include // Standard exception objects 53 | #include // Stream buffer classes 54 | #include // String class 55 | #include // STL string_view type 56 | #include // STL unordered map container 57 | #include // STL unordered set container 58 | #include // STL utility components 59 | #include // STL variant type 60 | #include // STL dynamic array container 61 | 62 | #include "entt/entt.hpp" // Entity Component System Library 63 | #include "entt/fwd.hpp" // Entity Component Forward Declarations 64 | #include "glm/glm.hpp" // OpenGL Mathematics Library 65 | #include "gsl/gsl" // Guideline Support Library 66 | 67 | // LevelDB C++ Library 68 | #include "leveldb/c.h" 69 | #include "leveldb/cache.h" 70 | #include "leveldb/comparator.h" 71 | #include "leveldb/db.h" 72 | #include "leveldb/dumpfile.h" 73 | #include "leveldb/env.h" 74 | #include "leveldb/filter_policy.h" 75 | #include "leveldb/iterator.h" 76 | #include "leveldb/options.h" 77 | #include "leveldb/slice.h" 78 | #include "leveldb/status.h" 79 | #include "leveldb/table.h" 80 | #include "leveldb/table_builder.h" 81 | #include "leveldb/write_batch.h" 82 | 83 | // RapidJSON C++ Library 84 | #include "rapidjson/document.h" 85 | #include "rapidjson/encodings.h" 86 | #include "rapidjson/error/en.h" 87 | #include "rapidjson/filereadstream.h" 88 | #include "rapidjson/filewritestream.h" 89 | #include "rapidjson/pointer.h" 90 | #include "rapidjson/prettywriter.h" 91 | #include "rapidjson/rapidjson.h" 92 | #include "rapidjson/reader.h" 93 | #include "rapidjson/schema.h" 94 | #include "rapidjson/stringbuffer.h" 95 | #include "rapidjson/writer.h" 96 | 97 | using uchar = uint8_t; 98 | using ushort = uint16_t; 99 | using uint = uint32_t; 100 | using ulong = unsigned long; 101 | using uint64 = uint64_t; 102 | 103 | using schar = int8_t; 104 | using int64 = int64_t; 105 | 106 | using ldouble = long double; 107 | using FacingID = uchar; 108 | 109 | // clang-format off 110 | template 111 | class AutomaticID; 112 | class Dimension; 113 | using DimensionType = AutomaticID; 114 | 115 | struct RecipeNetIdTag; 116 | struct CreativeItemNetIdTag; 117 | struct ContainerRuntimeIdTag; 118 | struct ItemStackNetIdTag; 119 | struct ItemStackRequestIdTag; 120 | struct ItemStackLegacyRequestIdTag; 121 | 122 | template 123 | class TypedClientNetId; 124 | template 125 | class TypedServerNetId; 126 | template 127 | class TypedRuntimeId; 128 | 129 | using RecipeNetId = TypedServerNetId; 130 | using CreativeItemNetId = TypedServerNetId; 131 | using ContainerRuntimeId = TypedRuntimeId; 132 | using ItemStackNetId = TypedServerNetId; 133 | using ItemStackRequestId = TypedClientNetId; 134 | using ItemStackLegacyRequestId = TypedClientNetId; 135 | 136 | namespace Bedrock { 137 | template 138 | class Result; 139 | template 140 | class NonOwnerPointer; 141 | template 142 | using NotNullNonOwnerPtr = gsl::not_null>; 143 | 144 | namespace Threading { 145 | template > 146 | class ThreadLocalObject; 147 | }; 148 | }; 149 | 150 | template 151 | class http_stl_allocator; 152 | using http_string = std::basic_string, class http_stl_allocator>; 153 | using http_wstring = std::basic_string, class http_stl_allocator>; 154 | 155 | namespace asio::ssl { 156 | class verify_context; 157 | } 158 | // clang-format on 159 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 2 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 3 | 4 | plugins { 5 | kotlin("jvm") version "1.9.0" 6 | kotlin("plugin.serialization") version "1.9.0" 7 | id("com.github.johnrengelman.shadow") version "7.1.2" 8 | java 9 | distribution 10 | application 11 | } 12 | 13 | group = "com.liteldev" 14 | version = "1.2.1" 15 | val mainName = "com.liteldev.headeroutput.HeaderOutput" 16 | 17 | application { 18 | mainClass.set(mainName) 19 | } 20 | 21 | repositories { 22 | mavenCentral() 23 | maven { url = uri("https://jitpack.io") } 24 | } 25 | 26 | dependencies { 27 | implementation(kotlin("stdlib")) 28 | implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.0") 29 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") 30 | implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.5") 31 | implementation("org.apache.logging.log4j:log4j-api:2.20.0") 32 | implementation("org.apache.logging.log4j:log4j-core:2.20.0") 33 | implementation("org.apache.logging.log4j:log4j-slf4j-impl:2.20.0") 34 | implementation("io.github.oshai:kotlin-logging-jvm:5.0.0") 35 | implementation("cc.ekblad:4koma:1.2.0") 36 | 37 | testImplementation(kotlin("test")) 38 | } 39 | 40 | tasks.test { 41 | useJUnitPlatform() 42 | } 43 | 44 | tasks.getByName("shadowJar") { 45 | } 46 | 47 | tasks.withType { 48 | kotlinOptions.jvmTarget = JavaVersion.VERSION_17.toString() 49 | } 50 | 51 | tasks.withType { 52 | sourceCompatibility = JavaVersion.VERSION_17.toString() 53 | targetCompatibility = JavaVersion.VERSION_17.toString() 54 | } 55 | 56 | tasks.build { dependsOn(tasks.named("shadowJar")) } 57 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiteLDev/HeaderOutput/d4eabfd46094dec9397924dbdc821ec57077a9f0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "HeaderOutput" 2 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput 2 | 3 | import com.liteldev.headeroutput.config.GeneratorConfig 4 | import com.liteldev.headeroutput.entity.BaseType 5 | import io.github.oshai.kotlinlogging.KotlinLogging 6 | import java.io.File 7 | 8 | object HeaderGenerator { 9 | 10 | const val HEADER_SUFFIX = "h" 11 | private const val HEADER_TEMPLATE = "#pragma once\n\n" 12 | private val PREDEFINE_FILE_NAME = "${GeneratorConfig.rootPath}/_HeaderOutputPredefine.$HEADER_SUFFIX" 13 | private val logger = KotlinLogging.logger { } 14 | 15 | val pathMap = hashMapOf>() 16 | 17 | fun generate() { 18 | logger.info { "Generating header files" } 19 | File(GeneratorConfig.generatePath).mkdirs() 20 | createPredefineFile() 21 | var lastLine = "" 22 | TypeManager.nestingMap.forEach { (name, baseType) -> 23 | print("\r${" ".repeat(lastLine.length)}\r") 24 | lastLine = "Generating ${name}..." 25 | print(lastLine) 26 | generate(baseType) 27 | } 28 | print("\r${" ".repeat(lastLine.length)}\r") 29 | logger.info { "Generating header files done" } 30 | } 31 | 32 | private fun generate(type: BaseType) { 33 | val path = type.path 34 | if (pathMap.containsKey(path)) { 35 | pathMap[path]!!.add(type) 36 | } else { 37 | pathMap[path] = mutableListOf(type) 38 | } 39 | 40 | when { 41 | type.isNamespace() -> generateNamespace(type) 42 | else -> { 43 | val sb = StringBuilder() 44 | if (type.outerType != null) { 45 | sb.appendLine("namespace ${type.outerType!!.name} {") 46 | sb.appendLine() 47 | sb.appendLine(type.generateTypeDefine()) 48 | sb.appendLine("};") 49 | } else { 50 | sb.appendLine(type.generateTypeDefine()) 51 | } 52 | val file = File(GeneratorConfig.generatePath, type.path) 53 | file.parentFile.mkdirs() 54 | file.writeText( 55 | buildString { 56 | append(HEADER_TEMPLATE) 57 | append("#include \"${type.path.relativePathTo(PREDEFINE_FILE_NAME)}\"\n\n") 58 | append(generateIncludesAndForwardDeclare(type)) 59 | append(sb.toString()) 60 | } 61 | ) 62 | } 63 | } 64 | } 65 | 66 | private fun generateNamespace(type: BaseType) { 67 | require(type.isNamespace()) { "${type.name} is not namespace" } 68 | 69 | val file = File(GeneratorConfig.generatePath, type.path) 70 | file.parentFile.mkdirs() 71 | file.writeText( 72 | buildString { 73 | append(HEADER_TEMPLATE) 74 | append("#include \"${type.path.relativePathTo(PREDEFINE_FILE_NAME)}\"\n\n") 75 | append(generateIncludesAndForwardDeclare(type)) 76 | append(type.generateTypeDefine()) 77 | } 78 | ) 79 | 80 | if (type.innerTypes.isNotEmpty()) { 81 | type.innerTypes.forEach { innerType -> 82 | generate(innerType) 83 | } 84 | } 85 | } 86 | 87 | private fun generateIncludesAndForwardDeclare(type: BaseType): String { 88 | return buildString { 89 | if (type.includeList.isNotEmpty()) { 90 | appendLine("// auto generated inclusion list") 91 | appendLine(type.includeList.sorted() 92 | .joinToString("\n") { "#include \"$it\"" }) 93 | appendLine() 94 | } 95 | if (type.forwardDeclareList.isNotEmpty()) { 96 | appendLine("// auto generated forward declare list") 97 | appendLine("// clang-format off") 98 | appendLine(type.forwardDeclareList.sorted() 99 | .joinToString("\n") { it }) 100 | appendLine("// clang-format on\n") 101 | } 102 | } 103 | } 104 | 105 | private fun createPredefineFile() { 106 | val file = File(GeneratorConfig.generatePath, PREDEFINE_FILE_NAME) 107 | file.parentFile.mkdirs() 108 | file.writeText(File(GeneratorConfig.predefineHeaderPath).readText()) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput 2 | 3 | import com.liteldev.headeroutput.TypeManager.typeDataMap 4 | import com.liteldev.headeroutput.config.GeneratorConfig 5 | import com.liteldev.headeroutput.data.* 6 | import com.liteldev.headeroutput.entity.BaseType 7 | import com.liteldev.headeroutput.entity.ClassType 8 | import com.liteldev.headeroutput.entity.NamespaceType 9 | import io.github.oshai.kotlinlogging.KotlinLogging 10 | import kotlinx.cli.ArgParser 11 | import kotlinx.cli.ArgType 12 | import kotlinx.cli.default 13 | import kotlinx.serialization.ExperimentalSerializationApi 14 | import kotlinx.serialization.json.* 15 | import java.io.File 16 | import java.util.* 17 | 18 | @OptIn(ExperimentalSerializationApi::class) 19 | private val json = Json { coerceInputValues = true; explicitNulls = false } 20 | 21 | object HeaderOutput { 22 | private val logger = KotlinLogging.logger { } 23 | 24 | private lateinit var originData: JsonObject 25 | lateinit var classNameList: MutableSet 26 | lateinit var structNameList: MutableSet 27 | 28 | @JvmStatic 29 | fun main(args: Array) { 30 | if (!readCommandLineArgs(args)) return 31 | 32 | GeneratorConfig.loadConfig() 33 | 34 | clearOldFiles() 35 | loadOriginData() 36 | loadIdentifiedTypes() 37 | constructTypes() 38 | 39 | TypeManager.constructNotExistedType() 40 | TypeManager.initParents() 41 | TypeManager.initNestingMap() 42 | TypeManager.initReferences() 43 | TypeManager.initInclusionList() 44 | 45 | HeaderGenerator.generate() 46 | val duplicateFiles = HeaderGenerator.pathMap.filterValues { it.size > 1 } 47 | if (duplicateFiles.isNotEmpty()) { 48 | logger.warn { "These files are generated more than once:" } 49 | duplicateFiles.toSortedMap().forEach { (path, types) -> 50 | logger.warn { "$path: ${types.joinToString { it.name }}" } 51 | } 52 | } 53 | 54 | logger.warn { "These types are not sorted by any rules or declare map: ${BaseType.notSortedTypes.sorted()}" } 55 | } 56 | 57 | private fun readCommandLineArgs(args: Array): Boolean { 58 | val parser = ArgParser("HeaderOutput") 59 | val alwaysTrue by parser.option(ArgType.Boolean, "y", "y", "Always true").default(true) 60 | val configPath by parser.option(ArgType.String, "config", "c", "The config file path").default("./config.toml") 61 | val declareMapFile by parser.option(ArgType.String, "declare-map", "d", "The declare map file path") 62 | .default("./declareMap.json") 63 | val predefineHeader by parser.option(ArgType.String, "predefine-header", "p", "The predefine header file path") 64 | .default("./predefine.h") 65 | val generatePath by parser.option(ArgType.String, "output-dir", "o", "The header output path") 66 | .default("./header") 67 | val jsonPath by parser.option(ArgType.String, "input", "i", "The original data json file path") 68 | .default("./originalData.json") 69 | parser.parse(args) 70 | GeneratorConfig.alwaysTrue = alwaysTrue 71 | GeneratorConfig.configPath = configPath 72 | GeneratorConfig.generatePath = generatePath 73 | GeneratorConfig.jsonPath = jsonPath 74 | GeneratorConfig.declareMapPath = declareMapFile 75 | GeneratorConfig.predefineHeaderPath = predefineHeader 76 | if (!File(GeneratorConfig.configPath).isFile) { 77 | logger.error { "Invalid config file path" } 78 | return false 79 | } 80 | if (!File(GeneratorConfig.generatePath).isDirectory) { 81 | if (!File(GeneratorConfig.generatePath).mkdirs()) { 82 | logger.error { "Fail to create generate header files path" } 83 | return false 84 | } 85 | } 86 | if (!File(GeneratorConfig.jsonPath).isFile) { 87 | logger.error { "Invalid original data json file path" } 88 | return false 89 | } 90 | if (!File(GeneratorConfig.declareMapPath).isFile) { 91 | logger.error { "Invalid declare map file path" } 92 | return false 93 | } 94 | if (!File(GeneratorConfig.predefineHeaderPath).isFile) { 95 | logger.error { "Invalid predefine header file path" } 96 | return false 97 | } 98 | return true 99 | } 100 | 101 | private fun clearOldFiles() { 102 | // ask to delete all files in generate path 103 | val generatePath = File(GeneratorConfig.generatePath) 104 | if (generatePath.exists()) { 105 | if (GeneratorConfig.alwaysTrue) { 106 | generatePath.deleteRecursively() 107 | return 108 | } 109 | print("Delete all files in ${generatePath.canonicalPath}? (y/n): ") 110 | val input = readlnOrNull() 111 | if (input == "y") { 112 | generatePath.deleteRecursively() 113 | } else { 114 | logger.info { "Skip deleting files" } 115 | } 116 | } 117 | } 118 | 119 | private fun loadOriginData() { 120 | logger.info { "Loading origin data..." } 121 | val configText = File(GeneratorConfig.jsonPath).readText() 122 | originData = Json.parseToJsonElement(configText).jsonObject 123 | Objects.requireNonNull(originData["classes"], "origin data must contains classes field") 124 | typeDataMap.putAll(originData["classes"]!!.jsonObject.mapValues { entry -> 125 | runCatching { json.decodeFromJsonElement(entry.value) }.getOrNull() ?: run { 126 | logger.error { "Fail to parse type data ${entry.key}" } 127 | TypeData.empty() 128 | } 129 | }.toMutableMap()) 130 | typeDataMap.values.forEach { type -> 131 | var counter = 0 132 | type.virtual.forEach { 133 | // 对于没有名字的虚函数,将其标记为未知函数,并且将其名字设置为 __unk_vfn_0, __unk_vfn_1, ... 134 | if (it.name.isEmpty()) it.symbolType = SymbolNodeType.Unknown 135 | if (it.isUnknownFunction()) { 136 | it.storageClass = StorageClassType.Virtual 137 | it.addFlag(MemberTypeData.FLAG_PTR_CALL) 138 | it.name = "__unk_vfn_${counter}" 139 | it.symbol = "__unk_vfn_${counter}" 140 | it.valType.name = "void" 141 | it.valType.kind = VarSymbolKind.PrimitiveType 142 | } 143 | counter++ 144 | } 145 | } 146 | } 147 | 148 | private fun constructTypes() { 149 | val notIdentifiedTypes = mutableSetOf() 150 | logger.info { "Loading types..." } 151 | typeDataMap 152 | .filterNot { (k, _) -> GeneratorConfig.isExcluded(k) } 153 | .filterNot { (k, _) -> GeneratorConfig.isIgnored(k) } 154 | .forEach { (typeName, type) -> 155 | TypeManager.addType( 156 | typeName, 157 | when { 158 | isStruct(typeName) -> ClassType(typeName, type, isStruct = true) 159 | isClass(typeName) -> ClassType(typeName, type) 160 | isNameSpace(typeName, type) -> NamespaceType(typeName, type) 161 | else -> { 162 | notIdentifiedTypes.add(typeName) 163 | ClassType(typeName, type) 164 | } 165 | } 166 | ) 167 | } 168 | logger.debug { "Can not determine these types' type. Treat them as class type" } 169 | logger.debug { notIdentifiedTypes.joinToString() } 170 | } 171 | 172 | private fun loadIdentifiedTypes() { 173 | logger.info { "Loading identifier..." } 174 | val identifier = originData["identifier"]?.jsonObject 175 | classNameList = 176 | (identifier?.get("class")?.jsonArray).orEmpty().map { it.jsonPrimitive.content }.toMutableSet() 177 | structNameList = 178 | (identifier?.get("struct")?.jsonArray).orEmpty().map { it.jsonPrimitive.content }.toMutableSet() 179 | 180 | // check if any type is not identified but derived from other types 181 | typeDataMap.values 182 | .flatMap { it.collectReferencedTypes().toList() } 183 | .filter { it.first in originData["classes"]?.jsonObject?.keys.orEmpty() } 184 | .toMutableSet() 185 | .forEach { 186 | when (it.second) { 187 | BaseType.TypeKind.CLASS -> classNameList.add(it.first) 188 | BaseType.TypeKind.STRUCT -> structNameList.add(it.first) 189 | else -> {} 190 | } 191 | } 192 | } 193 | 194 | private fun isNameSpace(typeName: String, typeData: TypeData): Boolean { 195 | if (isStruct(typeName) || isClass(typeName)) 196 | return false 197 | if (TypeManager.classTypeNames.contains(typeName)) 198 | return false 199 | if (listOf( 200 | typeData.privateTypes, 201 | typeData.privateStaticTypes, 202 | typeData.protectedTypes, 203 | typeData.protectedStaticTypes, 204 | typeData.publicStaticTypes, 205 | typeData.virtual, 206 | typeData.virtualTableEntry 207 | ).any { it.isNotEmpty() } 208 | ) return false 209 | return typeData.publicTypes.none { it.isPtrCall() } 210 | } 211 | 212 | private fun isStruct(typeName: String) = structNameList.contains(typeName) 213 | 214 | 215 | private fun isClass(typeName: String) = classNameList.contains(typeName) 216 | 217 | } 218 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput 2 | 3 | import com.liteldev.headeroutput.ast.template.NodeType 4 | import com.liteldev.headeroutput.config.GeneratorConfig 5 | import com.liteldev.headeroutput.data.TypeData 6 | import com.liteldev.headeroutput.entity.* 7 | import com.liteldev.headeroutput.entity.BaseType.TypeKind 8 | import io.github.oshai.kotlinlogging.KotlinLogging 9 | 10 | object TypeManager { 11 | private val logger = KotlinLogging.logger { } 12 | private val typeMap = hashMapOf() 13 | 14 | val typeDataMap = hashMapOf() 15 | private val typeKinds by lazy { 16 | logger.info { "Collecting type kinds" } 17 | val map = hashMapOf() 18 | val typeDataSet = typeDataMap.values 19 | typeDataSet.forEach { 20 | map.putAll(it.collectReferencedTypes()) 21 | } 22 | map 23 | } 24 | val nestingMap = hashMapOf() 25 | val template = hashMapOf>() 26 | val classTypeNames by lazy { 27 | logger.info { "Collecting class type names" } 28 | val set = mutableSetOf() 29 | set.addAll(HeaderOutput.classNameList) 30 | set.addAll(HeaderOutput.structNameList) 31 | typeDataMap.values.map { it.parentTypes }.flatten().let(set::addAll) 32 | typeKinds.keys.let(set::addAll) 33 | set 34 | } 35 | 36 | fun addType(fullName: String, type: BaseType) { 37 | typeMap[fullName] = type 38 | } 39 | 40 | fun getType(fullName: String): BaseType? { 41 | return typeMap[fullName] 42 | } 43 | 44 | fun hasType(fullName: String): Boolean { 45 | return typeMap.containsKey(fullName) 46 | } 47 | 48 | fun getAllTypes(): List { 49 | return typeMap.values.toList() 50 | } 51 | 52 | fun constructNotExistedType() { 53 | logger.info { "Constructing not existed types" } 54 | typeKinds.forEach { (name, kind) -> 55 | if (!hasType(name)) { 56 | createDummyClass(name, kind) 57 | } 58 | } 59 | } 60 | 61 | fun initParents() { 62 | logger.info { "Initializing parents" } 63 | typeMap.values.filterIsInstance().forEach { type -> 64 | type.typeData.parentTypes.getOrNull(0)?.run { typeMap[this] } 65 | ?.let { type.parents.add(it); type.referenceTypes.add(it) } 66 | /* 67 | fixme: Fix in header generator: recursive parent 68 | type.typeData.parentTypes?.forEach { parent -> 69 | getType(parent)?.let { type.parents.add(it) } 70 | } 71 | */ 72 | } 73 | } 74 | 75 | fun initReferences() { 76 | logger.info { "Initializing references" } 77 | typeMap.values.forEach(BaseType::collectSelfReferencedType) 78 | } 79 | 80 | fun initInclusionList() { 81 | logger.info { "Initializing inclusion list" } 82 | typeMap.forEach { (_, type) -> 83 | type.initIncludeAndForwardDeclareList() 84 | } 85 | } 86 | 87 | fun initNestingMap() { 88 | logger.info { "Initializing nesting map" } 89 | typeMap.filter { !it.key.contains("::") } 90 | .forEach { (key, value) -> 91 | nestingMap[key] = value 92 | value.constructInnerTypeList() 93 | } 94 | // 收集所有形成嵌套关系的类,检查哪些类没有被收集到 95 | val allNestingType = nestingMap.values.flatMap { it.innerTypes }.toMutableSet() 96 | allNestingType.addAll(nestingMap.values) 97 | val allType = typeMap.values.toSet() 98 | val notNestingType = allType - allNestingType 99 | logger.debug { "These class has no nesting relationship" } 100 | logger.debug { notNestingType.joinToString { it.name } } 101 | // generate a dummy class for each not nesting class 102 | notNestingType.forEach { 103 | val parentName = it.name.substringBeforeLast("::") 104 | if (!hasType(parentName)) { 105 | createDummyClass(parentName) 106 | } 107 | val parentType = getType(parentName) ?: return@forEach 108 | parentType.innerTypes.add(it) 109 | it.outerType = parentType 110 | } 111 | } 112 | 113 | /** 114 | * @param name: the type's name to be created, must be an inner type 115 | */ 116 | private fun createDummyClass(name: String, type: TypeKind? = null): BaseType? { 117 | require(!hasType(name)) { "type $name already exists" } 118 | 119 | if (GeneratorConfig.isExcluded(name)) { 120 | return null 121 | } 122 | 123 | val dummyClass = if (type == null) { 124 | if (typeMap.any { (n, t) -> n.startsWith("$name::") && t.isNamespace() }) { 125 | NamespaceType(name, TypeData.empty()) 126 | } else if (!classTypeNames.contains(name) && classTypeNames.none { name.startsWith("$it::") }) { 127 | NamespaceType(name, TypeData.empty()) 128 | } else { 129 | ClassType(name, TypeData.empty(), template.contains(name)) 130 | } 131 | } else when (type) { 132 | TypeKind.CLASS -> ClassType(name, TypeData.empty(), isTemplateClass = template.contains(name)) 133 | TypeKind.STRUCT -> ClassType( 134 | name, 135 | TypeData.empty(), 136 | isStruct = true, 137 | isTemplateClass = template.contains(name) 138 | ) 139 | 140 | TypeKind.ENUM -> EnumType(name) 141 | TypeKind.UNION -> UnionType(name) 142 | TypeKind.NAMESPACE -> NamespaceType(name, TypeData.empty()) 143 | } 144 | 145 | if (name.contains("::")) { 146 | val parentName = name.substringBeforeLast("::") 147 | if (!hasType(parentName)) { 148 | createDummyClass(parentName) 149 | } 150 | 151 | val parentType = getType(parentName) ?: return null 152 | parentType.innerTypes.add(dummyClass) 153 | dummyClass.outerType = parentType 154 | } else { 155 | nestingMap[name] = dummyClass 156 | } 157 | 158 | addType(name, dummyClass) 159 | return dummyClass 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/ast/template/Nodes.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.ast.template 2 | 3 | import com.liteldev.headeroutput.TypeManager 4 | 5 | enum class NodeType { 6 | TYPE, 7 | BOOLEAN, 8 | INTEGER, 9 | FLOAT, 10 | VARIABLE, 11 | } 12 | 13 | interface Flattenable { 14 | fun flatten() 15 | } 16 | 17 | sealed class Node 18 | 19 | data class IntegerNode(val value: Int) : Node() 20 | 21 | data class FloatNode(val value: Float) : Node() 22 | 23 | data class BooleanNode(val value: Boolean) : Node() 24 | 25 | data class FunctionNode(val returnType: Node, val params: List, val parent: TypeNode? = null) : Node(), 26 | Flattenable { 27 | override fun flatten() { 28 | if (returnType is Flattenable) { 29 | returnType.flatten() 30 | } 31 | parent?.flatten() 32 | params.filterIsInstance().forEach { it.flatten() } 33 | } 34 | } 35 | 36 | data object VariableDummyNode : Node() 37 | 38 | sealed class Specifier 39 | 40 | data object ConstSpecifier : Specifier() 41 | 42 | data object VolatileSpecifier : Specifier() 43 | 44 | data object PointerSpecifier : Specifier() 45 | 46 | data object LReferenceSpecifier : Specifier() 47 | 48 | data object RReferenceSpecifier : Specifier() 49 | 50 | sealed class CallSpecifier : Specifier() 51 | 52 | data object CCDeclSpecifier : CallSpecifier() 53 | 54 | data object CCStdCallSpecifier : CallSpecifier() 55 | 56 | data class TypeNode( 57 | val name: String, 58 | val preSpecifiers: List = emptyList(), 59 | val postSpecifiers: List = emptyList(), 60 | val templateParams: List = emptyList(), 61 | ) : Node(), Flattenable { 62 | private fun mapToNodeTypes(): List { 63 | return templateParams.map { 64 | when (it) { 65 | is TypeNode -> NodeType.TYPE 66 | is IntegerNode -> NodeType.INTEGER 67 | is FloatNode -> NodeType.FLOAT 68 | is BooleanNode -> NodeType.BOOLEAN 69 | is FunctionNode -> NodeType.TYPE 70 | is VariableDummyNode -> NodeType.VARIABLE 71 | } 72 | } 73 | } 74 | 75 | override fun flatten() { 76 | if (templateParams.isEmpty()) { 77 | return 78 | } 79 | val existedList = TypeManager.template[name]?.toMutableList() 80 | if (existedList == null) { 81 | TypeManager.template[name] = mapToNodeTypes() 82 | } else { 83 | existedList.removeIf { it == NodeType.VARIABLE } 84 | val currentList = mapToNodeTypes() 85 | if (existedList != currentList) { 86 | if (existedList.size > currentList.size) { 87 | val newList: MutableList = arrayListOf() 88 | newList.addAll(currentList) 89 | if (currentList.isEmpty()) { 90 | newList.add(existedList[0]) 91 | } 92 | newList.add(NodeType.VARIABLE) 93 | TypeManager.template[name] = newList 94 | } else if (existedList.isEmpty() && currentList.isNotEmpty() && currentList[0] != NodeType.VARIABLE) { 95 | existedList.add(currentList[0]) 96 | existedList.add(NodeType.VARIABLE) 97 | TypeManager.template[name] = existedList 98 | } else if (existedList.size < currentList.size) { 99 | existedList.add(NodeType.VARIABLE) 100 | TypeManager.template[name] = existedList 101 | } 102 | } 103 | } 104 | templateParams.filterIsInstance().forEach { it.flatten() } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/ast/template/ParseException.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.ast.template 2 | 3 | class ParseException(message: String) : Exception(message) 4 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/ast/template/Parser.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.ast.template 2 | 3 | 4 | class Parser(private val template: String) { 5 | private var cursor = 0 6 | 7 | private fun postParseFunctionType(returnType: TypeNode, parent: TypeNode? = null): FunctionNode { 8 | val params = mutableListOf() 9 | if (!consume(")")) { 10 | params.add(parseTypeOrBooleanOrNumber()) 11 | while (consume(",")) { 12 | consumeBlank() 13 | params.add(parseTypeOrBooleanOrNumber()) 14 | } 15 | consumeBlank() 16 | if (!consume(")")) { 17 | expect(")") 18 | } 19 | } 20 | parseSpecifier() 21 | consumeBlank() 22 | return FunctionNode(returnType, params, parent) 23 | } 24 | 25 | private fun preParseFunctionType(returnType: TypeNode): Node { 26 | consumeBlank() 27 | if (!consume("(")) { 28 | return returnType 29 | } 30 | val currentPos = cursor 31 | consumeBlank() 32 | val parent = runCatching { parseType(false) }.onFailure { cursor = currentPos }.getOrNull() 33 | if (parent is TypeNode) { 34 | if (consume(")(")) { 35 | consumeBlank() 36 | return postParseFunctionType(returnType, parent) 37 | } else { 38 | cursor = currentPos 39 | } 40 | } 41 | consumeBlank() 42 | val specifier = parseSpecifier() 43 | consumeBlank() 44 | if (specifier.isNotEmpty()) { 45 | if (!consume(")(")) { 46 | cursor = currentPos 47 | } else { 48 | consumeBlank() 49 | return postParseFunctionType(returnType, TypeNode("*")) 50 | } 51 | } 52 | consumeBlank() 53 | return postParseFunctionType(returnType) 54 | } 55 | 56 | private fun parseType(requireType: Boolean = true): Node? { 57 | consumeBlank() 58 | val preSpecifiers = parseSpecifier() 59 | consumeBlank() 60 | if (consume("class") || consume("struct") || !requireType) { 61 | val name = parseTypeName(!requireType) ?: expect("type name") 62 | val children = mutableListOf() 63 | consumeBlank() 64 | if (consume("<")) { 65 | consumeBlank() 66 | if (!consume(">")) { 67 | children.add(parse() ?: expectTypeOrBooleanOrNumber()) 68 | while (consume(",")) { 69 | consumeBlank() 70 | children.add(parse() ?: expectTypeOrBooleanOrNumber()) 71 | consumeBlank() 72 | } 73 | if (!consume(">")) { 74 | expect(">") 75 | } 76 | } 77 | if (children.isEmpty()) { 78 | children.add(VariableDummyNode) 79 | } 80 | } 81 | val postSpecifiers = parseSpecifier() 82 | val returnType = TypeNode(name, preSpecifiers, postSpecifiers, children) 83 | return preParseFunctionType(returnType) 84 | } else if (consume("enum", true)) { 85 | val name = parseTypeName() ?: expect("type name") 86 | val postSpecifiers = parseSpecifier() 87 | val returnType = TypeNode(name, preSpecifiers, postSpecifiers) 88 | return preParseFunctionType(returnType) 89 | } else { 90 | var primitiveType = "" 91 | while (true) { 92 | consumeBlank() 93 | primitiveType += when { 94 | consume("unsigned", true) -> "unsigned " 95 | consume("signed", true) -> "signed " 96 | consume("long", true) -> "long " 97 | consume("short", true) -> "short " 98 | consume("int", true) -> "int " 99 | consume("char", true) -> "char " 100 | consume("float", true) -> "float " 101 | consume("double", true) -> "double " 102 | consume("bool", true) -> "bool " 103 | consume("__int64", true) -> "__int64 " 104 | consume("void", true) -> "void " 105 | consume("wchar_t", true) -> "wchar_t " 106 | else -> break 107 | } 108 | } 109 | primitiveType.trim() 110 | if (primitiveType.isEmpty()) { 111 | if (preSpecifiers.isNotEmpty()) { 112 | expect("primitive type") 113 | } 114 | return null 115 | } 116 | val postSpecifiers = parseSpecifier() 117 | val returnType = TypeNode(primitiveType, preSpecifiers, postSpecifiers) 118 | return preParseFunctionType(returnType) 119 | } 120 | } 121 | 122 | private fun parseTypeName(allowEndWithColon: Boolean = false): String? { 123 | consumeBlank() 124 | // first char can't be digit 125 | if (cursor < template.length && template[cursor].isDigit()) { 126 | return null 127 | } 128 | val sb = StringBuilder() 129 | while (cursor < template.length) { 130 | val char = template[cursor] 131 | if (char.isNormalLetter() || char == ':') { 132 | sb.append(char) 133 | cursor++ 134 | } else { 135 | break 136 | } 137 | } 138 | if (sb.isEmpty()) { 139 | return null 140 | } 141 | // : can't be the last char and can't be the first char 142 | if (!allowEndWithColon && (sb.last() == ':' || sb.first() == ':')) { 143 | return null 144 | } 145 | // 所有 : 都必须连续成对出现 146 | if (!checkColonPairs(sb.toString())) { 147 | return null 148 | } 149 | return sb.toString() 150 | } 151 | 152 | fun parseVariableName(): String? { 153 | consumeBlank() 154 | // first char can't be digit 155 | if (cursor < template.length && template[cursor].isDigit()) { 156 | return null 157 | } 158 | val sb = StringBuilder() 159 | while (cursor < template.length) { 160 | val char = template[cursor] 161 | if (char.isNormalLetter()) { 162 | sb.append(char) 163 | cursor++ 164 | } else { 165 | break 166 | } 167 | } 168 | if (sb.isEmpty()) { 169 | return null 170 | } 171 | return sb.toString() 172 | } 173 | 174 | private fun parseBoolean(): BooleanNode? { 175 | consumeBlank() 176 | return if (consume("true", true)) { 177 | BooleanNode(true) 178 | } else if (consume("false", true)) { 179 | BooleanNode(false) 180 | } else { 181 | null 182 | } 183 | } 184 | 185 | private fun parseNumber(): Node? { 186 | consumeBlank() 187 | val sb = StringBuilder() 188 | while (cursor < template.length) { 189 | val char = template[cursor] 190 | if (char.isDigit() || char == '.' || char == '-') { 191 | sb.append(char) 192 | cursor++ 193 | } else { 194 | break 195 | } 196 | } 197 | if (sb.isEmpty()) { 198 | return null 199 | } 200 | return runCatching { 201 | if (sb.contains(".")) { 202 | FloatNode(sb.toString().toFloat()) 203 | } else { 204 | IntegerNode(sb.toString().toInt()) 205 | } 206 | }.onFailure { TypeNode(sb.toString()) }.getOrThrow() 207 | } 208 | 209 | fun parseSpecifier(): List { 210 | val specifiers = mutableListOf() 211 | while (true) { 212 | when { 213 | consume("const", true) -> specifiers.add(ConstSpecifier) 214 | consume("volatile", true) -> specifiers.add(VolatileSpecifier) 215 | consume("*") -> specifiers.add(PointerSpecifier) 216 | consume("&&") -> specifiers.add(RReferenceSpecifier) 217 | consume("&") -> specifiers.add(LReferenceSpecifier) 218 | consume("__cdecl", true) -> specifiers.add(CCDeclSpecifier) 219 | consume("__stdcall", true) -> specifiers.add(CCStdCallSpecifier) 220 | else -> break 221 | } 222 | } 223 | return specifiers 224 | } 225 | 226 | fun parse(): Node? { 227 | return parseType() ?: parseBoolean() ?: parseNumber() 228 | } 229 | 230 | private fun consume(str: String, nextIsNotLetter: Boolean = false): Boolean { 231 | consumeBlank() 232 | if (template.substring(cursor).startsWith(str)) { 233 | cursor += str.length 234 | if (nextIsNotLetter) { 235 | if (cursor < template.length && template[cursor].isLetterOrDigit()) { 236 | cursor -= str.length 237 | return false 238 | } 239 | } 240 | return true 241 | } 242 | return false 243 | } 244 | 245 | private fun consumeBlank() { 246 | while (cursor < template.length) { 247 | if (template[cursor].isWhitespace()) { 248 | cursor++ 249 | } else { 250 | break 251 | } 252 | } 253 | } 254 | 255 | private fun Char.isNormalLetter(): Boolean { 256 | // true for unicode can be used in variable name 257 | return this !in "<>,\n\t\r?*&:;{}()[]=+-/\"'`~!@#$%^|.\\ " 258 | } 259 | 260 | private fun checkColonPairs(input: String): Boolean { 261 | var colonCount = 0 262 | for (char in input) { 263 | if (char == ':') { 264 | colonCount++ 265 | } else { 266 | if (colonCount % 2 != 0) return false 267 | colonCount = 0 268 | } 269 | } 270 | return colonCount % 2 == 0 271 | } 272 | 273 | private fun parseTypeOrBooleanOrNumber(): Node { 274 | return parseType() ?: parseBoolean() ?: parseNumber() ?: expectTypeOrBooleanOrNumber() 275 | } 276 | 277 | private fun printError(error: String): Nothing = 278 | throw ParseException("Parse error: $error\n$template\n${" ".repeat(cursor)}^") 279 | 280 | private fun expectTypeOrBooleanOrNumber(): Nothing = 281 | printError("expect type or boolean or number") 282 | 283 | private fun expect(str: String): Nothing = 284 | printError("expect $str") 285 | } 286 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.config 2 | 3 | import cc.ekblad.toml.decode 4 | import cc.ekblad.toml.tomlMapper 5 | import io.github.oshai.kotlinlogging.KotlinLogging 6 | import kotlinx.serialization.ExperimentalSerializationApi 7 | import kotlinx.serialization.decodeFromString 8 | import kotlinx.serialization.json.Json 9 | import java.io.File 10 | import kotlin.system.exitProcess 11 | 12 | object GeneratorConfig { 13 | private val logger = KotlinLogging.logger { } 14 | 15 | @OptIn(ExperimentalSerializationApi::class) 16 | private val json = Json { explicitNulls = false } 17 | 18 | var enableRelativePath = false 19 | var alwaysTrue: Boolean = false 20 | lateinit var rootPath: String 21 | lateinit var jsonPath: String 22 | lateinit var generatePath: String 23 | lateinit var configPath: String 24 | lateinit var declareMapPath: String 25 | lateinit var predefineHeaderPath: String 26 | lateinit var replacementRegex: List> 27 | 28 | private lateinit var excludeRegexList: List 29 | private lateinit var ignoreRegexList: List 30 | private lateinit var generatorConfigData: OutputConfig 31 | 32 | 33 | fun loadConfig() { 34 | logger.info { "Loading config..." } 35 | val configText = File(configPath).readText() 36 | generatorConfigData = try { 37 | tomlMapper { }.decode(configText) 38 | } catch (e: Exception) { 39 | try { 40 | json.decodeFromString(configText) 41 | } catch (e: Exception) { 42 | logger.error { "Invalid config file" } 43 | exitProcess(1) 44 | } 45 | } 46 | enableRelativePath = generatorConfigData.config.enableRelativePath 47 | rootPath = generatorConfigData.config.rootPath 48 | excludeRegexList = generatorConfigData.exclusion.regex.map { it.toRegex() } 49 | ignoreRegexList = generatorConfigData.ignore.regex.map { it.toRegex() } 50 | replacementRegex = generatorConfigData.replacement.regex.map { it.regex.toRegex() to it.to } 51 | } 52 | 53 | // Exclude means exclude from the origin data, and do not generate the dummy type 54 | fun isExcluded(name: String): Boolean { 55 | return excludeRegexList.any { name.matches(it) } 56 | } 57 | 58 | // Ignore means ignore from the origin data, and generate the dummy type 59 | fun isIgnored(name: String): Boolean { 60 | return ignoreRegexList.any { name.matches(it) } 61 | } 62 | 63 | fun getSortRules() = generatorConfigData.sort 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/config/OutputConfig.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.config 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class OutputConfig( 7 | val config: Config, 8 | val exclusion: Exclusion, 9 | val sort: Sort, 10 | val ignore: Ignore, 11 | val replacement: Replacement 12 | ) { 13 | @Serializable 14 | data class Config(val rootPath: String, val enableRelativePath: Boolean) 15 | 16 | @Serializable 17 | data class Sort( 18 | val parent: List, 19 | val namespace: List, 20 | val regex: List 21 | ) { 22 | @Serializable 23 | data class Parent( 24 | val parent: String, 25 | val dst: String 26 | ) 27 | 28 | @Serializable 29 | data class Namespace( 30 | val namespace: String, 31 | val dst: String 32 | ) 33 | 34 | @Serializable 35 | data class Regex( 36 | val regex: String, 37 | val dst: String, 38 | val override: Boolean = false 39 | ) 40 | } 41 | 42 | @Serializable 43 | data class Exclusion( 44 | val regex: List 45 | ) 46 | 47 | @Serializable 48 | data class Ignore( 49 | val regex: List 50 | ) 51 | 52 | @Serializable 53 | data class Replacement( 54 | val regex: List 55 | ) { 56 | @Serializable 57 | data class Regex( 58 | val regex: String, 59 | val to: String 60 | ) 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/data/EnumType.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.data 2 | 3 | import kotlinx.serialization.KSerializer 4 | import kotlinx.serialization.Serializable 5 | import kotlinx.serialization.descriptors.PrimitiveKind 6 | import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor 7 | import kotlinx.serialization.descriptors.SerialDescriptor 8 | import kotlinx.serialization.encoding.Decoder 9 | import kotlinx.serialization.encoding.Encoder 10 | import kotlin.reflect.KClass 11 | 12 | @Suppress("unused") 13 | @Serializable(with = SymbolNodeType.Companion::class) 14 | enum class SymbolNodeType(override val value: Int) : IntEnumBase { 15 | Normal(0), 16 | Constructor(1), 17 | Destructor(2), 18 | Operator(3), 19 | StaticVar(4), 20 | Unknown(5); 21 | 22 | companion object : IntEnumSerializer(SymbolNodeType::class.simpleName!!, SymbolNodeType::class) 23 | } 24 | 25 | @Suppress("unused") 26 | @Serializable(with = AccessType.Companion::class) 27 | enum class AccessType(override val value: Int) : IntEnumBase { 28 | Public(0), 29 | Protected(1), 30 | Private(2), 31 | None(3); 32 | 33 | companion object : IntEnumSerializer(AccessType::class.simpleName!!, AccessType::class) 34 | } 35 | 36 | /** StorageClassType 表示一个Symbol的类属性 37 | * 以下为其可能的取值 38 | * 39 | * static: 静态成员,全局实例,无实例绑定 40 | * 41 | * virtual: 虚拟对象,由虚表访问 42 | * 43 | * none: 标准成员,全局实例,绑定到实例的访问 44 | * */ 45 | @Suppress("unused") 46 | @Serializable(with = StorageClassType.Companion::class) 47 | enum class StorageClassType(override val value: Int) : IntEnumBase { 48 | Static(0), 49 | Virtual(1), 50 | None(2); 51 | 52 | companion object : 53 | IntEnumSerializer(StorageClassType::class.simpleName!!, StorageClassType::class) 54 | } 55 | 56 | @Suppress("unused") 57 | @Serializable(with = VarSymbolKind.Companion::class) 58 | enum class VarSymbolKind(override val value: Int) : IntEnumBase { 59 | Unknown(0), 60 | Md5Symbol(1), 61 | PrimitiveType(2), 62 | FunctionSignature(3), 63 | Identifier(4), 64 | NamedIdentifier(5), 65 | VcallThunkIdentifier(6), 66 | LocalStaticGuardIdentifier(7), 67 | IntrinsicFunctionIdentifier(8), 68 | ConversionOperatorIdentifier(9), 69 | DynamicStructorIdentifier(10), 70 | StructorIdentifier(11), 71 | LiteralOperatorIdentifier(12), 72 | ThunkSignature(13), 73 | PointerType(14), 74 | TagType(15), 75 | ArrayType(16), 76 | Custom(17), 77 | IntrinsicType(18), 78 | NodeArray(19), 79 | QualifiedName(20), 80 | TemplateParameterReference(21), 81 | EncodedStringLiteral(22), 82 | IntegerLiteral(23), 83 | RttiBaseClassDescriptor(24), 84 | LocalStaticGuardVariable(25), 85 | FunctionSymbol(26), 86 | VariableSymbol(27), 87 | SpecialTableSymbol(28); 88 | 89 | companion object : IntEnumSerializer(VarSymbolKind::class.simpleName!!, VarSymbolKind::class) 90 | } 91 | 92 | interface IntEnumBase { 93 | val value: Int 94 | } 95 | 96 | open class IntEnumSerializer(serialName: String, private val clazz: KClass) : 97 | KSerializer where E : Enum, E : IntEnumBase { 98 | 99 | override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(serialName, PrimitiveKind.INT) 100 | 101 | override fun serialize(encoder: Encoder, value: E) { 102 | encoder.encodeInt(value.value) 103 | } 104 | 105 | override fun deserialize(decoder: Decoder): E { 106 | val decodeInt = decoder.decodeInt() 107 | return clazz.java.enumConstants.firstOrNull { it.value == decodeInt } 108 | ?: throw IllegalArgumentException("No enum constant found for value $decodeInt") 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/data/MemberTypeData.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.data 2 | 3 | import com.liteldev.headeroutput.TypeManager 4 | import com.liteldev.headeroutput.appendSpace 5 | import com.liteldev.headeroutput.config.GeneratorConfig 6 | import com.liteldev.headeroutput.entity.BaseType 7 | import kotlinx.serialization.SerialName 8 | import kotlinx.serialization.Serializable 9 | 10 | @Suppress("unused") 11 | @Serializable 12 | data class MemberTypeData( 13 | @SerialName("storage_class") var storageClass: StorageClassType, 14 | @SerialName("access_type") val accessType: AccessType, // public 15 | @SerialName("symbol_type") var symbolType: SymbolNodeType, 16 | @SerialName("type") val valType: VariableTypeData, 17 | @SerialName("namespace") val namespace: String, // Mob 18 | @SerialName("name") var name: String, // hasComponent 19 | @SerialName("params") val params: List = emptyList(), 20 | @SerialName("params_name") val paramsName: List = emptyList(), 21 | @SerialName("flag_bits") var flags: Int, // 1 22 | @SerialName("rva") val rva: Long, // 13417264 23 | @SerialName("symbol") var symbol: String, // ?hasComponent@Mob@@UEBA_NAEBVHashedString@@@Z 24 | @SerialName("fake_symbol") val fakeSymbol: String?, // ?hasComponent@Mob@@UEBA_NAEBVHashedString@@@Z 25 | ) { 26 | val type: BaseType by lazy { 27 | TypeManager.getType(namespace)!! 28 | } 29 | 30 | fun genFuncString( 31 | namespace: Boolean = false, 32 | useFakeSymbol: Boolean = false, 33 | vIndex: Int? = null 34 | ): String { 35 | fun StringBuilder.appendIndented(str: String) = appendSpace(START_BLANK_SPACE).append(str).append("\n") 36 | var origin = buildString { 37 | val isFunctionPtr = valType.name.contains("*)(") && valType.name.endsWith(")") 38 | val infos = mutableListOf() 39 | if (vIndex != null) appendIndented("// vIndex: $vIndex") 40 | appendSpace(START_BLANK_SPACE) 41 | if (isStaticGlobalVariable()) { 42 | append("MCAPI ") 43 | if (namespace) append("extern ") else append("static ") 44 | if (valType.name.isBlank()) valType.name = "auto" 45 | else valType.name = valType.name.replaceEnumType() 46 | append("${valType.name} $name;") 47 | } else { 48 | if (isOperator() && (name.startsWith("operator ") || name == "operator ${valType.name}")) 49 | valType.name = "" 50 | else if (valType.name.isBlank() && !isConstructor() && !isDestructor()) valType.name = "auto" 51 | else valType.name = valType.name.replaceEnumType() 52 | 53 | val paramNameBeginIndex = if (isPtrCall()) 1 else 0 54 | val paramsString = 55 | if (paramsName.isNotEmpty() && params.size + paramNameBeginIndex == paramsName.size) { 56 | var i = paramNameBeginIndex 57 | params.joinToString(", ") { 58 | if (it.name.contains("*)(") && (it.name.endsWith(")") || it.name.endsWith(") const"))) 59 | it.name.replace("*)(", "*${paramsName[i++]})(") 60 | else if (it.name.contains("&)(") && it.name.endsWith(")") || it.name.endsWith(") const")) 61 | it.name.replace("&)(", "&${paramsName[i++]})(") 62 | else "${it.name} ${paramsName[i++]}" 63 | } 64 | } else { 65 | params.joinToString(", ") { it.name } 66 | }.replaceEnumType() 67 | 68 | // Linkage specifiers 69 | if (isVirtual()) 70 | if (useFakeSymbol) append("MCVAPI ") 71 | else append("virtual ") 72 | else append("MCAPI ") 73 | 74 | if (!(isPtrCall() || isVirtual() || namespace)) append("static ") 75 | if ((isConstructor() && !isCopyConstructor() && !isMoveConstructor() && params.size == 1) || isConversionOperator()) 76 | append("explicit ") 77 | if (isFunctionPtr) append("auto ") 78 | else if (valType.name != "") append("${valType.name} ") 79 | append("$name(${paramsString})") 80 | 81 | if (isConst()) append(" const") 82 | if (isFunctionPtr) append(" -> ${valType.name}") 83 | if (isPureCall()) 84 | append(" = 0") 85 | else if (isVirtual() && !useFakeSymbol && symbol.startsWith("__gen_")) 86 | append(" = default") 87 | append(";") 88 | } 89 | } 90 | GeneratorConfig.replacementRegex.forEach { origin = origin.replace(it.first, it.second) } 91 | return origin 92 | } 93 | 94 | fun isConstructor() = symbolType == SymbolNodeType.Constructor 95 | 96 | fun isDestructor() = symbolType == SymbolNodeType.Destructor 97 | 98 | fun isOperator() = symbolType == SymbolNodeType.Operator 99 | 100 | fun isUnknownFunction() = symbolType == SymbolNodeType.Unknown 101 | 102 | fun isStaticGlobalVariable() = symbolType == SymbolNodeType.StaticVar 103 | 104 | fun isConst() = hasFlag(FLAG_CONST) 105 | 106 | fun isPtrCall() = hasFlag(FLAG_PTR_CALL) 107 | 108 | fun isPureCall() = hasFlag(FLAG_PURE_CALL) 109 | 110 | fun isConversionOperator() = isOperator() && name.startsWith("operator ") 111 | 112 | fun isCopyOperator() = isOperator() && params.run { 113 | size == 1 && (this[0].name == "${type.type} ${type.name} const &") 114 | } 115 | 116 | fun isDefaultConstructor() = isConstructor() && params.isEmpty() 117 | 118 | fun isCopyConstructor() = isConstructor() && params.run { 119 | size == 1 && (this[0].name == "${type.type} ${type.name} const &") 120 | } 121 | 122 | fun isMoveConstructor() = isConstructor() && params.run { 123 | size == 1 && (this[0].name == "${type.type} ${type.name} &&") 124 | } 125 | 126 | fun addFlag(flag: Int) { 127 | if (flags and flag != flag) flags += flag 128 | } 129 | 130 | fun removeFlag(flag: Int) { 131 | if (flags and flag == flag) flags -= flag 132 | } 133 | 134 | fun hasFlag(flag: Int) = flags and flag == flag 135 | 136 | fun isVirtual() = storageClass == StorageClassType.Virtual 137 | 138 | fun compare(other: MemberTypeData): Int { 139 | if (this.name != other.name) return this.name.compareTo(other.name) 140 | if (this.valType.name != other.valType.name) return this.valType.name.compareTo(other.valType.name) 141 | if (this.params.size != other.params.size) return this.params.size - other.params.size 142 | this.params.forEachIndexed { index, variableTypeData -> 143 | if (variableTypeData.name != other.params[index].name) return variableTypeData.name.compareTo(other.params[index].name) 144 | } 145 | return 0 146 | } 147 | 148 | companion object { 149 | //[0] const 150 | //[1] __ptr64 spec 151 | //[2] isPureCall 152 | const val FLAG_CONST = 1 shl 0 153 | const val FLAG_PTR_CALL = 1 shl 1 154 | const val FLAG_PURE_CALL = 1 shl 2 155 | 156 | const val START_BLANK_SPACE = 4 157 | 158 | private val typeMatchRegex = Regex("enum\\s+([a-zA-Z0-9_]+(?:::[a-zA-Z0-9_]+)*)") 159 | fun String.replaceEnumType(): String { 160 | var result = this 161 | typeMatchRegex.findAll(this).forEach { matchResult -> 162 | val name = matchResult.groupValues[1] 163 | result = if (TypeManager.hasType(name)) result.replace("enum $name", "::$name") 164 | else result.replace("enum $name", "enum class $name") 165 | } 166 | return result 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/data/TypeData.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.data 2 | 3 | import com.liteldev.headeroutput.ast.template.Flattenable 4 | import com.liteldev.headeroutput.ast.template.Parser 5 | import com.liteldev.headeroutput.entity.BaseType 6 | import kotlinx.serialization.SerialName 7 | import kotlinx.serialization.Serializable 8 | import java.util.* 9 | 10 | @Serializable 11 | data class TypeData( 12 | @SerialName("child_types") val childTypes: List = emptyList(), 13 | @SerialName("parent_types") val parentTypes: List = emptyList(), 14 | @SerialName("private") val privateTypes: List = emptyList(), 15 | @SerialName("private.static") val privateStaticTypes: List = emptyList(), 16 | @SerialName("protected") val protectedTypes: List = emptyList(), 17 | @SerialName("protected.static") val protectedStaticTypes: List = emptyList(), 18 | @SerialName("public") val publicTypes: List = emptyList(), 19 | @SerialName("public.static") val publicStaticTypes: List = emptyList(), 20 | val virtual: List = emptyList(), 21 | @SerialName("virtual.unordered") val virtualUnordered: MutableList = mutableListOf(), 22 | @SerialName("vtbl_entry") val virtualTableEntry: List = emptyList() 23 | ) { 24 | 25 | private fun collectAllFunction() = listOf( 26 | privateTypes, 27 | privateStaticTypes, 28 | protectedTypes, 29 | protectedStaticTypes, 30 | publicTypes, 31 | publicStaticTypes, 32 | virtual, 33 | virtualUnordered, 34 | ).flatten() 35 | 36 | fun collectInstanceFunction() = listOfNotNull( 37 | privateTypes, 38 | protectedTypes, 39 | publicTypes, 40 | virtual, 41 | virtualUnordered, 42 | ).flatten() 43 | 44 | private fun matchTypes(name: String) = typeMatchRegex.findAll(name) 45 | .map { it.groupValues[2] to BaseType.TypeKind.valueOf(it.groupValues[1].uppercase(Locale.getDefault())) } 46 | .onEach { (typeName, _) -> 47 | if (name.contains("$typeName<")) { 48 | runCatching { (Parser(name).parse() as? Flattenable)?.flatten() } 49 | } 50 | } // detects template class 51 | 52 | 53 | fun collectReferencedTypes(): Map { 54 | return collectAllFunction().flatMap { memberType -> 55 | memberType.params.map { it.name } + memberType.valType.name 56 | }.flatMap(::matchTypes).toMap() 57 | } 58 | 59 | companion object { 60 | val typeMatchRegex = Regex("(struct|class|enum|union)\\s+([a-zA-Z0-9_]+(?:::[a-zA-Z0-9_]+)*)") 61 | 62 | fun empty(): TypeData { 63 | return TypeData() 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/data/VariableTypeData.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.data 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class VariableTypeData( 7 | var name: String, 8 | var kind: VarSymbolKind, 9 | ) { 10 | override fun equals(other: Any?): Boolean { 11 | if (this === other) return true 12 | if (javaClass != other?.javaClass) return false 13 | 14 | other as VariableTypeData 15 | 16 | if (name != other.name) return false 17 | return kind == other.kind 18 | } 19 | 20 | override fun hashCode(): Int { 21 | var result = name.hashCode() 22 | result = 31 * result + kind.value 23 | return result 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.entity 2 | 3 | import com.liteldev.headeroutput.* 4 | import com.liteldev.headeroutput.HeaderGenerator.HEADER_SUFFIX 5 | import com.liteldev.headeroutput.config.GeneratorConfig 6 | import com.liteldev.headeroutput.data.TypeData 7 | import io.github.oshai.kotlinlogging.KotlinLogging 8 | import kotlinx.serialization.decodeFromString 9 | import kotlinx.serialization.json.Json 10 | import java.io.File 11 | import java.util.* 12 | 13 | abstract class BaseType( 14 | var name: String, 15 | type: TypeKind, 16 | var typeData: TypeData, 17 | ) { 18 | var type: TypeKind = type 19 | protected set 20 | 21 | var outerType: BaseType? = null 22 | 23 | val innerTypes: MutableSet = mutableSetOf() 24 | val referenceTypes: MutableSet = mutableSetOf() 25 | val includeList: MutableSet = mutableSetOf() 26 | val forwardDeclareList: MutableSet = mutableSetOf() 27 | 28 | val simpleName = name.substringAfterLast("::") 29 | val namespace = name.substringBeforeLast("::", "") + "::" 30 | val fullEscapeName = name.replace("::", "_") 31 | val fullUpperEscapeName = fullEscapeName.uppercase(Locale.getDefault()) 32 | 33 | // only can use after all types are constructed 34 | val allReferences: Set by lazy { 35 | referenceTypes + innerTypes.flatMap { it.allReferences } 36 | } 37 | 38 | // only can use after all types are constructed 39 | val allInnerTypes: Set by lazy { 40 | innerTypes + innerTypes.flatMap { it.allInnerTypes } 41 | } 42 | 43 | // should be initialized after nested types are constructed and dummy types are created 44 | val path: String by lazy { 45 | val root = GeneratorConfig.rootPath 46 | getTopLevelFileType().run { 47 | val regexRules = GeneratorConfig.getSortRules().regex 48 | regexRules.filter { it.override }.find { this.name.matches(it.regex.toRegex()) }?.let { 49 | return@run "$root/${it.dst}/${this.simpleName}.$HEADER_SUFFIX" 50 | } 51 | if (declareMap.containsKey(this.name)) { 52 | return@run "$root/${declareMap[this.name]!!.toSnakeCase()}/${this.simpleName}.$HEADER_SUFFIX" 53 | } 54 | if (this is ClassType) { 55 | val parentRules = GeneratorConfig.getSortRules().parent 56 | parentRules.find { this.typeData.parentTypes.contains(it.parent) || this.name == it.parent } 57 | ?.let { 58 | return@run "$root/${it.dst}/${this.simpleName}.$HEADER_SUFFIX" 59 | } 60 | } 61 | if (this is NamespaceType) { 62 | val namespaceRules = GeneratorConfig.getSortRules().namespace 63 | namespaceRules.find { this.name == it.namespace || this.name.startsWith(it.namespace + "::") } 64 | ?.let { 65 | return@run "$root/${it.dst}/${this.simpleName}.$HEADER_SUFFIX" 66 | } 67 | } else { 68 | val namespaceRules = GeneratorConfig.getSortRules().namespace 69 | namespaceRules.find { this.namespace.startsWith(it.namespace + "::") } 70 | ?.let { 71 | return@run "$root/${it.dst}/${this.simpleName}.$HEADER_SUFFIX" 72 | } 73 | } 74 | regexRules.filter { !it.override }.find { this.name.matches(it.regex.toRegex()) }?.let { 75 | return@run "$root/${it.dst}/${this.simpleName}.$HEADER_SUFFIX" 76 | } 77 | notSortedTypes.add(this.name) 78 | return@run "$root/${name.replace("::", "__")}.$HEADER_SUFFIX" 79 | } 80 | } 81 | 82 | abstract fun generateTypeDefine(): String 83 | 84 | open fun generateTypeDeclare(): String { 85 | error("Type $name can not generate declare, current type: ${this.javaClass.simpleName}") 86 | } 87 | 88 | open fun initIncludeAndForwardDeclareList() {} 89 | 90 | fun constructInnerTypeList(outerType: BaseType? = null) { 91 | this.outerType = outerType 92 | innerTypes.addAll( 93 | TypeManager.getAllTypes().filter { 94 | it.name.startsWith(this.name + "::") && !it.name.substring(this.name.length + 2).contains("::") 95 | }.onEach { 96 | it.constructInnerTypeList(this) 97 | } 98 | ) 99 | } 100 | 101 | fun collectSelfReferencedType() { 102 | typeData.collectReferencedTypes().forEach { (name, _) -> 103 | TypeManager.getType(name)?.let(referenceTypes::add) 104 | } 105 | } 106 | 107 | enum class TypeKind { 108 | CLASS, STRUCT, ENUM, UNION, NAMESPACE; 109 | 110 | override fun toString(): String { 111 | return when (this) { 112 | CLASS -> "class" 113 | STRUCT -> "struct" 114 | ENUM -> "enum" 115 | UNION -> "union" 116 | NAMESPACE -> "namespace" 117 | } 118 | } 119 | } 120 | 121 | companion object { 122 | private val logger = KotlinLogging.logger { } 123 | val declareMap by lazy { 124 | runCatching { 125 | File(GeneratorConfig.declareMapPath).readText().let { Json.decodeFromString>(it) } 126 | }.onFailure { 127 | logger.error { "Failed to load declare map, types will be sorted all by rules" } 128 | }.getOrNull() ?: emptyMap() 129 | } 130 | val notSortedTypes = hashSetOf() 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.entity 2 | 3 | import com.liteldev.headeroutput.* 4 | import com.liteldev.headeroutput.ast.template.NodeType 5 | import com.liteldev.headeroutput.data.MemberTypeData 6 | import com.liteldev.headeroutput.data.TypeData 7 | 8 | open class ClassType( 9 | name: String, typeData: TypeData, val isTemplateClass: Boolean = false, isStruct: Boolean = false, 10 | ) : BaseType(name, TypeKind.CLASS, typeData) { 11 | 12 | private val publicFunctions = arrayListOf() 13 | private val publicVariables = arrayListOf() 14 | private val protectedFunctions = arrayListOf() 15 | private val protectedVariables = arrayListOf() 16 | private val privateFunctions = arrayListOf() 17 | private val privateVariables = arrayListOf() 18 | 19 | val parents = arrayListOf() 20 | 21 | // fixme: Fix in header generator 22 | init { 23 | if (isStruct) { 24 | this.type = TypeKind.STRUCT 25 | } 26 | val ordered = typeData.virtual.map { it.symbol }.toMutableList() 27 | typeData.virtualUnordered.removeIf { unordered -> 28 | unordered.symbol in ordered 29 | } 30 | fun determineType( 31 | members: List, 32 | funList: MutableList, 33 | varList: MutableList 34 | ) { 35 | members.forEach { member -> 36 | if (member.isStaticGlobalVariable()) { 37 | varList.add(member) 38 | } else { 39 | funList.add(member) 40 | } 41 | } 42 | funList.sortWith { o1, o2 -> o1.compare(o2) } 43 | varList.sortWith { o1, o2 -> o1.compare(o2) } 44 | } 45 | determineType(typeData.publicTypes, publicFunctions, publicVariables) 46 | determineType(typeData.publicStaticTypes, publicFunctions, publicVariables) 47 | determineType(typeData.protectedTypes, protectedFunctions, protectedVariables) 48 | determineType(typeData.protectedStaticTypes, protectedFunctions, protectedVariables) 49 | determineType(typeData.privateTypes, privateFunctions, privateVariables) 50 | determineType(typeData.privateStaticTypes, privateFunctions, privateVariables) 51 | } 52 | 53 | private fun generateInnerTypeDefine(): String { 54 | if (innerTypes.isEmpty()) return "" 55 | val generateOrder = innerTypes.toMutableList() 56 | generateOrder.sortWith(Comparator { o1, o2 -> 57 | if (o1.isEnum() && o2.isEnum()) return@Comparator o1.name.compareTo(o2.name) 58 | if (o1.isEnum() || o2.isEnum()) return@Comparator if (o1 is EnumType) -1 else 1 59 | if (o1.isUnion() && o2.isUnion()) return@Comparator o1.name.compareTo(o2.name) 60 | if (o1.isUnion() || o2.isUnion()) return@Comparator if (o1 is EnumType) -1 else 1 61 | if (o1 in o2.allReferences || o1.allInnerTypes.any { it in o2.allReferences }) return@Comparator -1 62 | if (o2 in o1.allReferences || o2.allInnerTypes.any { it in o1.allReferences }) return@Comparator 1 63 | return@Comparator o1.name.compareTo(o2.name) 64 | }) 65 | return buildString { 66 | val inners = generateOrder.filterIsInstance() 67 | if (inners.isNotEmpty()) { 68 | appendLine("// $simpleName inner types declare") 69 | appendLine("// clang-format off") 70 | appendLine(inners.joinToString(separator = "\n") { it.generateTypeDeclare() }) 71 | appendLine("// clang-format on\n") 72 | } 73 | appendLine("// $simpleName inner types define") 74 | append(generateOrder.joinToString(separator = "\n") { it.generateTypeDefine() }) 75 | } 76 | } 77 | 78 | override fun generateTypeDefine(): String = buildString { 79 | val classType = if (type == TypeKind.STRUCT) "struct" else "class" 80 | getTemplateDefine()?.let(this::appendLine) 81 | appendLine("$classType $simpleName ${genParents()}{") 82 | if (innerTypes.isNotEmpty()) { 83 | appendLine("public:") 84 | val def = generateInnerTypeDefine() 85 | if (def.isNotBlank()) { 86 | append(" ") 87 | append(def.replace("\n", "\n ")) 88 | } 89 | appendLine() 90 | } 91 | append(generateAntiReconstruction()) 92 | append(generatePublic()) 93 | append(generateProtected()) 94 | append(generatePrivate()) 95 | append(generateProtected(genFunc = false)) 96 | append(generatePrivate(genFunc = false)) 97 | // append(generateMemberAccessor()) 98 | appendLine("};") 99 | } 100 | 101 | /** 102 | * generate type declare 103 | * when type's outer type is null or class, just return the type's declaration because nested type declare forward is not allowed in c++ 104 | * when type's outer type is namespace, return namespace declaration + type's declaration 105 | */ 106 | override fun generateTypeDeclare(): String { 107 | var baseDeclaration = when (type) { 108 | TypeKind.CLASS -> "class $simpleName;" 109 | TypeKind.STRUCT -> "struct $simpleName;" 110 | else -> error("not support type $type") 111 | } 112 | getTemplateDefine()?.let { baseDeclaration = "$it $baseDeclaration" } 113 | if (outerType == null || outerType is ClassType) { 114 | return baseDeclaration 115 | } 116 | if (outerType?.type == TypeKind.NAMESPACE) { 117 | return "namespace ${outerType?.name} { $baseDeclaration }" 118 | } 119 | error("unreachable") 120 | } 121 | 122 | override fun initIncludeAndForwardDeclareList() { 123 | // not include self, inner type, and types can forward declare 124 | val declareRequiredTypes = allReferences.filter { 125 | val notInSameFile = it.getTopLevelFileType() != this.getTopLevelFileType() 126 | val canNotForwardDeclareSimply = outerType?.isNamespace() == true 127 | || it.name.contains("::") 128 | || ((it as? ClassType)?.isTemplateClass == true) 129 | || it.isEnum() 130 | notInSameFile && canNotForwardDeclareSimply 131 | } 132 | declareRequiredTypes.forEach { 133 | if (it.isEnum() || it.outerType is ClassType || (it as? ClassType)?.isTemplateClass == true) { 134 | this.path.relativePathTo(it.path).let(includeList::add) 135 | } else { 136 | it.generateTypeDeclare().let(forwardDeclareList::add) 137 | } 138 | } 139 | val parents = collectParents() 140 | if (parents.isNotEmpty()) { 141 | includeList.addAll(parents.map { this.path.relativePathTo(it.path) }) 142 | } 143 | includeList.remove(this.path.relativePathTo(this.path)) 144 | includeList.remove("") 145 | } 146 | 147 | private fun genParents(): String { 148 | if (parents.isEmpty()) { 149 | return "" 150 | } 151 | val sb = StringBuilder(": ") 152 | parents.joinToString { "public ::${it.name}" }.let(sb::append) 153 | sb.append(" ") 154 | return sb.toString() 155 | } 156 | 157 | private fun generateAntiReconstruction(): String { 158 | val public = typeData.collectInstanceFunction() 159 | .filter { it.isConstructor() || (it.isOperator() && it.name == "operator=") } 160 | val genOperator = public.none { it.isCopyOperator() } 161 | val genEmptyParamConstructor = public.none { it.isDefaultConstructor() } 162 | val genCopyConstructor = public.none { it.isCopyConstructor() } 163 | return if (!genOperator && !genEmptyParamConstructor && !genCopyConstructor) { 164 | "" 165 | } else StringBuilder( 166 | "public:\n // prevent constructor by default\n" 167 | ).apply { 168 | if (genOperator) { 169 | appendLine(" $simpleName& operator=($simpleName const &);") 170 | } 171 | if (genCopyConstructor) { 172 | appendLine(" $simpleName($simpleName const &);") 173 | } 174 | if (genEmptyParamConstructor) { 175 | appendLine(" $simpleName();") 176 | } 177 | appendLine() 178 | }.toString() 179 | } 180 | 181 | private fun generatePublic() = buildString { 182 | if (typeData.publicTypes.isEmpty() && typeData.publicStaticTypes.isEmpty() && typeData.virtual.isEmpty() && typeData.virtualUnordered.isEmpty() 183 | ) { 184 | return "" 185 | } 186 | appendLine("public:") 187 | appendLine(" // NOLINTBEGIN") 188 | var counter = 0 189 | val virtual = typeData.virtual.map { 190 | counter++ to it 191 | }.filter { (_, it) -> it.namespace.isEmpty() || it.namespace == name } 192 | if (virtual.isNotEmpty()) 193 | virtual.joinToString("\n\n", postfix = "\n\n") { (i, it) -> 194 | it.genFuncString(vIndex = i) 195 | }.let(::append) 196 | if (typeData.virtualUnordered.isNotEmpty()) { 197 | typeData.virtualUnordered.sortedBy { it.name }.joinToString("\n\n", postfix = "\n\n") { 198 | it.genFuncString(useFakeSymbol = true) 199 | }.let(::append) 200 | } 201 | publicFunctions.let(::generateMembers).let(::append) 202 | publicVariables.let(::generateMembers).let(::append) 203 | appendLine(" // NOLINTEND") 204 | trim() 205 | appendLine() 206 | } 207 | 208 | /** 209 | * @param genFunc if true, generate function, otherwise generate static global variable 210 | */ 211 | private fun generateProtected(genFunc: Boolean = true) = buildString { 212 | if (genFunc) { 213 | if (protectedFunctions.isEmpty()) { 214 | return "" 215 | } 216 | appendLine(" // protected:") 217 | appendLine(" // NOLINTBEGIN") 218 | protectedFunctions.let(::generateMembers).let(::append) 219 | appendLine(" // NOLINTEND") 220 | } else { 221 | if (protectedVariables.isEmpty()) { 222 | return "" 223 | } 224 | appendLine(" // protected:") 225 | appendLine(" // NOLINTBEGIN") 226 | protectedVariables.let(::generateMembers).let(::append) 227 | appendLine(" // NOLINTEND") 228 | } 229 | trim() 230 | appendLine() 231 | } 232 | 233 | /** 234 | * @param genFunc if true, generate function, otherwise generate static global variable 235 | */ 236 | private fun generatePrivate(genFunc: Boolean = true) = buildString { 237 | if (genFunc) { 238 | if (privateFunctions.isEmpty()) { 239 | return "" 240 | } 241 | appendLine(" // private:") 242 | appendLine(" // NOLINTBEGIN") 243 | privateFunctions.let(::generateMembers).let(::append) 244 | appendLine(" // NOLINTEND") 245 | } else { 246 | if (privateVariables.isEmpty()) { 247 | return "" 248 | } 249 | appendLine(" // private:") 250 | appendLine(" // NOLINTBEGIN") 251 | privateVariables.let(::generateMembers).let(::append) 252 | appendLine(" // NOLINTEND") 253 | } 254 | trim() 255 | appendLine() 256 | } 257 | 258 | private fun generateMembers(members: List) = buildString { 259 | if (members.isEmpty()) return "" 260 | fun isStatic(member: MemberTypeData) = !(member.isPtrCall() || member.isVirtual()) 261 | members.sortedWith { o1, o2 -> 262 | if (isStatic(o1) == isStatic(o2)) { 263 | o1.name.compareTo(o2.name) 264 | } else { 265 | if (isStatic(o1)) 1 else -1 266 | } 267 | }.joinToString("\n\n", postfix = "\n\n") { 268 | it.genFuncString() 269 | }.let(::append) 270 | } 271 | 272 | private fun generateMemberAccessor() = buildString { 273 | val members = privateVariables + protectedVariables 274 | if (members.isEmpty()) return "" 275 | appendLine("// member accessor") 276 | appendLine("public:") 277 | appendLine(" // NOLINTBEGIN") 278 | members.forEach { 279 | val pureName = it.name.removeSuffix("[]") 280 | appendLine(" static auto& $$pureName() { return $pureName; }\n") 281 | } 282 | appendLine(" // NOLINTEND") 283 | trim() 284 | appendLine() 285 | } 286 | 287 | private fun getTemplateDefine(): String? { 288 | val params = TypeManager.template[name] ?: return null 289 | check(params.isNotEmpty()) { "$name: template must have at least one parameter." } 290 | return buildString { 291 | append("template<") 292 | if (params[0] == NodeType.VARIABLE) { 293 | append("typename... T0>") 294 | return@buildString 295 | } 296 | check(params.last() != NodeType.VARIABLE || params.size > 1) { 297 | "$name: variable template must be the last template parameter, but not the only one." 298 | } 299 | val isVariable = params.last() == NodeType.VARIABLE 300 | params.forEachIndexed { index, nodeType -> 301 | when (nodeType) { 302 | NodeType.TYPE -> append("typename") 303 | NodeType.INTEGER -> append("int") 304 | NodeType.FLOAT -> append("float") 305 | NodeType.BOOLEAN -> append("bool") 306 | NodeType.VARIABLE -> return@forEachIndexed 307 | } 308 | if (isVariable && index == params.size - 2) { 309 | append("...") 310 | } 311 | append(" T$index") 312 | if (index != params.size - 1 && params.getOrNull(index + 1) != NodeType.VARIABLE) { 313 | append(", ") 314 | } 315 | } 316 | append(">") 317 | } 318 | } 319 | 320 | fun collectParents(): List { 321 | val parents = arrayListOf() 322 | parents.addAll(this.parents) 323 | innerTypes.forEach { innerType -> 324 | if (innerType is ClassType) parents.addAll(innerType.collectParents()) 325 | } 326 | return parents 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.entity 2 | 3 | import com.liteldev.headeroutput.data.TypeData 4 | 5 | class EnumType(name: String) : BaseType(name, TypeKind.ENUM, TypeData.empty()) { 6 | override fun generateTypeDefine(): String { 7 | return "enum class $simpleName {};\n" 8 | } 9 | 10 | override fun generateTypeDeclare(): String { 11 | val baseDeclaration = "enum class $simpleName;" 12 | if (outerType == null || outerType is ClassType) { 13 | return baseDeclaration 14 | } 15 | if (outerType?.type == TypeKind.NAMESPACE) { 16 | return "namespace ${outerType?.name} { $baseDeclaration }" 17 | } 18 | error("unreachable") 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.entity 2 | 3 | import com.liteldev.headeroutput.data.TypeData 4 | import com.liteldev.headeroutput.isEnum 5 | import com.liteldev.headeroutput.isNamespace 6 | import com.liteldev.headeroutput.relativePathTo 7 | 8 | class NamespaceType( 9 | name: String, typeData: TypeData 10 | ) : BaseType(name, TypeKind.NAMESPACE, typeData) { 11 | 12 | private fun genPublic() = buildString { 13 | if (typeData.publicTypes.isEmpty()) { 14 | return@buildString 15 | } 16 | appendLine(" // NOLINTBEGIN") 17 | typeData.publicTypes.sortedWith { o1, o2 -> o1.compare(o2) }.joinToString("\n\n") { 18 | it.genFuncString(namespace = true) 19 | }.let(::appendLine) 20 | appendLine(" // NOLINTEND") 21 | appendLine() 22 | } 23 | 24 | override fun generateTypeDefine(): String { 25 | val sb = StringBuilder() 26 | sb.append("namespace $name {\n") 27 | sb.append(genPublic()) 28 | sb.appendLine("};") 29 | return sb.toString() 30 | } 31 | 32 | override fun initIncludeAndForwardDeclareList() { 33 | // not include self, inner type, and types can forward declare 34 | allReferences.filter { 35 | outerType?.isNamespace() == true 36 | || it.name.contains("::") 37 | || ((it as? ClassType)?.isTemplateClass == true) 38 | || it.isEnum() 39 | }.forEach { 40 | if (it.isEnum() || it.outerType is ClassType || (it as? ClassType)?.isTemplateClass == true) { 41 | this.path.relativePathTo(it.path).let(includeList::add) 42 | } else { 43 | it.generateTypeDeclare().let(forwardDeclareList::add) 44 | } 45 | } 46 | includeList.remove(this.path.relativePathTo(this.path)) 47 | includeList.remove("") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/entity/UnionType.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput.entity 2 | 3 | import com.liteldev.headeroutput.data.TypeData 4 | 5 | class UnionType(name: String) : BaseType(name, TypeKind.UNION, TypeData.empty()) { 6 | override fun generateTypeDefine(): String { 7 | return "union $simpleName {};\n" 8 | } 9 | 10 | override fun generateTypeDeclare(): String { 11 | val baseDeclaration = "union $simpleName;" 12 | if (outerType == null || outerType is ClassType) { 13 | return baseDeclaration 14 | } 15 | if (outerType?.type == TypeKind.NAMESPACE) { 16 | return "namespace ${outerType?.name} { $baseDeclaration }" 17 | } 18 | error("unreachable") 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/liteldev/headeroutput/utils.kt: -------------------------------------------------------------------------------- 1 | package com.liteldev.headeroutput 2 | 3 | import com.liteldev.headeroutput.config.GeneratorConfig 4 | import com.liteldev.headeroutput.entity.BaseType 5 | import java.nio.file.Paths 6 | 7 | fun String.relativePathTo(path: String): String { 8 | if (!GeneratorConfig.enableRelativePath) 9 | return path 10 | return Paths.get(this.substringBeforeLast("/")).relativize(Paths.get(path)).toString().replace("\\", "/") 11 | } 12 | 13 | fun StringBuilder.appendSpace(count: Int): StringBuilder = append(" ".repeat(count)) 14 | 15 | fun BaseType.isClass(): Boolean = this.type == BaseType.TypeKind.CLASS 16 | 17 | fun BaseType.isStruct() = this.type == BaseType.TypeKind.STRUCT 18 | 19 | fun BaseType.isNamespace() = this.type == BaseType.TypeKind.NAMESPACE 20 | 21 | fun BaseType.isEnum() = this.type == BaseType.TypeKind.ENUM 22 | 23 | fun BaseType.isUnion() = this.type == BaseType.TypeKind.UNION 24 | 25 | fun BaseType.getTopLevelFileType(): BaseType { 26 | require(TypeManager.nestingMap.isNotEmpty()) { "TypeManager.nestingMap is empty" } 27 | 28 | outerType ?: return this 29 | if (isNamespace()) { 30 | return this 31 | } 32 | var outer = outerType!! 33 | if (outer.isNamespace()) { 34 | return this 35 | } 36 | while (outer.outerType != null) { 37 | if (outer.outerType!!.isNamespace()) { 38 | return outer 39 | } 40 | outer = outer.outerType!! 41 | } 42 | return outer 43 | } 44 | 45 | fun String.toSnakeCase(): String { 46 | val sb = StringBuilder() 47 | var lastCharIsSeparator = false 48 | forEachIndexed { index, c -> 49 | if (c.isUpperCase()) { 50 | if (index != 0 && !lastCharIsSeparator) { 51 | sb.append("_") 52 | } 53 | sb.append(c.lowercaseChar()) 54 | } else { 55 | sb.append(c) 56 | } 57 | lastCharIsSeparator = c == '/' 58 | } 59 | return sb.toString() 60 | } 61 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/test/kotlin/TestEnum.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.serialization.KSerializer 2 | import kotlinx.serialization.Serializable 3 | import kotlinx.serialization.decodeFromString 4 | import kotlinx.serialization.descriptors.PrimitiveKind 5 | import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor 6 | import kotlinx.serialization.descriptors.SerialDescriptor 7 | import kotlinx.serialization.encodeToString 8 | import kotlinx.serialization.encoding.Decoder 9 | import kotlinx.serialization.encoding.Encoder 10 | import kotlinx.serialization.json.Json 11 | import kotlin.reflect.KClass 12 | import kotlin.test.Test 13 | import kotlin.test.assertEquals 14 | 15 | interface IntEnumKind { 16 | val value: Int 17 | } 18 | 19 | open class IntEnumSerializer(serialName: String, private val clazz: KClass) : 20 | KSerializer where E : Enum, E : IntEnumKind { 21 | 22 | override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(serialName, PrimitiveKind.INT) 23 | 24 | override fun serialize(encoder: Encoder, value: E) { 25 | encoder.encodeInt(value.value) 26 | } 27 | 28 | override fun deserialize(decoder: Decoder): E { 29 | val decodeInt = decoder.decodeInt() 30 | return clazz.java.enumConstants.firstOrNull { it.value == decodeInt } 31 | ?: throw IllegalArgumentException("No enum constant found for value $decodeInt") 32 | } 33 | } 34 | 35 | @Suppress("unused") 36 | @Serializable(with = Test2Enum.Companion::class) 37 | enum class Test2Enum(override val value: Int) : IntEnumKind { 38 | A(0), B(1), C(2); 39 | 40 | companion object : IntEnumSerializer(Test2Enum::class.simpleName!!, Test2Enum::class) 41 | } 42 | 43 | object Test1EnumSerializer : KSerializer { 44 | override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Test1Enum", PrimitiveKind.INT) 45 | 46 | override fun serialize(encoder: Encoder, value: Test1Enum) { 47 | encoder.encodeInt(value.value) 48 | } 49 | 50 | override fun deserialize(decoder: Decoder): Test1Enum { 51 | return Test1Enum.getByValue(decoder.decodeInt())!! 52 | } 53 | } 54 | 55 | @Serializable(with = Test1EnumSerializer::class) 56 | enum class Test1Enum(val value: Int) { 57 | Test1Enum1(1), Test1Enum2(2); 58 | 59 | companion object { 60 | fun getByValue(value: Int) = entries.firstOrNull { it.value == value } 61 | } 62 | } 63 | 64 | @Serializable 65 | data class SomeData(val test1Enum: Test1Enum) 66 | 67 | class TestEnum { 68 | @Test 69 | fun testEnumSerializer() { 70 | val json = Json.encodeToString(Test2Enum.B) 71 | assertEquals("1", json) 72 | assertEquals(Test2Enum.B, Json.decodeFromString(json)) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/kotlin/TestTemplateAst.kt: -------------------------------------------------------------------------------- 1 | import com.liteldev.headeroutput.ast.template.Parser 2 | import com.liteldev.headeroutput.ast.template.TypeNode 3 | import kotlin.test.Test 4 | 5 | class TestTemplateAst { 6 | private fun parseType(template: String): TypeNode? { 7 | val result = Parser(template).parse() 8 | if (result is TypeNode) { 9 | return result 10 | } 11 | return null 12 | } 13 | 14 | @Test 15 | fun testBlank() { 16 | val test = " " 17 | val ast = parseType(test) 18 | assert(ast == null) 19 | } 20 | 21 | @Test 22 | fun testFunction() { 23 | val test = 24 | "class std::function*)>" 25 | val ast = parseType(test) 26 | println(ast) 27 | } 28 | 29 | @Test 30 | fun testLiteral() { 31 | val test = "class std::a, 1, bool, 1.2, false>" 32 | val ast = parseType(test) 33 | assert(ast != null) 34 | println(ast) 35 | } 36 | 37 | @Test 38 | fun testSpace() { 39 | val test = " class ABC::DE啊aF < const class Test < class Testa> , 11, 2.44 ,struct aaiwja **& >" 40 | val ast = parseType(test) 41 | println(ast) 42 | } 43 | 44 | @Test 45 | fun testParentFunction() { 46 | val test = 47 | "struct std::pair, class std::allocator> &, class std::vector, class std::allocator>, class std::allocator, class std::allocator>>> &) const, class CommandRegistry::Symbol> const" 48 | val ast = parseType(test) 49 | println(ast) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/kotlin/main.kt: -------------------------------------------------------------------------------- 1 | import com.liteldev.headeroutput.HeaderOutput 2 | 3 | fun main() { 4 | HeaderOutput.main(arrayOf( 5 | "-c", "assets/config.toml", 6 | "-d", "assets/declareMap.json", 7 | "-p", "assets/predefine.h", 8 | "-y" 9 | )) 10 | } 11 | --------------------------------------------------------------------------------