diff options
| author | Tomasz Kramkowski <tomasz@kramkow.ski> | 2026-11-03 20:01:42 +0000 |
|---|---|---|
| committer | Tomasz Kramkowski <tomasz@kramkow.ski> | 2026-11-03 20:01:42 +0000 |
| commit | 098bd37f355a77abbb5751fc55a91f7ebe74cc6b (patch) | |
| tree | d3bc93cb427276a4936d79903b62e156d5c538e7 | |
| parent | 64c349eb1dc0fde65fd4e6ddf525faa0a4c64a91 (diff) | |
| download | steamvr-patches-098bd37f355a77abbb5751fc55a91f7ebe74cc6b.tar.gz steamvr-patches-098bd37f355a77abbb5751fc55a91f7ebe74cc6b.tar.xz steamvr-patches-098bd37f355a77abbb5751fc55a91f7ebe74cc6b.zip | |
| -rw-r--r-- | .clang-format | 77 | ||||
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | LICENSE | 131 | ||||
| -rw-r--r-- | LICENSES/CC-BY-SA-4.0.text | 428 | ||||
| -rw-r--r-- | LICENSES/CC0-1.0.text | 121 | ||||
| -rw-r--r-- | LICENSES/GPL-3.0-or-later.md | 675 | ||||
| -rw-r--r-- | README.md | 5 | ||||
| -rwxr-xr-x | frame-steamos/install | 2 | ||||
| -rw-r--r-- | pyproject.toml | 26 | ||||
| -rw-r--r-- | src/steamvr_patches/__init__.py | 125 | ||||
| -rw-r--r-- | src/steamvr_patches/elf.py | 232 | ||||
| -rw-r--r-- | src/steamvr_patches/template.py | 169 | ||||
| -rw-r--r-- | steamvr/image-wait/image_wait.c | 148 | ||||
| -rw-r--r-- | steamvr/image-wait/trampoline.S | 26 | ||||
| -rwxr-xr-x | steamvr/patch-steamvr | 216 | ||||
| -rw-r--r-- | steamvr/patches.toml | 82 | ||||
| -rw-r--r-- | uv.lock | 182 |
17 files changed, 2303 insertions, 348 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1616256 --- /dev/null +++ b/.clang-format @@ -0,0 +1,77 @@ +# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +# SPDX-License-Identifier: CC0-1.0 + +--- +AlignAfterOpenBracket: BlockIndent +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakBeforeMultilineStrings: false +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +AlwaysBreakAfterReturnType: None +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Linux +BreakBeforeTernaryOperators: true +BreakStringLiterals: false +ColumnLimit: 85 +ContinuationIndentWidth: 8 +DerivePointerAlignment: false +DisableFormat: false +# Preserve for now, to avoid problems, will be changed later. +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^<Python\.h>$' + Priority: -1 + - Regex: '^"' + Priority: 1 + - Regex: '^<' + Priority: 2 +IncludeIsMainRegex: '$' +IndentCaseBlocks: true +IndentCaseLabels: false +IndentGotoLabels: false # maybe ... +IndentPPDirectives: None +IndentWidth: 8 +IndentWrappedFunctionNames: false +InsertBraces: false +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +MaxEmptyLinesToKeep: 1 +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakOpenParenthesis: 10 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 60 +PenaltyReturnTypeOnItsOwnLine: 40 +PointerAlignment: Right +QualifierAlignment: Leave +ReflowComments: false +# Never for now, to avoid problems, will be changed later. +SortIncludes: CaseInsensitive +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAroundPointerQualifiers: Both +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 8 +UseCRLF: false +UseTab: ForContinuationAndIndentation +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd2f5d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +# SPDX-License-Identifier: CC0-1.0 + +dist/ +__pycache__/ +.venv diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 6f73ac4..0000000 --- a/LICENSE +++ /dev/null @@ -1,131 +0,0 @@ -# Creative Commons CC0 1.0 Universal - -CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL -SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT -RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" -BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS -DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS -LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE -INFORMATION OR WORKS PROVIDED HEREUNDER. - -## Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator and -subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in part -through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily elects -to apply CC0 to the Work and publicly distribute the Work under its terms, -with knowledge of his or her Copyright and Related Rights in the Work and -the meaning and intended legal effect of CC0 on those rights. - -1. __Copyright and Related Rights.__ A Work made available under CC0 may be - protected by copyright and related or neighboring rights ("Copyright and - Related Rights"). Copyright and Related Rights include, but are not - limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - - ii. moral rights retained by the original author(s) and/or - performer(s); - - iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - - v. rights protecting the extraction, dissemination, use and reuse of - data in a Work; - - vi. database rights (such as those arising under Directive 96/9/EC of - the European Parliament and of the Council of 11 March 1996 on the - legal protection of databases, and under any national - implementation thereof, including any amended or successor version - of such directive); and - - vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. __Waiver.__ To the greatest extent permitted by, but not in - contravention of, applicable law, Affirmer hereby overtly, fully, - permanently, irrevocably and unconditionally waives, abandons, and - surrenders all of Affirmer's Copyright and Related Rights and associated - claims and causes of action, whether now known or unknown (including - existing as well as future claims and causes of action), in the Work (i) - in all territories worldwide, (ii) for the maximum duration provided by - applicable law or treaty (including future time extensions), (iii) in - any current or future medium and for any number of copies, and (iv) for - any purpose whatsoever, including without limitation commercial, - advertising or promotional purposes (the "Waiver"). Affirmer makes the - Waiver for the benefit of each member of the public at large and to the - detriment of Affirmer's heirs and successors, fully intending that such - Waiver shall not be subject to revocation, rescission, cancellation, - termination, or any other legal or equitable action to disrupt the quiet - enjoyment of the Work by the public as contemplated by Affirmer's - express Statement of Purpose. - -3. __Public License Fallback.__ Should any part of the Waiver for any - reason be judged legally invalid or ineffective under applicable law, - then the Waiver shall be preserved to the maximum extent permitted - taking into account Affirmer's express Statement of Purpose. In - addition, to the extent the Waiver is so judged Affirmer hereby grants - to each affected person a royalty-free, non transferable, non - sublicensable, non exclusive, irrevocable and unconditional license to - exercise Affirmer's Copyright and Related Rights in the Work (i) in all - territories worldwide, (ii) for the maximum duration provided by - applicable law or treaty (including future time extensions), (iii) in - any current or future medium and for any number of copies, and (iv) for - any purpose whatsoever, including without limitation commercial, - advertising or promotional purposes (the "License"). The License shall - be deemed effective as of the date CC0 was applied by Affirmer to the - Work. Should any part of the License for any reason be judged legally - invalid or ineffective under applicable law, such partial invalidity or - ineffectiveness shall not invalidate the remainder of the License, and - in such case Affirmer hereby affirms that he or she will not (i) - exercise any of his or her remaining Copyright and Related Rights in the - Work or (ii) assert any associated claims and causes of action with - respect to the Work, in either case contrary to Affirmer's express - Statement of Purpose. - -4. __Limitations and Disclaimers.__ - - a. No trademark or patent rights held by Affirmer are waived, - abandoned, surrendered, licensed or otherwise affected by this - document. - - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, - or the present or absence of errors, whether or not discoverable, - all to the greatest extent permissible under applicable law. - - c. Affirmer disclaims responsibility for clearing rights of other - persons that may apply to the Work or any use thereof, including - without limitation any person's Copyright and Related Rights in the - Work. Further, Affirmer disclaims responsibility for obtaining any - necessary consents, permissions or other rights required for any use - of the Work. - - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. diff --git a/LICENSES/CC-BY-SA-4.0.text b/LICENSES/CC-BY-SA-4.0.text new file mode 100644 index 0000000..2d58298 --- /dev/null +++ b/LICENSES/CC-BY-SA-4.0.text @@ -0,0 +1,428 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/LICENSES/CC0-1.0.text b/LICENSES/CC0-1.0.text new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENSES/CC0-1.0.text @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSES/GPL-3.0-or-later.md b/LICENSES/GPL-3.0-or-later.md new file mode 100644 index 0000000..496acdb --- /dev/null +++ b/LICENSES/GPL-3.0-or-later.md @@ -0,0 +1,675 @@ +# GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +<https://fsf.org/> + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies +also to any other work released this way by its authors. You can apply +it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public +License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper +mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see <https://www.gnu.org/licenses/>. + +The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read <https://www.gnu.org/licenses/why-not-lgpl.html>. @@ -1,3 +1,8 @@ +<!-- +SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +SPDX-License-Identifier: CC-BY-SA-4.0 +--> + # Patches for SteamVR on Linux Use at your own risk! diff --git a/frame-steamos/install b/frame-steamos/install index d36b0df..e03a621 100755 --- a/frame-steamos/install +++ b/frame-steamos/install @@ -1,6 +1,6 @@ #!/bin/sh # SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> -# SPDX-License-Identifier: CC0-1.0 +# SPDX-License-Identifier: GPL-3.0-or-later set -e diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e2ffb1d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +# SPDX-License-Identifier: CC0-1.0 + +[project] +name = "steamvr-patches" +version = "0.1.0" +description = "SteamVR patching utilities" +readme = "README.md" +license = "GPL-3.0-or-later" +license-files = ["LICENSES/*"] +requires-python = ">=3.13" +dependencies = [ + "capstone>=5,<6", + "pydantic>=2.12,<3", + "pyelftools>=0.32,<1", +] + +[project.scripts] +generate-patcher = "steamvr_patches:main" + +[build-system] +requires = ["uv_build>=0.13,<0.14"] +build-backend = "uv_build" + +[tool.ruff.lint] +select = ["E", "F", "I", "UP"] diff --git a/src/steamvr_patches/__init__.py b/src/steamvr_patches/__init__.py new file mode 100644 index 0000000..be3b696 --- /dev/null +++ b/src/steamvr_patches/__init__.py @@ -0,0 +1,125 @@ +# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +# SPDX-License-Identifier: GPL-3.0-or-later + +import argparse +import hashlib +import sys +import tempfile +import tomllib +from base64 import b85encode +from importlib.resources import files +from io import BytesIO +from json import dumps +from pathlib import Path +from typing import Annotated, BinaryIO, Self +from zlib import compress + +from pydantic import BaseModel, ConfigDict, Field, field_validator + +from .elf import Hook, compile_hooks + +NonnegativeInt = Annotated[int, Field(strict=True, ge=0)] + + +class Compilation(BaseModel): + model_config = ConfigDict(extra="forbid") + + sources: list[Path] = Field(min_length=1) + imports: dict[str, NonnegativeInt] = Field(default_factory=dict) + hooks: list[Hook] = Field(min_length=1) + + +class FilePatch(BaseModel): + model_config = ConfigDict(val_json_bytes="hex", extra="forbid") + + path: Path + sha256: bytes = Field(min_length=32, max_length=32) + edits: list[tuple[NonnegativeInt, bytes]] + compiled: Compilation | None = None + + +class PatchSet(BaseModel): + model_config = ConfigDict(extra="forbid") + + target: str + targets: list[FilePatch] + + @field_validator("targets") + @classmethod + def unique_paths(cls, targets: list[FilePatch]) -> list[FilePatch]: + paths = [target.path for target in targets] + if len(set(paths)) != len(paths): + raise ValueError("duplicate patch target") + return targets + + @classmethod + def parse(cls, config: dict, directory: Path) -> Self: + result = cls.model_validate(config) + for target in result.targets: + if target.compiled is not None: + target.compiled.sources = [ + directory / source for source in target.compiled.sources + ] + return result + + +def compile_patch(config: FilePatch, source: BinaryIO, output: Path) -> dict: + source.seek(0) + data = source.read() + if hashlib.sha256(data).digest() != config.sha256: + raise ValueError(f"input SHA-256 mismatch: {config.path}") + edits = list(config.edits) + if config.compiled is not None: + compiled = config.compiled + edits += compile_hooks( + source, compiled.sources, compiled.imports, compiled.hooks, output + ) + end = 0 + for offset, replacement in sorted(edits): + if offset < end: + raise ValueError("overlapping edits") + end = offset + len(replacement) + patched = BytesIO(data) + for offset, replacement in edits: + patched.seek(offset) + patched.write(replacement) + return { + "original_hash": config.sha256, + "patched_hash": hashlib.sha256(patched.getbuffer()).digest(), + "edits": edits, + } + + +def render(patches: dict[str, dict]) -> str: + template = files("steamvr_patches").joinpath("template.py").read_text() + placeholder = "PATCH_DATA = {}" + if template.count(placeholder) != 1: + raise ValueError("missing or duplicate runtime patch-table placeholder") + data = dumps(patches, default=bytes.hex, separators=(",", ":")).encode() + encoded = b85encode(compress(data, level=9)) + return template.replace(placeholder, f"PATCH_DATA = decode_patch_data({encoded!r})") + + +def generate(patch_data: Path, input_root: Path) -> str: + with (patch_data / "patches.toml").open("rb") as source: + definition = PatchSet.parse(tomllib.load(source), patch_data) + patches = {} + with tempfile.TemporaryDirectory(prefix="generate-patcher-") as temporary: + for index, config in enumerate(definition.targets): + with (input_root / config.path).open("rb") as source: + patches[config.path.as_posix()] = compile_patch( + config, source, Path(temporary) / str(index) + ) + return render(patches) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + "patch_data", type=Path, help="directory containing patches.toml etc" + ) + parser.add_argument( + "input_root", type=Path, help="directory containing the unpatched target files" + ) + args = parser.parse_args() + sys.stdout.write(generate(args.patch_data, args.input_root)) diff --git a/src/steamvr_patches/elf.py b/src/steamvr_patches/elf.py new file mode 100644 index 0000000..e69c567 --- /dev/null +++ b/src/steamvr_patches/elf.py @@ -0,0 +1,232 @@ +# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +# SPDX-License-Identifier: GPL-3.0-or-later + +"""Compile C/assembly hooks into one Linux x86-64 ELF patch payload.""" + +import struct +import subprocess +from pathlib import Path +from typing import BinaryIO + +from capstone import CS_ARCH_X86, CS_MODE_64, Cs +from elftools.construct import Container +from elftools.elf.constants import P_FLAGS +from elftools.elf.elffile import ELFFile +from elftools.elf.relocation import RelocationSection +from elftools.elf.sections import SymbolTableSection +from pydantic import BaseModel, ConfigDict, Field + + +class Hook(BaseModel): + model_config = ConfigDict(strict=True, frozen=True, extra="forbid") + + address: int = Field(ge=0) + length: int = Field(ge=5) + target: str = Field(min_length=1) + + +class CompiledPayload: + """Position-independent code with resolved entry addresses inside it.""" + + code: bytes + entries: dict[str, int] + + def __init__(self, source: BinaryIO, targets: list[str]): + elf = ELFFile(source) + for section in elf.iter_sections(): + if ( + section["sh_flags"] & 2 + and section["sh_size"] + and section.name != ".payload" + ): + raise ValueError(f"unsupported allocated section: {section.name}") + # PC32 (2) PLT32 (4) (PC-relative) + if isinstance(section, RelocationSection): + if any( + r["r_info_type"] not in (2, 4) for r in section.iter_relocations() + ): + raise ValueError("helper contains a non-relative relocation") + payload = elf.get_section_by_name(".payload") + if payload is None: + raise ValueError("missing compiled payload") + self.code = payload.data() + symbols = elf.get_section_by_name(".symtab") + if not isinstance(symbols, SymbolTableSection): + raise ValueError("missing compiled symbol table") + payload_index = elf.get_section_index(".payload") + address = payload["sh_addr"] + self.entries = {} + for target in targets: + matches = symbols.get_symbol_by_name(target) or [] + if len(matches) != 1 or matches[0]["st_shndx"] != payload_index: + raise ValueError( + f"hook target is not a unique payload symbol: {target}" + ) + entry = matches[0]["st_value"] + if not address <= entry < address + len(self.code): + raise ValueError( + f"hook target is outside the compiled payload: {target}" + ) + self.entries[target] = entry + + +def align(value: int, boundary: int) -> int: + return (value + boundary - 1) & -boundary + + +def run(*args: str) -> str: + return subprocess.check_output(args, text=True) + + +def resolve_hook_sites(elf: ELFFile, hooks: list[Hook]) -> list[tuple[Hook, int]]: + """Find file offsets and verify that each hook covers whole instructions.""" + source = elf.stream + sites = [] + disassembler = Cs(CS_ARCH_X86, CS_MODE_64) + for hook in hooks: + offset = next(elf.address_offsets(hook.address, hook.length)) + source.seek(offset) + instructions = disassembler.disasm(source.read(hook.length), hook.address) + if sum(i.size for i in instructions) != hook.length: + raise ValueError("hook length cuts an instruction or contains invalid code") + sites.append((hook, offset)) + return sites + + +def compile_code( + sources: list[Path], + imports: dict[str, int], + targets: list[str], + code_va: int, + output: Path, +) -> CompiledPayload: + """Compile and link the patch sources at their destination address.""" + output.mkdir(parents=True, exist_ok=False) + objects = [] + flags = [ + "-Os", + "-ffreestanding", + "-fPIE", + "-fvisibility=hidden", + "-fno-stack-protector", + "-fno-strict-aliasing", + "-mno-red-zone", + "-fno-asynchronous-unwind-tables", + "-fno-unwind-tables", + "-fcf-protection=none", + "-Wall", + "-Wextra", + ] + for number, source_path in enumerate(sources): + obj = output / f"{number}.o" + source_flags = ["-std=c17"] if source_path.suffix == ".c" else [] + run("cc", *flags, *source_flags, "-c", str(source_path), "-o", str(obj)) + objects.append(str(obj)) + + linker_script = output / "layout.ld" + linker_script.write_text( + "PHDRS { code PT_LOAD FLAGS(5); }\n" # PF_R | PF_X: readable/executable. + f"SECTIONS {{ . = {code_va:#x};\n" + " .payload : { *(.text.entry) *(.text*) *(.rodata*) } :code\n" + " /DISCARD/ : { *(.comment) *(.note.GNU-stack) }\n}\n" + ) + import_args = [f"--defsym={name}={value:#x}" for name, value in imports.items()] + linked = output / "helper.elf" + run( + "ld", + "--build-id=none", + "--emit-relocs", + f"--script={linker_script}", + *import_args, + f"--output={linked}", + *objects, + ) + with linked.open("rb") as compiled: + return CompiledPayload(compiled, targets) + + +def make_hook_edits( + sites: list[tuple[Hook, int]], entries: dict[str, int] +) -> list[tuple[int, bytes]]: + """Redirect each hook site to its compiled entry with a relative jump.""" + edits = [] + for hook, offset in sites: + displacement = entries[hook.target] - hook.address - 5 + if not -(1 << 31) <= displacement < 1 << 31: + raise ValueError( + f"hook target is outside relative jump range: {hook.target}" + ) + jump = b"\xe9" + struct.pack("<i", displacement) + b"\x90" * (hook.length - 5) + edits.append((offset, jump)) + return edits + + +def compile_segment( + elf: ELFFile, + sources: list[Path], + imports: dict[str, int], + targets: list[str], + output: Path, +) -> tuple[CompiledPayload, list[tuple[int, bytes]]]: + """Place the compiled code and enlarged program-header table in a new segment.""" + phdr = elf.structs.Elf_Phdr + headers = [segment.header for segment in elf.iter_segments()] + # Place the new segment after all existing loadable mappings. + load_end = max( + header["p_vaddr"] + header["p_memsz"] + for header in headers + if header["p_type"] == "PT_LOAD" + ) + segment_va = align(load_end, 4096) + segment_offset = align(elf.stream_len, 4096) + table_size = (len(headers) + 1) * phdr.sizeof() + code_delta = align(table_size, 16) + code_va = segment_va + code_delta + compiled = compile_code(sources, imports, targets, code_va, output) + + # Relocate the enlarged program-header table into the new segment. + table_header = next(header for header in headers if header["p_type"] == "PT_PHDR") + table_header.update( + p_flags=P_FLAGS.PF_R, + p_offset=segment_offset, + p_vaddr=segment_va, + p_paddr=segment_va, + p_filesz=table_size, + p_memsz=table_size, + p_align=8, + ) + segment_size = code_delta + len(compiled.code) + headers.append( + Container( + p_type="PT_LOAD", + p_flags=P_FLAGS.PF_R | P_FLAGS.PF_X, + p_offset=segment_offset, + p_vaddr=segment_va, + p_paddr=segment_va, + p_filesz=segment_size, + p_memsz=segment_size, + p_align=4096, + ) + ) + edits = [ + (0x20, struct.pack("<Q", segment_offset)), # Elf64_Ehdr.e_phoff + (0x38, struct.pack("<H", len(headers))), # Elf64_Ehdr.e_phnum + (segment_offset, b"".join(phdr.build(header) for header in headers)), + (segment_offset + code_delta, compiled.code), + ] + return compiled, edits + + +def compile_hooks( + source: BinaryIO, + sources: list[Path], + imports: dict[str, int], + hooks: list[Hook], + output: Path, +) -> list[tuple[int, bytes]]: + elf = ELFFile(source) + sites = resolve_hook_sites(elf, hooks) + compiled, segment_edits = compile_segment( + elf, sources, imports, [hook.target for hook in hooks], output + ) + return make_hook_edits(sites, compiled.entries) + segment_edits diff --git a/src/steamvr_patches/template.py b/src/steamvr_patches/template.py new file mode 100644 index 0000000..4f88b10 --- /dev/null +++ b/src/steamvr_patches/template.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +# SPDX-License-Identifier: CC0-1.0 + +"""Patching wrapper script. + +This file is generated. + +See https://kramkow.ski/project/steamvr-patches.html and +https://the-tk.com/cgit/steamvr-patches/about/ for more information. + +Use at your own risk! + + +Installation (where <thing> is the thing this patcher targets, e.g. steamvr): + +1. Put this `patch-<thing>` script somewhere, e.g. `~/.local/libexec/patch-<thing>` +2. Mark it executable +3. Change the launch options of the specified <thing> to: + `/absolute/path/to/patch-<thing> %command%`. + If you already have something in there, I'm sure you can figure it out. +4. Launch <thing> normally. You should see a desktop notification with some + diagnostics which specify if the script actually did/tried to do something. +""" + +import hashlib +import os +import stat +import subprocess +import sys +import tempfile +from base64 import b85decode +from dataclasses import dataclass +from io import BytesIO +from json import loads as json_loads +from pathlib import Path +from zlib import decompress + +NAME = Path(__file__).name +MARKER = b"Valve, please fix." + + +@dataclass(frozen=True) +class Edit: + offset: int + replacement: bytes + + +@dataclass(frozen=True) +class Patch: + original_hash: bytes + patched_hash: bytes + edits: tuple[Edit, ...] + + @classmethod + def from_dict(cls, data: dict) -> "Patch": + return cls( + bytes.fromhex(data["original_hash"]), + bytes.fromhex(data["patched_hash"]), + tuple( + Edit(offset, bytes.fromhex(replacement)) + for offset, replacement in data["edits"] + ), + ) + + def apply_to(self, data: bytes) -> bytes: + payload = data.removesuffix(MARKER) + digest = sha256(payload) + if digest == self.patched_hash: + return payload + MARKER + if digest != self.original_hash: + if payload != data: + raise LookupError( + "already patched with another set of patches: " + "verify file integrity and restart" + ) + raise LookupError("unrecognized file hash: this wrapper may need updating") + result = BytesIO(payload) + for edit in self.edits: + result.seek(edit.offset) + result.write(edit.replacement) + patched = result.getvalue() + if sha256(patched) != self.patched_hash: + raise ValueError("patched file checksum mismatch") + return patched + MARKER + + +def decode_patch_data(data: bytes) -> dict: + return json_loads(decompress(b85decode(data))) + + +# Just to keep the file small, not for obfuscation. +PATCH_DATA = {} +PATCHES: dict[str, Patch] = { + path: Patch.from_dict(data) for path, data in PATCH_DATA.items() +} + + +def sha256(data): + return hashlib.sha256(data).digest() + + +def notify(message): + print(f"{NAME}: {message}", file=sys.stderr) + try: + subprocess.run( + ["notify-send", NAME, message], + check=False, + timeout=3, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + except (OSError, subprocess.TimeoutExpired): + pass + + +def replace(path, data): + mode = stat.S_IMODE(path.stat().st_mode) + with tempfile.NamedTemporaryFile( + dir=path.parent, prefix=path.name + ".", delete=False + ) as f: + temporary = Path(f.name) + try: + f.write(data) + f.flush() + os.fchmod(f.fileno(), mode) + os.fsync(f.fileno()) + os.replace(temporary, path) + finally: + temporary.unlink(missing_ok=True) + + +def main(command): + diagnostics = [] + if not command: + print(f"Usage: {NAME} command [args...]", file=sys.stderr) + return 2 + try: + root = Path.cwd() + pending = [] + for relative, patch in PATCHES.items(): + path = (root / relative).resolve() + try: + original = path.read_bytes() + except FileNotFoundError as e: + diagnostics.append(f"Skipped `{relative}`: {e}") + continue + try: + updated = patch.apply_to(original) + except ValueError as e: + raise ValueError(f"Patching `{relative}` failed: {e}") from e + except LookupError as e: + diagnostics.append(f"Skipped `{relative}`: {e}") + continue + if updated != original: + pending.append((path, updated)) + for path, updated in pending: + replace(path, updated) + diagnostics.append(f"Patched {path}") + if diagnostics: + notify("\n".join(diagnostics)) + except (OSError, ValueError) as e: + notify("\n".join([f"Encountered an error: {e}", *diagnostics])) + return 1 + os.execvp(command[0], command) + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/steamvr/image-wait/image_wait.c b/steamvr/image-wait/image_wait.c new file mode 100644 index 0000000..e8112c3 --- /dev/null +++ b/steamvr/image-wait/image_wait.c @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +// SPDX-License-Identifier: CC0-1.0 + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> + +#define ASSERT_SIZE(type, expected) \ + static_assert(sizeof (type) == (expected), #type " size must be " #expected) +#define ASSERT_OFFSET(type, field, expected) \ + static_assert( \ + offsetof(type, field) == (expected), \ + #type "." #field " offset must be " #expected \ + ) + +ASSERT_SIZE(void *, 8); + +enum { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000 }; + +typedef struct { + uintptr_t semaphore; + uint64_t value; +} SemaphoreEntry; +ASSERT_SIZE(SemaphoreEntry, 16); +ASSERT_OFFSET(SemaphoreEntry, value, 8); + +// vr::IVRSharedResource +struct SharedResource { + const struct SharedResourceVtable *vtable; +}; + +typedef struct Semaphore *GetSemaphore(struct SharedResource *); + +struct SharedResourceVtable { + unsigned char unknown_000[0x30]; + GetSemaphore *get_semaphore; +}; +ASSERT_OFFSET(struct SharedResourceVtable, get_semaphore, 0x30); + +typedef uint64_t SemaphoreGetU64(struct Semaphore *); + +struct SemaphoreVtable { + unsigned char unknown_000[0x40]; + SemaphoreGetU64 *get_handle; + unsigned char unknown_048[0x70 - 0x48]; + SemaphoreGetU64 *get_value; +}; +ASSERT_OFFSET(struct SemaphoreVtable, get_handle, 0x40); +ASSERT_OFFSET(struct SemaphoreVtable, get_value, 0x70); + +// vr::IVRSharedSemaphore +struct Semaphore { + const struct SemaphoreVtable *vtable; +}; + +// vr::IVRSharedImage +struct Image { + const void *vtable; +}; + +struct Texture { + unsigned char unknown_000[0x48]; + struct Image *image; +}; +ASSERT_OFFSET(struct Texture, image, 0x48); + +struct Batch { + unsigned char unknown_000[0xf0]; + SemaphoreEntry *waits; + SemaphoreEntry *waits_end; + unsigned char unknown_100[0x140 - 0x100]; + uint32_t *wait_stages; + unsigned char unknown_148[0x398 - 0x148]; + SemaphoreEntry *signals; + SemaphoreEntry *signals_end; +}; +ASSERT_OFFSET(struct Batch, waits, 0xf0); +ASSERT_OFFSET(struct Batch, waits_end, 0xf8); +ASSERT_OFFSET(struct Batch, wait_stages, 0x140); +ASSERT_OFFSET(struct Batch, signals, 0x398); +ASSERT_OFFSET(struct Batch, signals_end, 0x3a0); + +struct RendererContext { + unsigned char unknown_000[8]; + struct Batch *batch; +}; +ASSERT_OFFSET(struct RendererContext, batch, 8); + +// CFacetVRRenderer +struct Renderer { + unsigned char unknown_000[0x12b0]; + struct RendererContext *context; +}; +ASSERT_OFFSET(struct Renderer, context, 0x12b0); + +extern void append_wait( + struct Batch *batch, uintptr_t semaphore, uint32_t stages, uint64_t value +) __attribute__((visibility("hidden"))); + +// The patch calls this function at the beginning of a method in the "Facet" +// `renderer` which is called, among other things, on `texture`s that the current +// submission batch will use as shader inputs (e.g. eye textures). +void register_image_wait(struct Renderer *renderer, struct Texture *texture) +{ + // First we need the completion semaphore for the texture. + struct Image *image = texture->image; + if (!image) return; + + // The vbase offset entry for converting `vr::IVRSharedImage *` to + // `vr::IVRSharedResource *` is located at `image->vtable - 0x50`. + ptrdiff_t resource_offset = + *(const ptrdiff_t *)((const char *)image->vtable - 0x50); + struct SharedResource *resource = (void *)((char *)image + resource_offset); + + struct Semaphore *semaphore = resource->vtable->get_semaphore(resource); + if (!semaphore) return; + + uintptr_t handle = semaphore->vtable->get_handle(semaphore); + if (!handle) return; + + // The current batch that is being prepared, and that may need the image + // semaphore as a dependency. + struct Batch *batch = renderer->context->batch; + + // If the texture we got called on has the same completion semaphore as one + // of the completion signals of this batch, then making the batch depend on + // the semaphore could create a loop, and that would be bad. + for (SemaphoreEntry *p = batch->signals; p != batch->signals_end; p++) { + if (p->semaphore == handle) return; + } + + // If there's already a wait on this texture's semaphore, ensure it will + // block all work in this batch and that the value is at least as high as the + // semaphore value required for this texture to be ready. + uint64_t value = semaphore->vtable->get_value(semaphore); + uint32_t *stages = batch->wait_stages; + for (SemaphoreEntry *p = batch->waits; p != batch->waits_end; + p++, stages++) { + if (p->semaphore != handle) continue; + + if (p->value < value) p->value = value; + *stages |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + return; + } + + // Otherwise append a wait on this texture's completion semaphore. + append_wait(batch, handle, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, value); +} diff --git a/steamvr/image-wait/trampoline.S b/steamvr/image-wait/trampoline.S new file mode 100644 index 0000000..6c7dbc4 --- /dev/null +++ b/steamvr/image-wait/trampoline.S @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> + * SPDX-License-Identifier: CC0-1.0 + */ + +.intel_syntax noprefix +.section .text.entry,"ax",@progbits +.global input_wait +input_wait: + push rdi + push rsi + push rdx + push rcx + sub rsp, 8 + call register_image_wait + add rsp, 8 + pop rcx + pop rdx + pop rsi + pop rdi + + # Replay the three displaced instructions, then resume the original function. + push rbp + mov rax, rsi + mov r9d, edx + jmp resume_transition +.section .note.GNU-stack,"",@progbits diff --git a/steamvr/patch-steamvr b/steamvr/patch-steamvr deleted file mode 100755 index f176b0a..0000000 --- a/steamvr/patch-steamvr +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> -# SPDX-License-Identifier: CC0-1.0 - -"""A steamvr launch command wrapper that patches various parts of steamvr -to fix bugs. - -For SteamVR Beta 2.19.1, build 26389675. - -Use at your own risk! -""" - -import hashlib -import os -import stat -import subprocess -import sys -import tempfile -from dataclasses import dataclass -from pathlib import Path - -NAME = Path(__file__).name -MARKER = b"Valve, please fix." - - -@dataclass(frozen=True) -class Edit: - offset: int - replacement: bytes - - -@dataclass(frozen=True) -class Patch: - original_hash: bytes - patched_hash: bytes - edits: tuple[Edit, ...] - - def apply_to(self, data: bytes) -> bytes: - payload = data.removesuffix(MARKER) - digest = sha256(payload) - if digest == self.patched_hash: - return payload + MARKER - if digest != self.original_hash: - if payload != data: - raise LookupError( - "already patched with a another set of patches: verify file integrity and restart" - ) - raise LookupError("unrecognized build: this wrapper may need updating") - result = bytearray(payload) - for edit in self.edits: - end = edit.offset + len(edit.replacement) - if edit.offset < 0 or end > len(result): - raise ValueError("patch extends outside the file") - result[edit.offset : end] = edit.replacement - if sha256(result) != self.patched_hash: - raise ValueError("patched file checksum mismatch") - return bytes(result) + MARKER - - -def patch( - original_hash: str, patched_hash: str, edits: tuple[tuple[int, str], ...] -) -> Patch: - return Patch( - bytes.fromhex(original_hash), - bytes.fromhex(patched_hash), - tuple( - Edit(offset, bytes.fromhex(replacement)) for offset, replacement in edits - ), - ) - - -PATCHES = { - "bin/linux64/vrcompositor": patch( - "4c380adc27832db9afaf83e446080354338bcb4c96f0f0cc561c90cd4d79168f", - "a6d073c17489919e838c3d2ac4941a923bc18172a372f3c32bae3433e95f9dbc", - ( - # Wait for GPU to finish using its resources (see #952) - (0x404941, "84c00f842f570000eb06"), - (0x404951, "488dbbe00d0000e90a570000"), - (0x40A05B, "e830db0600"), - (0x40A060, "e9dca8ffff9090"), - # Stop reusing GPU work identifier across frames - # (allows above patch to work reliably) - (0x41D224, "31c09090909090"), - (0x41DB39, "31c09090909090"), - (0x41DB74, "909090909090"), - # Fix shader descriptor texture array counts (should be 3, not 1) - # for bindings 9, 17, and 20. Affected shaders: - # - distort_none_ps - # - distort_none_ps_layered - # - distort_panorama_ps - # - distort_panorama_solid_color_ps - # - distort_ps_achromatic_nd - (0x404961, "498b5500b80300000089424ceb22"), - (0x404991, "89828c0000008982a4000000eb12"), - (0x4049B1, "4c8bbd18feffffe902200100"), - (0x4169B8, "e9a4dffeff9090"), - ), - ), - "bin/linux64/vrserver": patch( - "a7c9246f8fe36749caa0bfe3984efbb5fa7dbbb28e5263a5c1744f5ba6b0c10b", - "b14314f1fbfe8acb164583853e32c9a6870317bb9cbb278d9cea21862aaebd8f", - ( - # Remove logger calls in forked processes (can cause a deadlock and - # make the process linger preventing SteamVR from starting again) - (0x2C1386, "9090909090"), - (0x2C1577, "9090909090"), - (0x2C160C, "9090909090"), - (0x2C1726, "9090909090"), - (0x2C17FF, "9090909090"), - (0x2C1699, "9090909090"), - ), - ), - "drivers/vrlink/bin/linux64/driver_vrlink.so": patch( - "a33d1da2d1c9851a61e19b87ffc89bbbb6e8b1e503e189e3e19bccd2e6cd3b7f", - "c7d8adbbcd44d577f7891c15e53d6000eaf0b257fde642b09ff286bd00be9468", - ( - # Keep increasing (mod(2^16)) the ACK cutoff instead of clearing it - # (#965) - (0xD91DD, "89836c0d000090909090"), - # Wait for encoder to finish before reconfiguring it - # (ulVideoTimeline == ulTimelineSequence) (#965) - (0x13C2D2, "83bb4c090000007458e9702c0000"), - (0x13C333, "488b8328090000e9ec210000"), - (0x13E524, "e9a9ddffff9090"), - # Clean up failed encoded frames so the above fix doesn't wait - # forever when these trigger a reset - (0x13D735, "e927000000"), - (0x13D761, "488b9d70ffffff4c01f3e9d6feffff"), - # Reset tracking and encoding when starting a new streaming session - (0x1129F1, "57488b7f184885ff740feb04"), - (0x112A01, "488b076a015eff50285fe91013feff"), - (0x11A251, "4c89a30804000031c0894308c3"), - (0x11A297, "e8b5ffffff6690"), - (0x11A2AA, "e84287ffff"), - # Ignore stale tracking IDs and repeated older attempts - (0x11B301, "89d02b47084189c6c1e004"), - (0x11B30C, "0f8853050000"), - (0x11B614, "0f841719fdff"), - (0xECF31, "410fb644240988430ee926e90200"), - ), - ), -} - - -def sha256(data): - return hashlib.sha256(data).digest() - - -def notify(message): - print(f"{NAME}: {message}", file=sys.stderr) - try: - subprocess.run( - ["notify-send", NAME, message], - check=False, - timeout=3, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - except (OSError, subprocess.TimeoutExpired): - pass - - -def replace(path, data): - mode = stat.S_IMODE(path.stat().st_mode) - with tempfile.NamedTemporaryFile( - dir=path.parent, prefix=path.name + ".", delete=False - ) as f: - temporary = Path(f.name) - try: - f.write(data) - f.flush() - os.fchmod(f.fileno(), mode) - os.fsync(f.fileno()) - os.replace(temporary, path) - finally: - temporary.unlink(missing_ok=True) - - -def main(command): - diagnostics = [] - if not command: - print(f"Usage: {NAME} command [args...]", file=sys.stderr) - return 2 - try: - root = Path.cwd() - pending = [] - for relative, patch in PATCHES.items(): - path = (root / relative).resolve() - try: - original = path.read_bytes() - except FileNotFoundError as e: - diagnostics.append(f"Skipped `{relative}`: {e}") - continue - try: - updated = patch.apply_to(original) - except ValueError as e: - raise ValueError(f"Patching `{relative}` failed: {e}") from e - except LookupError as e: - diagnostics.append(f"Skipped `{relative}`: {e}") - continue - if updated != original: - pending.append((path, updated)) - for path, updated in pending: - replace(path, updated) - diagnostics.append(f"Patched {path}") - if diagnostics: - notify("\n".join(diagnostics)) - except (OSError, ValueError) as e: - notify("\n".join([f"Encountered an error: {e}", *diagnostics])) - return 1 - os.execvp(command[0], command) - - -if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) diff --git a/steamvr/patches.toml b/steamvr/patches.toml new file mode 100644 index 0000000..a4e618e --- /dev/null +++ b/steamvr/patches.toml @@ -0,0 +1,82 @@ +# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +# SPDX-License-Identifier: CC0-1.0 + +target = "Beta 2.19.1, build 26389675" + +[[targets]] +path = "bin/linux64/vrcompositor" +sha256 = "4c380adc27832db9afaf83e446080354338bcb4c96f0f0cc561c90cd4d79168f" +edits = [ + # Wait for GPU to finish using its resources (see #952) + [0x404941, "84c00f842f570000eb06"], + [0x404951, "488dbbe00d0000e90a570000"], + [0x40a05b, "e830db0600"], + [0x40a060, "e9dca8ffff9090"], + # Stop reusing GPU work identifier across frames (allows above patch to + # work reliably) + [0x41d224, "31c09090909090"], + [0x41db39, "31c09090909090"], + [0x41db74, "909090909090"], + # Fix shader descriptor texture array counts (should be 3, not 1) for + # bindings 9, 17, and 20. Affected shaders: + # - distort_none_ps + # - distort_none_ps_layered + # - distort_panorama_ps + # - distort_panorama_solid_color_ps + # - distort_ps_achromatic_nd + [0x404961, "498b5500b80300000089424ceb22"], + [0x404991, "89828c0000008982a4000000eb12"], + [0x4049b1, "4c8bbd18feffffe902200100"], + [0x4169b8, "e9a4dffeff9090"], +] +[targets.compiled] +sources = ["image-wait/trampoline.S", "image-wait/image_wait.c"] +imports = { append_wait = 0x465dd0, resume_transition = 0x40e9d7 } +hooks = [ + # Make the compositor wait for frames submitted by the application to + # complete rendering before trying to encode them etc. (#1046) + { address = 0x40e9d0, length = 7, target = "input_wait" }, +] + +[[targets]] +path = "bin/linux64/vrserver" +sha256 = "a7c9246f8fe36749caa0bfe3984efbb5fa7dbbb28e5263a5c1744f5ba6b0c10b" +edits = [ + # Remove logger calls in forked processes (can cause a deadlock and make + # the process linger preventing SteamVR from starting again) + [0x2c1386, "9090909090"], + [0x2c1577, "9090909090"], + [0x2c160c, "9090909090"], + [0x2c1726, "9090909090"], + [0x2c17ff, "9090909090"], + [0x2c1699, "9090909090"], +] + +[[targets]] +path = "drivers/vrlink/bin/linux64/driver_vrlink.so" +sha256 = "a33d1da2d1c9851a61e19b87ffc89bbbb6e8b1e503e189e3e19bccd2e6cd3b7f" +edits = [ + # Keep increasing (mod(2^16)) the ACK cutoff instead of clearing it (#965) + [0xd91dd, "89836c0d000090909090"], + # Wait for encoder to finish before reconfiguring it + # (ulVideoTimeline == ulTimelineSequence) (#965) + [0x13c2d2, "83bb4c090000007458e9702c0000"], + [0x13c333, "488b8328090000e9ec210000"], + [0x13e524, "e9a9ddffff9090"], + # Clean up failed encoded frames so the above fix doesn't wait forever when + # these trigger a reset + [0x13d735, "e927000000"], + [0x13d761, "488b9d70ffffff4c01f3e9d6feffff"], + # Reset tracking and encoding when starting a new streaming session + [0x1129f1, "57488b7f184885ff740feb04"], + [0x112a01, "488b076a015eff50285fe91013feff"], + [0x11a251, "4c89a30804000031c0894308c3"], + [0x11a297, "e8b5ffffff6690"], + [0x11a2aa, "e84287ffff"], + # Ignore stale tracking IDs and repeated older attempts + [0x11b301, "89d02b47084189c6c1e004"], + [0x11b30c, "0f8853050000"], + [0x11b614, "0f841719fdff"], + [0xecf31, "410fb644240988430ee926e90200"], +] + @@ -0,0 +1,182 @@ +# SPDX-FileCopyrightText: 2026 Tomasz Kramkowski <tomasz@kramkow.ski> +# SPDX-License-Identifier: CC0-1.0 + +version = 1 +revision = 5 +requires-python = ">=3.13" + +[[package]] +name = "annotated-types" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, +] + +[[package]] +name = "capstone" +version = "5.0.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/01/6516910f546fbb996068207b9dd0229b14bc8dae223114d5e0e27d3cad11/capstone-5.0.9.tar.gz", hash = "sha256:0429af292ddc604d3c9344696807e281d5e728db029f00e6a4ea9e3bff1aac9e", size = 2947300, upload-time = "2026-05-28T16:05:57.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/f3/43f4dbfc36c7740d512621a9d42b276ac21bb6ca7cb974d4c8a19060d387/capstone-5.0.9-py3-none-macosx_10_9_universal2.whl", hash = "sha256:58a874d9a6cb15122135b1385a782e5b54c8a4d9396161b6498465046d2e0442", size = 2195391, upload-time = "2026-05-28T16:05:45.515Z" }, + { url = "https://files.pythonhosted.org/packages/70/74/d00de02c62ce864c6b43796c524516ea064e3e6fc1327d452f78270e8323/capstone-5.0.9-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:8c4a1d7134d7d5290e5d77066500385f2c7a4bf66835ccbfde5180042d508b3c", size = 1190392, upload-time = "2026-05-28T16:05:46.953Z" }, + { url = "https://files.pythonhosted.org/packages/42/9a/4eecfbc94961cda70301fd13de69042be1943bee456e37bc1369840601b1/capstone-5.0.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fc269c5082e17d7f9f265cd6980e2fdc6ab572df913951d76db0357e06b8001d", size = 1202361, upload-time = "2026-05-28T16:05:48.1Z" }, + { url = "https://files.pythonhosted.org/packages/45/d9/dbeadb9fcd461e608ce79b1cb647fed6baa812fa6e1c68b0ba5ef81291af/capstone-5.0.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41772ad0c71aa5d3e97d541e802593c2e71fb94ac7e20d4202f97ce2a3eb8ed5", size = 1461016, upload-time = "2026-05-28T16:05:49.554Z" }, + { url = "https://files.pythonhosted.org/packages/d1/39/17747862222bb062e86b501f1f148d5ff589b77908b080d30f7f085cbfb7/capstone-5.0.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:273fd8d747d2e35c88f91450be51a603ecfaafb00d96d9f315dcb8689c86193e", size = 1485316, upload-time = "2026-05-28T16:05:50.721Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/9a694f60f5b26612039d975c1c84d2ad8fd0ad839b0b1058dbe68dd32b0b/capstone-5.0.9-py3-none-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5871b57e987d5bf23b531fbe868211b74ad750fa9b830a3d3cdc4956bc0ae3", size = 1483221, upload-time = "2026-05-28T16:05:51.925Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/c178ac241892bb3668b633065bdbe6dc719c2aa54668d68a8e23a6753525/capstone-5.0.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:69064219c814ad64af35469a7c4ddd19b730cacb96a1a796435ccde0e1567d05", size = 1459140, upload-time = "2026-05-28T16:05:53.182Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f3/3865a0371603994f8bf521595b3072080bfe1b715f8bd080988d026dd2a1/capstone-5.0.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0cc3b1ec319ab0530efc6b181491b77dd3373c81b6dbcf2f05a80e3d8dd61d5e", size = 1488205, upload-time = "2026-05-28T16:05:54.31Z" }, + { url = "https://files.pythonhosted.org/packages/09/a1/39a575aa27a35b43b0ad93065a1803dba39038060eccfb29031d82e3fb2c/capstone-5.0.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cfec34e6e01472fe60850c87c5bea9918c274fa2a605b510e6489bf87c0c9960", size = 1488865, upload-time = "2026-05-28T16:05:55.603Z" }, + { url = "https://files.pythonhosted.org/packages/50/e6/6f06fdb6a9ed32b2f7cd9c036b92d5324112c3ef7080f2c71efc367d40dd/capstone-5.0.9-py3-none-win_amd64.whl", hash = "sha256:732cedbbb56d42e723f14d7af6387f1454194a820b4b96b56d1e53f865ef85d0", size = 1273459, upload-time = "2026-05-28T16:05:56.73Z" }, +] + +[[package]] +name = "pydantic" +version = "2.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/d5/b4867fa7c7f04c34997d7d8aad744f64d210bb99d67cf18bf3fb1eea1aed/pydantic-2.14.2.tar.gz", hash = "sha256:fb7b2db6cefe8414dd612edeede49c4c433acb0432b0f92b95024e45f66c505d", size = 911596, upload-time = "2026-10-29T16:37:12.476Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/01/c71a71258b58659c72504e5fd65b451f6e36e34dc931d0b237ded213c814/pydantic-2.14.2-py3-none-any.whl", hash = "sha256:7efbf7507b937931cd32a5755acb00ea1cf5ccb1d4e8067a5850d5069e382118", size = 490403, upload-time = "2026-10-29T16:37:10.181Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.50.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/8e/23ec1021834e35041fbb2f10946141e6acdfd39379c45cc4d65a4e8a1d98/pydantic_core-2.50.2.tar.gz", hash = "sha256:1b0e8dc3932cf4e9ed5727570325cacc716b9409ffc794a221c0ccc72e8eb775", size = 518711, upload-time = "2026-10-29T16:34:57.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/fc/ac3ae9d934932ce3c380e7fe53e01372b49ef5dcb49db56f1a1f005b4d8c/pydantic_core-2.50.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:93eb41d626d9a2749a678823c70bba8c25033cef474ae8001fd7621adbbd8437", size = 2222463, upload-time = "2026-10-29T16:32:32.125Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0b/7605223654ed4e777d05668e914d32b476b1fa2bb756bff9820fe270456b/pydantic_core-2.50.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c539d452cba22f250103c7d1c00cbe9038eb705e657476e701c43374efd9f2e", size = 2093995, upload-time = "2026-10-29T16:32:33.496Z" }, + { url = "https://files.pythonhosted.org/packages/b4/29/4a1a653cfedd3f5e1ccd3b1e919fe324fb45607d12c30fdd7ca5af16ba8d/pydantic_core-2.50.2-cp313-cp313-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:53bd25e8ab54c02bb4c55d4848c1d36f75f3b9a7356e83179fe27794d9e9e6c4", size = 2253178, upload-time = "2026-10-29T16:32:34.77Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f5/03ce5ed01c98b60272cdee8ac1abb819b64ef347717c11d96215e32d9952/pydantic_core-2.50.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f142cd663d1148ed32993ed9a58e5c67ea312f446a01a6c13a956945aafc9f23", size = 2113894, upload-time = "2026-10-29T16:32:39.241Z" }, + { url = "https://files.pythonhosted.org/packages/d3/bc/f7644474ac0faad9a7fdf5179239893b21339169b8b4f89e3075c4be16b2/pydantic_core-2.50.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:126f158474346f457c0759c29c1a6334acd5f7f99dd272bb27d19314f3d81b4d", size = 2198248, upload-time = "2026-10-29T16:32:42.357Z" }, + { url = "https://files.pythonhosted.org/packages/c2/8f/3d3a1c1d313e02774f6c034eb1910eb6e795922e66e81f0f053f98afe866/pydantic_core-2.50.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0b94a515f44f1ebcb25395bcdba506c4cdaf6d3fcb0466b9658521b41e5ff057", size = 2368505, upload-time = "2026-10-29T16:32:43.755Z" }, + { url = "https://files.pythonhosted.org/packages/03/33/38fb4545c00ce2bfebc2a0ffd99f7f580f1e1efb8db3b2faf88def640435/pydantic_core-2.50.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a55c1163e829a08707b4a92c8ee02a5a3a3847ced74aa4da94e3e975ffe1ec63", size = 2451704, upload-time = "2026-10-29T16:32:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/79/be/b559a1ceaff8b1d4581733d47d2ffa785635ac05716eae4c964466aac6e8/pydantic_core-2.50.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:31a1fb3ffbd1195b24490f671d12623dd4c9bfa56d6af0a299fa43b516134f5b", size = 2241760, upload-time = "2026-10-29T16:32:46.382Z" }, + { url = "https://files.pythonhosted.org/packages/2d/7c/3c8ab7cb45e4dc7687a4f16a953fee14d2eca18dd90d6be8bd470ef06962/pydantic_core-2.50.2-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:83d6f42ae6eba44548a1a3d4f900a5b1500fdd14f09f04754a8800ab0d544432", size = 2254115, upload-time = "2026-10-29T16:32:47.818Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ad/3f3471206224cdac2af3492a3f7c724c47a82f5f8850ff364c2e36023497/pydantic_core-2.50.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9fcde11462e0faab4c2a2ae8cd4516f7dd8239eec583bd88c5524d8064ad2d0c", size = 2288598, upload-time = "2026-10-29T16:32:49.404Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1e/2070b388b1d8633cfb72e3964ac3c95aaeb195c240209894ff386847b494/pydantic_core-2.50.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:848984e231190ae49bc8b5229e64d2a6fba4231dc6046e1b6b28ddc060bc7601", size = 2475116, upload-time = "2026-10-29T16:32:51.414Z" }, + { url = "https://files.pythonhosted.org/packages/3d/96/e96ff19527c73b9aaddce2a4424006dc15439fd89ef2d39641048c5ee85a/pydantic_core-2.50.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8772c2c0091e6fc2d0499151793d31bf811826986b0727052935a95104845544", size = 2478926, upload-time = "2026-10-29T16:32:52.856Z" }, + { url = "https://files.pythonhosted.org/packages/35/10/2fefc082eb46f959e80735e859161305c0c2fbb3d5d20ac5547c49a84a3b/pydantic_core-2.50.2-cp313-cp313-win32.whl", hash = "sha256:bd6ecae08c0d871cf93b6f0d4ee76b06c65be44ea22311981a022b2f1948cf9d", size = 2108354, upload-time = "2026-10-29T16:32:54.276Z" }, + { url = "https://files.pythonhosted.org/packages/6f/dd/14eeec394ec1422f9c4e9743cd746f4502232a7c65eb235d1a03729d94ab/pydantic_core-2.50.2-cp313-cp313-win_amd64.whl", hash = "sha256:e5c801bdf3cc92274b8fc2c7023c8751e5ae81260c009ac558ff55ce712a2d45", size = 2250556, upload-time = "2026-10-29T16:32:55.396Z" }, + { url = "https://files.pythonhosted.org/packages/75/b7/a014acf1ae4b4aa7d32ff957e2250698637daa3a29448444180360b92284/pydantic_core-2.50.2-cp313-cp313-win_arm64.whl", hash = "sha256:fd9aa71a800c87c0f7ab53fa38009169a23a82c685989626ca74a4bd483e1b67", size = 2162633, upload-time = "2026-10-29T16:32:56.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/64/fc525bb981a9f287cfa34346e7ed1d5ef7c043c2b9b517f54c6136032d10/pydantic_core-2.50.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:efad0bdc67d3b119c3bab6b94d341a27333c2bb89ed75dfa182a4a06cd0ea329", size = 2226414, upload-time = "2026-10-29T16:32:58.559Z" }, + { url = "https://files.pythonhosted.org/packages/db/68/315701095f25687938ab79d263a6d939337456c2840fe69038fd38000338/pydantic_core-2.50.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6ffb4dce3ecb2cd563aa30f9eb5088a1cf1f4f688b90cbb374fa96e49fc50b02", size = 2095041, upload-time = "2026-10-29T16:32:59.873Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4b/efd3abf31aa24bc97b311735f418886f947b4f193df02a1977643963b03e/pydantic_core-2.50.2-cp314-cp314-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:150f6e101f575c21a4ecfed1be1c05bb89a0ad49cb9c846ed377583bd71b32c2", size = 2257635, upload-time = "2026-10-29T16:33:01.096Z" }, + { url = "https://files.pythonhosted.org/packages/2d/6c/146b36eed4de8115a0fda76840703e75f5f8b36aeb3af86976bf094d1a17/pydantic_core-2.50.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1db9cab6197e797824f0479dd2ae5fd2e272aa61c61b7faa5a6e450ca7a0d3b7", size = 2116807, upload-time = "2026-10-29T16:33:02.692Z" }, + { url = "https://files.pythonhosted.org/packages/fb/94/db006ea79bbe405cc38b25899319c7d461ff1813cbdfe6aa90a65ed1dfff/pydantic_core-2.50.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:1b9f652e17a13bbc09b45f74c3c326cca72059ce4dcdef687fcb04389db413a9", size = 2201037, upload-time = "2026-10-29T16:33:03.942Z" }, + { url = "https://files.pythonhosted.org/packages/41/f9/ea2bc1ec13a67af4a63c9a887fa0aa6ef4ade9018e2bb412d4fc80110e19/pydantic_core-2.50.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:823b222c4fc861a82c650998a6117ecbd150cc71bf8b78f9a31bf3f48c456108", size = 2374904, upload-time = "2026-10-29T16:33:05.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/8e/26a6a075301f0e563646037ea69bef8c63911d38fbd257f94cec69cdf985/pydantic_core-2.50.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:783abdfac21247d33bec40aa683d26bba30d61655c40de38b2decc4797aabf92", size = 2457626, upload-time = "2026-10-29T16:33:07.271Z" }, + { url = "https://files.pythonhosted.org/packages/9d/a2/8eeabaf1d394f0d3b9a27129ac21556eabee6f2ddcb418069ef4037e00b0/pydantic_core-2.50.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b29b7c5adf4b2be7c941f7f76432aa3269cd5052ff8610376b8ece24d7b02c51", size = 2245505, upload-time = "2026-10-29T16:33:08.824Z" }, + { url = "https://files.pythonhosted.org/packages/bd/54/449778cd397e2682b30d1bcfab0872f6ac36702b59cc326296b7f4f14928/pydantic_core-2.50.2-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4ac996e42a93d80122a23edce44e02c6b9c4913f5c6198a077da31475341a39a", size = 2259563, upload-time = "2026-10-29T16:33:10.107Z" }, + { url = "https://files.pythonhosted.org/packages/26/c2/2f65691d25488ee0f3c17861d079e8ccb7817955522d07cd3c3970d30af5/pydantic_core-2.50.2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:99e1d482138a71902415ba375bb4627679a5fefd3bd07803c3a8f37632f4981d", size = 2290470, upload-time = "2026-10-29T16:33:11.273Z" }, + { url = "https://files.pythonhosted.org/packages/9f/32/aa7c40234cd24536afa0895ff1d26f9f15638004e25242c5193ce053b96b/pydantic_core-2.50.2-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:a53d5f11cd6d15569ba3e53f3498077f0d5e633f6ec604d2ea613e895017ebee", size = 2477272, upload-time = "2026-10-29T16:33:12.771Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/c347c68e040cf93d8487a8f4fc28f2768312834c7d21c301538f416f9955/pydantic_core-2.50.2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23385c4626f8b5a7792aa381ea573d9432cfbf4c21375a0a5eed2a4fb64e0e58", size = 2484352, upload-time = "2026-10-29T16:33:14.485Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8e/72556126c8b6154fe7acaf757b96b2daa37aeffc3a517deb76e272f5ad09/pydantic_core-2.50.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:9a8a62281fd9c6a373b3fd3d473685e6838d43341eecde5f47729748e2c11ee3", size = 1491226, upload-time = "2026-10-29T16:33:15.603Z" }, + { url = "https://files.pythonhosted.org/packages/5b/66/004c85511812964a36b3535af022c5c0dfcb760a31e4905b96310e8dd6c8/pydantic_core-2.50.2-cp314-cp314-win32.whl", hash = "sha256:18f008ef58cd30782f81b6e0119e20b7e884edcefbc2d370162ebe033f6e5c99", size = 2113134, upload-time = "2026-10-29T16:33:16.87Z" }, + { url = "https://files.pythonhosted.org/packages/a3/5f/dcd3df20ab2a7279d42a8415fc184e8e77bcf9aa3c9ac3e938c1300a242c/pydantic_core-2.50.2-cp314-cp314-win_amd64.whl", hash = "sha256:b62c89545d21d930c333208fd548e7b4f262228ba3a322fd75c987a75ab11f31", size = 2253407, upload-time = "2026-10-29T16:33:18.47Z" }, + { url = "https://files.pythonhosted.org/packages/4e/fb/0685895f5d199b215011dc4eb8ccea032a64ec23dfa88eeb5acca939c2d2/pydantic_core-2.50.2-cp314-cp314-win_arm64.whl", hash = "sha256:6e731bf5a5e8e25398544d7709e39068ec4230b0177e4db7fa955692fd415ba3", size = 2160456, upload-time = "2026-10-29T16:33:19.782Z" }, + { url = "https://files.pythonhosted.org/packages/84/2f/4c8f67efe62837a508b74cf910904f73029cb211371351c5ceb36d56e85e/pydantic_core-2.50.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:33c6800ab9563adaa0d7893e9cd8f5b972ef1e6a23df70498e05c24f3fcec5e1", size = 2217558, upload-time = "2026-10-29T16:33:21.323Z" }, + { url = "https://files.pythonhosted.org/packages/ce/73/e0ef88cfbcca5409f57751289bc20be63a58af596a36e94b8cd795e59747/pydantic_core-2.50.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f81df206b937d0dca5efb38c25fc99037d82dbcd3740e34c3f81997d2322ac1c", size = 2074335, upload-time = "2026-10-29T16:33:22.658Z" }, + { url = "https://files.pythonhosted.org/packages/7b/cc/47844a3179ebce27064e13619594b3fc1a9b4509d8dec1e3960c5f87f5bb/pydantic_core-2.50.2-cp314-cp314t-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:21ea3b00f1e87c66f3f9e92f9f6fec20495ac0e60d7d2e68ba1fc49739d7cf40", size = 2264201, upload-time = "2026-10-29T16:33:24.953Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d1/30e9d3c86d81884ee3199560ddfa2c1a66a8dc0736441748eae43064abbc/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9eff570402175c7d5fff8497d5a9c05580664a79bb66c07b8f205d17f69e580b", size = 2094353, upload-time = "2026-10-29T16:33:26.739Z" }, + { url = "https://files.pythonhosted.org/packages/d4/8f/392a5999480d0bcab4539db0dafa93265152396fd63e87be4d24e13d6d82/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:240fec32a74af0979f7253532859b3fd209ad5eea822416bb010929dba445c3d", size = 2203119, upload-time = "2026-10-29T16:33:28.154Z" }, + { url = "https://files.pythonhosted.org/packages/cb/80/6e4e276a3fde5500224a39150e06a4f134f136165908be69402720718a36/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92acf02c770b9d5c4e9a3e8d64e9d95ae26456a86ebb7b7cf64a97f899e5871b", size = 2392881, upload-time = "2026-10-29T16:33:29.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/d5/5eb56c6317ab4abf33ef9519a50bfac0c96f7f408bad1bc36da6f27d43e4/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1ac5b99bc7b0a68ec64f5f75a6828a7bab5535c8df0cdc07901deb50e0815910", size = 2457290, upload-time = "2026-10-29T16:33:30.587Z" }, + { url = "https://files.pythonhosted.org/packages/7b/9e/f1fe9384bb37e4e728c51c79151808deacf7b2502e245f50b57c85eeb199/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:713d7c33c0e231e3786eb0ec95885814dd65d5f3a09754399b6acbf5e9365f11", size = 2241568, upload-time = "2026-10-29T16:33:32.04Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f1/cffc4f8456c35690bcb65a8f2304afbf2f519766345a28aba553d5a55cc8/pydantic_core-2.50.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:61e13100b30d8d25fc0d1f0297014b32941bf0975a3584bd08f3adebb68d1ead", size = 2257185, upload-time = "2026-10-29T16:33:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/84/34/ccd4df1cc39cb1e42fb7ec86a0dc0a23e8479de04d3012ca6b0bf2279a03/pydantic_core-2.50.2-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:523fe6574aea50f456cbc301c1cdddcbf237939f7657f9af99b4f0f71942b59c", size = 2299384, upload-time = "2026-10-29T16:33:34.799Z" }, + { url = "https://files.pythonhosted.org/packages/83/60/404b4b0c416a61867ee642cdc1450bca4d8a9a10e5f5f21e1ecaad05fdc8/pydantic_core-2.50.2-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:6c25b8826be85714b27dafe908d3461e1b09e3e31c0279a7029040c832c781ef", size = 2479384, upload-time = "2026-10-29T16:33:36.106Z" }, + { url = "https://files.pythonhosted.org/packages/ca/0d/57c81418a2fdfde0578c71396df773965adebd4421d9985a0f8dcbe9ec4d/pydantic_core-2.50.2-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a59db03b80a156a6fbeae41096866d1cb89cdf31031ba44c768a804538880636", size = 2504616, upload-time = "2026-10-29T16:33:37.423Z" }, + { url = "https://files.pythonhosted.org/packages/08/66/dec78e28a604f3f4985f5a795d4da3e5fef78b0921aeb7f614ed7c780a32/pydantic_core-2.50.2-cp314-cp314t-win32.whl", hash = "sha256:0d090ae76d5b09c003600c7779b3adcf3d1b49268f616c5c51d844e54c8e318d", size = 2093179, upload-time = "2026-10-29T16:33:38.705Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ea/79d9ee60648463804145d44b08f1e7e2474427d9704e3331d814483908b3/pydantic_core-2.50.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9b01a092955c1c7530196ad526755cff4f2d11f37ca9a6fc9ebe37ca49a48977", size = 2220575, upload-time = "2026-10-29T16:33:39.929Z" }, + { url = "https://files.pythonhosted.org/packages/eb/01/aa6a7bd017c3228a41942da4fd788b86fb4aad9677212a4737a0c157fe74/pydantic_core-2.50.2-cp314-cp314t-win_arm64.whl", hash = "sha256:4ea01f30e119f1ec7a2034e85c40f491d469896aef02cd6436fd9292fe1be3fb", size = 2160866, upload-time = "2026-10-29T16:33:41.237Z" }, + { url = "https://files.pythonhosted.org/packages/4b/7f/ec2974015b4be5e50266ce732b8942d3d0b3bff751ace8900999c6577bce/pydantic_core-2.50.2-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:a3317630e2791f56071b82f89bb71624146125e4311e6643f708c6ba744b5ac5", size = 2230936, upload-time = "2026-10-29T16:33:43.656Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ff/af99a09eddc348c1d0744c0df28dfcd4839ca497067f0fa2168208b41b1b/pydantic_core-2.50.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:f73930dacad5e611fe019e18df6f53c30629b1843d038d303b8dec86a32493bd", size = 2100409, upload-time = "2026-10-29T16:33:44.946Z" }, + { url = "https://files.pythonhosted.org/packages/84/75/8db3c46f02d6005277e5c2ff5933c85e7e1eb8798aa334c676c828650c3f/pydantic_core-2.50.2-cp315-cp315-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:e2e6240a0a5ad847017b3d65187877fdb7a11d5a58d43c30736f9f523478913b", size = 2259728, upload-time = "2026-10-29T16:33:46.461Z" }, + { url = "https://files.pythonhosted.org/packages/ee/9d/98c8d524921ca24e5c8a81a4b8f12209ac6ab05d0dd58b374e357da0ffc5/pydantic_core-2.50.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1fdf5b52ed22f1404c2433d0b46df2d86d9befb742d0b9b4042a2325c43cdb97", size = 2121771, upload-time = "2026-10-29T16:33:47.952Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ea/68c3b1fa919946012c940dea88e94e33e7dde46abe3e3d57053793c9bab8/pydantic_core-2.50.2-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:4e9392ddf67fe97ab9889f796d468c9908a8754b8b6c7520eb03a65012b9a1e7", size = 2196328, upload-time = "2026-10-29T16:33:49.131Z" }, + { url = "https://files.pythonhosted.org/packages/eb/9b/cfb6312a5b65b02eab6bc00fb0d258d0e2e49468dab29b430e7cb49b2ceb/pydantic_core-2.50.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:48e944091b7bff233920bed8a60b0ed6c96c73d3c5836bf51c4b9a4bfb58e4ca", size = 2380152, upload-time = "2026-10-29T16:33:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/41/5a/7fcced1d84ea294200abfa93e488f36d00732101d17bf5e1f1389e66de26/pydantic_core-2.50.2-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:91c92856b846aa690ea6239ffd6c5003c5e3e296a69095570bf4efee5bfa0bdc", size = 2462299, upload-time = "2026-10-29T16:33:51.884Z" }, + { url = "https://files.pythonhosted.org/packages/e6/d0/dd0e355694094f0045922f4da60ac9bc7a314babc494e46089fb00640fd3/pydantic_core-2.50.2-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5299296ff5cafcb69851fd652de8f9268a0bb036bcbedbfcaa94cf743baf3986", size = 2249999, upload-time = "2026-10-29T16:33:53.333Z" }, + { url = "https://files.pythonhosted.org/packages/3a/79/f5f4b36434604d22aacb7152667f87a470e68847c89b136c68a278cf409d/pydantic_core-2.50.2-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:d3d049b557875f9d15230eec8a731749a7a873d8ed6a051903b05bdf46e0e65f", size = 2262745, upload-time = "2026-10-29T16:33:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/14/21/a4c47a286745857758a4bb8d4d5e260c736a7bd6fab5da618f5711d1a3f1/pydantic_core-2.50.2-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b0b25d647036972c2951059046cb498f72b7af5a2def27bf8e5f44c3067ab7dd", size = 2293663, upload-time = "2026-10-29T16:33:56.132Z" }, + { url = "https://files.pythonhosted.org/packages/0d/db/25330e609fd219ce16d0a089af1b4b62095f01595dbe6972f7a7dfc313b9/pydantic_core-2.50.2-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:8835566b3e94026da3ce2249f9ee35c9d2ebe4cd131f6a39bfc82708d3010a46", size = 2473247, upload-time = "2026-10-29T16:33:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/53/16/704ca88b82156dc485da2dfb11d5d52a10b9ef15a294639c0aa5ebd1643e/pydantic_core-2.50.2-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:3fbcd402e4b607d4b8de5d4591215f31f042af03f9d09e58f693a6221e32833f", size = 2490536, upload-time = "2026-10-29T16:33:59.297Z" }, + { url = "https://files.pythonhosted.org/packages/50/34/1fdd0eebee9f8b6c4ec3c2490f49dc59a652c8c5a7fa0cbcaada0d91cda9/pydantic_core-2.50.2-cp315-cp315-win32.whl", hash = "sha256:8431ecb131489cbdaf6f51f7ade47475f73aff4f1912552f75ba19a6cfe233a8", size = 2117204, upload-time = "2026-10-29T16:34:00.514Z" }, + { url = "https://files.pythonhosted.org/packages/24/6b/702adc91486fb96ba55675d58ee15ddc38fb48553fb35e26fd005e303206/pydantic_core-2.50.2-cp315-cp315-win_amd64.whl", hash = "sha256:04cf672960b8c4fc28bb159c8cdd101fbd311c520237a5c0360448b851adfb41", size = 2261064, upload-time = "2026-10-29T16:34:01.773Z" }, + { url = "https://files.pythonhosted.org/packages/36/0d/7f117432b8614526aa0d96fadd2b7fb7393156ddf2caf26a6fe606e6cfb6/pydantic_core-2.50.2-cp315-cp315-win_arm64.whl", hash = "sha256:36917e29c0b815d6b940bc91560fcf453b5b85d55c579796373f0ea11d1c111a", size = 2161783, upload-time = "2026-10-29T16:34:02.966Z" }, + { url = "https://files.pythonhosted.org/packages/45/1c/2ba7a34fe8da4c83ec200fb9f85ab4d54b961be08367922743623f987849/pydantic_core-2.50.2-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:bd1fe69056a19c377a6c6de2c1240fb8676ba0512a969965efb486510c5e0f1f", size = 2223184, upload-time = "2026-10-29T16:34:04.214Z" }, + { url = "https://files.pythonhosted.org/packages/47/01/f71a8da1f53a5be1787d2a97ba6fd9adba115305c7f15ea0bf733ed8336c/pydantic_core-2.50.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:9b2383933b1dd7a8c93e02f99ebfd305f1286057172483b8e6898bed3fef69d3", size = 2078174, upload-time = "2026-10-29T16:34:05.426Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0a/847e1f5427372692f6eb5c38261fed6b8cedf379e37d9f6134d48b9c8ee9/pydantic_core-2.50.2-cp315-cp315t-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:a1b4d60bad57d6e0e7cd30cc4d1dc646d258dd6dfb7935ce76c7dd72dd1e98b0", size = 2268813, upload-time = "2026-10-29T16:34:06.733Z" }, + { url = "https://files.pythonhosted.org/packages/f5/bb/12966dc82b5e9c347981658b52e24349b6242902b644725c00ee7bdf4267/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:93bedc68c4138088ea3c042189c01d2945f530f0dcc36e1ea02df8418bd4f1cd", size = 2098331, upload-time = "2026-10-29T16:34:08.69Z" }, + { url = "https://files.pythonhosted.org/packages/d9/fe/340416b7ebe664524e7ada16ad2e3e069888b168010d75bda6a502bf2bf8/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:d684ff87d00de2f6abcbbfa9b0fdce8b513dd799a900809dcfa7eb62ad69832a", size = 2197432, upload-time = "2026-10-29T16:34:10.091Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4d/425162c3d88af0d190c4dc4f940402cc6dd44d934359a37f50da6b7e4672/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bb6d49f00e2babd40af4889d6b9f4bf50879f522dd4e2e22cb705cac5e0c5e8e", size = 2399300, upload-time = "2026-10-29T16:34:11.462Z" }, + { url = "https://files.pythonhosted.org/packages/2b/fe/2882bddf2e2038c8f41fa69f16ddeb6eeb1c002cc0cc0eb2157f73bb34e9/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6d47b2d0e00e030e0ebdb08115a37da7294e7a6d196d134a77ed7ee6c142284b", size = 2462672, upload-time = "2026-10-29T16:34:12.79Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ac/d7e6bfc6a95573647d1a699e19228dd99914ae6aab79a2bec293a038e980/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:95838be125133fa314753f8cc5168f7c41e3a9dd5eef5383dbbbeed511bd4853", size = 2246293, upload-time = "2026-10-29T16:34:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/74/64/97473ccaeb4ae5eb338162bd8479af121484e67f7078499730e3ee0a5592/pydantic_core-2.50.2-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:2d4a043c820363359e40ac96270e1915e5376e116be1f59cee0701775de3e376", size = 2259507, upload-time = "2026-10-29T16:34:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/48/15/78a9b7a6a0d0679ce1e46cdd38ff95afd18dd2d6a01ed87c80a01bc003f7/pydantic_core-2.50.2-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:57bccd3e1128736c792094da1feac6da46ec3feb1957ef649a9b3e4f8de6fa04", size = 2303550, upload-time = "2026-10-29T16:34:17.162Z" }, + { url = "https://files.pythonhosted.org/packages/39/ba/b697173f82e99cdcf5ec6654b2edb66a00f2f92ef9d9a168861fca3fcded/pydantic_core-2.50.2-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:24120c644322a35b5eb765416ac8442214c45689e255f5fc6e761e7e7c743c8b", size = 2474919, upload-time = "2026-10-29T16:34:18.841Z" }, + { url = "https://files.pythonhosted.org/packages/71/10/986be76723784ae87f9664658fae39df340eaa21681cea95b6339d2e1b3e/pydantic_core-2.50.2-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:b1adf2f37521b578c438f6ec61d1c1d0c20b076863ddd5d8b6f3ab658857db3f", size = 2509095, upload-time = "2026-10-29T16:34:20.16Z" }, + { url = "https://files.pythonhosted.org/packages/5f/0b/38dbd38c67de7642f87348da70122b89ca4c5fe8227cfdd5cc00c1cdb454/pydantic_core-2.50.2-cp315-cp315t-win32.whl", hash = "sha256:d79aaa468193c1bed6b0560d3c5c9f9e369e3efb0b128b38a2537e2d51d42773", size = 2096972, upload-time = "2026-10-29T16:34:21.715Z" }, + { url = "https://files.pythonhosted.org/packages/7a/df/154baf8413b08d6daf2f5dec211c303ec6bed50c032717f354f8dd85c994/pydantic_core-2.50.2-cp315-cp315t-win_amd64.whl", hash = "sha256:cc488daac19e6f5292180fac5e0ef7efffabeef4e096af9a17a75a7d3d6781f6", size = 2226659, upload-time = "2026-10-29T16:34:23.177Z" }, + { url = "https://files.pythonhosted.org/packages/b5/17/eea7a1e7d98a79ef11b6d31748b70c58f8723f87d7ee42edbb6a101f6245/pydantic_core-2.50.2-cp315-cp315t-win_arm64.whl", hash = "sha256:91315aea7adb51821d4a3378ede953bfdd76e3bf71551de4a82d7638cef7dc02", size = 2166387, upload-time = "2026-10-29T16:34:24.433Z" }, +] + +[[package]] +name = "pyelftools" +version = "0.33" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/11/767522582afab1b884d277de0e6e011640cb9d7292a38694b4b1a1df1ae8/pyelftools-0.33.tar.gz", hash = "sha256:660d82dcbeb8e83d1702bd97f223f761625da06111c0cc988eac6b8ab0c1b61f", size = 15068655, upload-time = "2026-05-29T12:56:22.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/2a/f9697576603dae937727827505a6126a066affb227034e77e6f9068910da/pyelftools-0.33-py3-none-any.whl", hash = "sha256:f215ad5f47d3f1373a21496a6c9e0707c622840d0622f23ff7ce08678b020036", size = 201178, upload-time = "2026-05-29T12:56:20.587Z" }, +] + +[[package]] +name = "steamvr-patches" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "capstone" }, + { name = "pydantic" }, + { name = "pyelftools" }, +] + +[package.metadata] +requires-dist = [ + { name = "capstone", specifier = ">=5,<6" }, + { name = "pydantic", specifier = ">=2.12,<3" }, + { name = "pyelftools", specifier = ">=0.32,<1" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, +] |
