diff --git a/AxibugEmuOnline.Client/Assets/Aya.UNes.asmdef b/AxibugEmuOnline.Client/Assets/Aya.UNes.asmdef new file mode 100644 index 0000000..dd9bddb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Aya.UNes.asmdef @@ -0,0 +1,13 @@ +{ + "name": "AxibugEmuOnline.Client.UNES", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Aya.UNes.asmdef.meta b/AxibugEmuOnline.Client/Assets/Aya.UNes.asmdef.meta new file mode 100644 index 0000000..11f4d03 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Aya.UNes.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3fe77f1eed9fc0847a86648f644fe815 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/LICENSE.md b/AxibugEmuOnline.Client/Assets/LICENSE.md new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/LICENSE.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/AxibugEmuOnline.Client/Assets/LICENSE.md.meta b/AxibugEmuOnline.Client/Assets/LICENSE.md.meta new file mode 100644 index 0000000..f0c8496 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8b0730be05596c544807c9992169cb68 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins.meta b/AxibugEmuOnline.Client/Assets/Plugins.meta new file mode 100644 index 0000000..926dfd4 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 69eb869b06dab98439f07da4549fb7cb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/Google.Protobuf.dll b/AxibugEmuOnline.Client/Assets/Plugins/Google.Protobuf.dll new file mode 100644 index 0000000..7663c31 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/Google.Protobuf.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/Google.Protobuf.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/Google.Protobuf.dll.meta new file mode 100644 index 0000000..794d80b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/Google.Protobuf.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2c61c2e567bd4e146b4c09946e815a55 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ClientNetworkNet.Standard2.dll b/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ClientNetworkNet.Standard2.dll new file mode 100644 index 0000000..c3c4a90 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ClientNetworkNet.Standard2.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ClientNetworkNet.Standard2.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ClientNetworkNet.Standard2.dll.meta new file mode 100644 index 0000000..fa5284c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ClientNetworkNet.Standard2.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 324deac494a24a7499801349c7908062 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ServerNetwork.Standard2.dll b/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ServerNetwork.Standard2.dll new file mode 100644 index 0000000..f525fd2 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ServerNetwork.Standard2.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ServerNetwork.Standard2.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ServerNetwork.Standard2.dll.meta new file mode 100644 index 0000000..07f0863 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/HaoYueNet.ServerNetwork.Standard2.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bba3137572a9a52478aa945ce387a192 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.ClientCore.Standard2.dll b/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.ClientCore.Standard2.dll new file mode 100644 index 0000000..58d737f Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.ClientCore.Standard2.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.ClientCore.Standard2.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.ClientCore.Standard2.dll.meta new file mode 100644 index 0000000..dbe316c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.ClientCore.Standard2.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 208c5b5b9ca9139418ec61b89a1516ed +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.DataHelper.dll b/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.DataHelper.dll new file mode 100644 index 0000000..46e3832 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.DataHelper.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.DataHelper.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.DataHelper.dll.meta new file mode 100644 index 0000000..2b3ed26 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/NoSugarNet.DataHelper.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1d83e3c67ee8b794c97b5876ece7a16e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/System.Buffers.dll b/AxibugEmuOnline.Client/Assets/Plugins/System.Buffers.dll new file mode 100644 index 0000000..c0970c0 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/System.Buffers.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/System.Buffers.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/System.Buffers.dll.meta new file mode 100644 index 0000000..efe13f4 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/System.Buffers.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a3e5397cebfd55d4d950fc3d7fcafd8f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/System.Memory.dll b/AxibugEmuOnline.Client/Assets/Plugins/System.Memory.dll new file mode 100644 index 0000000..1e6aef8 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/System.Memory.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/System.Memory.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/System.Memory.dll.meta new file mode 100644 index 0000000..b03e202 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/System.Memory.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7079d9113f8bfda4db8a78fffdd414a9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/System.Numerics.Vectors.dll b/AxibugEmuOnline.Client/Assets/Plugins/System.Numerics.Vectors.dll new file mode 100644 index 0000000..a808165 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/System.Numerics.Vectors.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/System.Numerics.Vectors.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/System.Numerics.Vectors.dll.meta new file mode 100644 index 0000000..6e2fab3 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/System.Numerics.Vectors.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 677f64bf577a1724699f45841c5bcb04 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll b/AxibugEmuOnline.Client/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll new file mode 100644 index 0000000..b17135b Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/AxibugEmuOnline.Client/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll.meta b/AxibugEmuOnline.Client/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll.meta new file mode 100644 index 0000000..378070a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c92fb77cb5b063541bd04cb8a496013f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Resources.meta b/AxibugEmuOnline.Client/Assets/Resources.meta new file mode 100644 index 0000000..dfacc8d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 481be8b9dd1218749a75bf1bd4612c04 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Resources/Mario.bytes b/AxibugEmuOnline.Client/Assets/Resources/Mario.bytes new file mode 100644 index 0000000..878ef21 Binary files /dev/null and b/AxibugEmuOnline.Client/Assets/Resources/Mario.bytes differ diff --git a/AxibugEmuOnline.Client/Assets/Resources/Mario.bytes.meta b/AxibugEmuOnline.Client/Assets/Resources/Mario.bytes.meta new file mode 100644 index 0000000..78d1e2d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Resources/Mario.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 040fea71e1812ce45bd2b72c8ad2e2ae +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Resources/Texture.meta b/AxibugEmuOnline.Client/Assets/Resources/Texture.meta new file mode 100644 index 0000000..deb6af3 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Resources/Texture.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6b52033f7628c4346a6dd0b2806e75ee +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Resources/Texture/RenderTexture.renderTexture b/AxibugEmuOnline.Client/Assets/Resources/Texture/RenderTexture.renderTexture new file mode 100644 index 0000000..99ddfbc --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Resources/Texture/RenderTexture.renderTexture @@ -0,0 +1,39 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!84 &8400000 +RenderTexture: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: RenderTexture + m_ImageContentsHash: + serializedVersion: 2 + Hash: 00000000000000000000000000000000 + m_ForcedFallbackFormat: 4 + m_DownscaleFallback: 0 + m_IsAlphaChannelOptional: 0 + serializedVersion: 5 + m_Width: 256 + m_Height: 240 + m_AntiAliasing: 1 + m_MipCount: -1 + m_DepthStencilFormat: 92 + m_ColorFormat: 8 + m_MipMap: 0 + m_GenerateMips: 1 + m_SRGB: 0 + m_UseDynamicScale: 0 + m_BindMS: 0 + m_EnableCompatibleFormat: 1 + m_TextureSettings: + serializedVersion: 2 + m_FilterMode: 1 + m_Aniso: 0 + m_MipBias: 0 + m_WrapU: 1 + m_WrapV: 1 + m_WrapW: 1 + m_Dimension: 2 + m_VolumeDepth: 1 + m_ShadowSamplingMode: 2 diff --git a/AxibugEmuOnline.Client/Assets/Resources/Texture/RenderTexture.renderTexture.meta b/AxibugEmuOnline.Client/Assets/Resources/Texture/RenderTexture.renderTexture.meta new file mode 100644 index 0000000..d8035c9 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Resources/Texture/RenderTexture.renderTexture.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6645567e4c11d9447b1aee2406f681c5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime.meta b/AxibugEmuOnline.Client/Assets/Runtime.meta new file mode 100644 index 0000000..079a930 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ca1eb074eb7f66946b0d974b802d7b8d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Controller.meta b/AxibugEmuOnline.Client/Assets/Runtime/Controller.meta new file mode 100644 index 0000000..52daa48 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 87f4900ceb2e3b24caa8eec8df6b34e7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Controller/IController.cs b/AxibugEmuOnline.Client/Assets/Runtime/Controller/IController.cs new file mode 100644 index 0000000..0548145 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Controller/IController.cs @@ -0,0 +1,15 @@ +using UnityEngine; + +namespace AxibugEmuOnline.Client.UNES.Controller +{ + public interface IController + { + void Strobe(bool on); + + int ReadState(); + + void PressKey(KeyCode keyCode); + + void ReleaseKey(KeyCode keyCode); + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Controller/IController.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Controller/IController.cs.meta new file mode 100644 index 0000000..097e178 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Controller/IController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fde4f0c3566240b4fa824b147b019ff7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Controller/KeyConfig.cs b/AxibugEmuOnline.Client/Assets/Runtime/Controller/KeyConfig.cs new file mode 100644 index 0000000..811c060 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Controller/KeyConfig.cs @@ -0,0 +1,22 @@ +using System; +using UnityEngine; + +namespace AxibugEmuOnline.Client.UNES.Controller +{ + [Serializable] + public class KeyConfig + { + public KeyCode Up = KeyCode.UpArrow; + public KeyCode Down = KeyCode.DownArrow; + public KeyCode Left = KeyCode.LeftArrow; + public KeyCode Right = KeyCode.RightArrow; + + public KeyCode A = KeyCode.A; + public KeyCode B = KeyCode.S; + + public KeyCode Start = KeyCode.Alpha1; + public KeyCode Select = KeyCode.Alpha2; + + public KeyCode Debug = KeyCode.P; + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Controller/KeyConfig.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Controller/KeyConfig.cs.meta new file mode 100644 index 0000000..4e8d851 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Controller/KeyConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e51d74785e24fa4f972a1eb002a81d7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Controller/NesController.cs b/AxibugEmuOnline.Client/Assets/Runtime/Controller/NesController.cs new file mode 100644 index 0000000..5b2126e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Controller/NesController.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using UnityEngine; +using AxibugEmuOnline.Client.UNES.Input; + +namespace AxibugEmuOnline.Client.UNES.Controller +{ + public class NesController : IController + { + private readonly UNESBehaviour _nes; + private int _data; + private int _serialData; + private bool _strobing; + + public bool Debug; + + public KeyConfig Config => _nes.KeyConfig; + + // bit: 7 6 5 4 3 2 1 0 + // button: A B Select Start Up Down Left + + private readonly Dictionary _keyMapping; + + public NesController(UNESBehaviour nse) + { + _nes = nse; + + _keyMapping = new Dictionary + { + {Config.A, 7}, + {Config.B, 6}, + {Config.Select, 5}, + {Config.Start, 4}, + {Config.Up, 3}, + {Config.Down, 2}, + {Config.Left, 1}, + {Config.Right, 0}, + }; + } + + public void Strobe(bool on) + { + _serialData = _data; + _strobing = on; + } + + public int ReadState() + { + int ret = ((_serialData & 0x80) > 0).AsByte(); + if (!_strobing) + { + _serialData <<= 1; + _serialData &= 0xFF; + } + + return ret; + } + + public void PressKey(KeyCode keyCode) + { + if (keyCode == Config.Debug) + { + Debug ^= true; + } + + if (!_keyMapping.ContainsKey(keyCode)) + { + return; + } + + _data |= 1 << _keyMapping[keyCode]; + } + + public void ReleaseKey(KeyCode keyCode) + { + if (!_keyMapping.ContainsKey(keyCode)) + { + return; + } + + _data &= ~(1 << _keyMapping[keyCode]); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Controller/NesController.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Controller/NesController.cs.meta new file mode 100644 index 0000000..0582652 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Controller/NesController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a6001b2b26e33144b77fe0b02e21dc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core.meta new file mode 100644 index 0000000..62ce764 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2f857291907b21142bc251146119762c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/Addressable.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/Addressable.cs new file mode 100644 index 0000000..770cb10 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/Addressable.cs @@ -0,0 +1,62 @@ +using System.Runtime.CompilerServices; + +namespace AxibugEmuOnline.Client.UNES +{ + public abstract class Addressable + { + public delegate uint ReadDelegate(uint address); + + public delegate void WriteDelegate(uint address, byte val); + + protected readonly Emulator _emulator; + protected readonly ReadDelegate[] _readMap; + protected readonly WriteDelegate[] _writeMap; + protected readonly uint _addressSize; + + protected Addressable(Emulator emulator, uint addressSpace) + { + _emulator = emulator; + _addressSize = addressSpace; + _readMap = new ReadDelegate[addressSpace + 1]; + _writeMap = new WriteDelegate[addressSpace + 1]; + } + + protected virtual void InitializeMemoryMap() + { + _readMap.Fill(address => 0); + + // Some games write to addresses not mapped and expect to continue afterwards + _writeMap.Fill((address, val) => { }); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint ReadByte(uint address) + { + address &= _addressSize; + return _readMap[address](address); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteByte(uint address, uint val) + { + address &= _addressSize; + _writeMap[address](address, (byte)val); + } + + public void MapReadHandler(uint start, uint end, CPU.ReadDelegate func) + { + for (uint i = start; i <= end; i++) + { + _readMap[i] = func; + } + } + + public void MapWriteHandler(uint start, uint end, CPU.WriteDelegate func) + { + for (uint i = start; i <= end; i++) + { + _writeMap[i] = func; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/Addressable.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/Addressable.cs.meta new file mode 100644 index 0000000..84e599c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/Addressable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8649a70e245ed1744aa620cb28504fb5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Core.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Core.cs new file mode 100644 index 0000000..b68c527 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Core.cs @@ -0,0 +1,81 @@ +using System; + +namespace AxibugEmuOnline.Client.UNES +{ + sealed partial class CPU + { + public enum InterruptType + { + NMI, + IRQ, + RESET + } + + private readonly uint[] _interruptHandlerOffsets = { 0xFFFA, 0xFFFE, 0xFFFC }; + private readonly bool[] _interrupts = new bool[2]; + + public void Initialize() + { + A = 0; + X = 0; + Y = 0; + SP = 0xFD; + P = 0x24; + + PC = ReadWord(_interruptHandlerOffsets[(int) InterruptType.RESET]); + } + + public void Reset() + { + SP -= 3; + F.InterruptsDisabled = true; + } + + public void TickFromPPU() + { + if (Cycle-- > 0) return; + ExecuteSingleInstruction(); + } + + public void ExecuteSingleInstruction() + { + for (var i = 0; i < _interrupts.Length; i++) + { + if (_interrupts[i]) + { + PushWord(PC); + Push(P); + PC = ReadWord(_interruptHandlerOffsets[i]); + F.InterruptsDisabled = true; + _interrupts[i] = false; + return; + } + } + + _currentInstruction = NextByte(); + + Cycle += _opCodeDefs[_currentInstruction].Cycles; + + ResetInstructionAddressingMode(); + // if (_numExecuted > 10000 && PC - 1 == 0xFF61) + // if(_emulator.Controller.debug || 0x6E00 <= PC && PC <= 0x6EEF) + // Console.WriteLine($"{(PC - 1).ToString("X4")} {_currentInstruction.ToString("X2")} {opcodeNames[_currentInstruction]}\t\t\tA:{A.ToString("X2")} X:{X.ToString("X2")} Y:{Y.ToString("X2")} P:{P.ToString("X2")} SP:{SP.ToString("X2")}"); + + var op = _opCodes[_currentInstruction]; + if (op == null) + { + throw new ArgumentException(_currentInstruction.ToString("X2")); + } + + op(); + } + + public void TriggerInterrupt(InterruptType type) + { + if (!F.InterruptsDisabled || type == InterruptType.NMI) + { + _interrupts[(int)type] = true; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Core.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Core.cs.meta new file mode 100644 index 0000000..5f375dc --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Core.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63ec605962bce0e4899b19d49d4ed747 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.IORegisters.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.IORegisters.cs new file mode 100644 index 0000000..039a0d1 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.IORegisters.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace AxibugEmuOnline.Client.UNES +{ + sealed partial class CPU + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteIoRegister(uint reg, byte val) + { + switch (reg) + { + case 0x4014: // OAM DMA + _emulator.PPU.PerformDMA(val); + break; + case 0x4016: + _emulator.Controller.Strobe(val == 1); + break; + } + + if (reg <= 0x401F) + { + return; // APU write + } + + throw new NotImplementedException($"{reg:X4} = {val:X2}"); + } + + public uint ReadIORegister(uint reg) + { + switch (reg) + { + case 0x4016: + return (uint) _emulator.Controller.ReadState() & 0x1; + } + return 0x00; + //throw new NotImplementedException(); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.IORegisters.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.IORegisters.cs.meta new file mode 100644 index 0000000..42ddf45 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.IORegisters.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ad8b3c04ef34de48b5cab3029811d9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Instructions.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Instructions.cs new file mode 100644 index 0000000..1d32389 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Instructions.cs @@ -0,0 +1,463 @@ +using System; +using static AxibugEmuOnline.Client.UNES.CPU.AddressingMode; + +namespace AxibugEmuOnline.Client.UNES +{ + public sealed partial class CPU + { + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + public class OpCodeDef : Attribute + { + public int OpCode; + public int Cycles = 1; + public bool PageBoundary; + public bool RMW; + public AddressingMode Mode = None; + } + + [OpCodeDef(OpCode = 0x20, Cycles = 6)] + private void JSR() + { + PushWord(PC + 1); + PC = NextWord(); + } + + [OpCodeDef(OpCode = 0x40, Cycles = 6)] + private void RTI() + { + // TODO: this dummy fetch should happen for all single-byte instructions + NextByte(); + P = Pop(); + PC = PopWord(); + } + + [OpCodeDef(OpCode = 0x60, Cycles = 6)] + private void RTS() + { + NextByte(); + PC = PopWord() + 1; + } + + [OpCodeDef(OpCode = 0xC8, Cycles = 2)] + private void INY() => Y++; + + [OpCodeDef(OpCode = 0x88, Cycles = 2)] + private void DEY() => Y--; + + [OpCodeDef(OpCode = 0xE8, Cycles = 2)] + private void INX() => X++; + + [OpCodeDef(OpCode = 0xCA, Cycles = 2, RMW = true)] + private void DEX() => X--; + + [OpCodeDef(OpCode = 0xA8, Cycles = 2)] + private void TAY() => Y = A; + + [OpCodeDef(OpCode = 0x98, Cycles = 2)] + private void TYA() => A = Y; + + [OpCodeDef(OpCode = 0xAA, Cycles = 2, RMW = true)] + private void TAX() => X = A; + + [OpCodeDef(OpCode = 0x8A, Cycles = 2, RMW = true)] + private void TXA() => A = X; + + [OpCodeDef(OpCode = 0xBA, Cycles = 2)] + private void TSX() => X = SP; + + [OpCodeDef(OpCode = 0x9A, Cycles = 2, RMW = true)] + private void TXS() => SP = X; + + [OpCodeDef(OpCode = 0x08, Cycles = 3)] + private void PHP() => Push(P | BreakSourceBit); + + [OpCodeDef(OpCode = 0x28, Cycles = 4)] + private void PLP() => P = (uint)(Pop() & ~BreakSourceBit); + + [OpCodeDef(OpCode = 0x68, Cycles = 4)] + private void PLA() => A = Pop(); + + [OpCodeDef(OpCode = 0x48, Cycles = 3)] + private void PHA() => Push(A); + + [OpCodeDef(OpCode = 0x24, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x2C, Mode = Absolute, Cycles = 4)] + private void BIT() + { + uint val = AddressRead(); + F.Overflow = (val & 0x40) > 0; + F.Zero = (val & A) == 0; + F.Negative = (val & 0x80) > 0; + } + + private void Branch(bool cond) + { + uint nPC = (uint)(PC + NextSByte() + 1); + if (cond) + { + PC = nPC; + Cycle++; + } + } + + [OpCodeDef(OpCode = 0x4C, Cycles = 3)] + [OpCodeDef(OpCode = 0x6C, Cycles = 5)] + private void JMP() + { + if (_currentInstruction == 0x4C) + { + PC = NextWord(); + } + else if (_currentInstruction == 0x6C) + { + uint off = NextWord(); + // AN INDIRECT JUMP MUST NEVER USE A VECTOR BEGINNING ON THE LAST BYTE OF A PAGE + // + // If address $3000 contains $40, $30FF contains $80, and $3100 contains $50, + // the result of JMP ($30FF) will be a transfer of control to $4080 rather than + // $5080 as you intended i.e. the 6502 took the low byte of the address from + // $30FF and the high byte from $3000. + // + // http://www.6502.org/tutorials/6502opcodes.html + uint hi = (off & 0xFF) == 0xFF ? off - 0xFF : off + 1; + uint oldPC = PC; + PC = ReadByte(off) | (ReadByte(hi) << 8); + + if ((oldPC & 0xFF00) != (PC & 0xFF00)) + { + Cycle += 2; + } + } + else + { + throw new NotImplementedException(); + } + } + + [OpCodeDef(OpCode = 0xB0, Cycles = 2)] + private void BCS() => Branch(F.Carry); + + [OpCodeDef(OpCode = 0x90, Cycles = 2)] + private void BCC() => Branch(!F.Carry); + + [OpCodeDef(OpCode = 0xF0, Cycles = 2)] + private void BEQ() => Branch(F.Zero); + + [OpCodeDef(OpCode = 0xD0, Cycles = 2)] + private void BNE() => Branch(!F.Zero); + + [OpCodeDef(OpCode = 0x70, Cycles = 2)] + private void BVS() => Branch(F.Overflow); + + [OpCodeDef(OpCode = 0x50, Cycles = 2)] + private void BVC() => Branch(!F.Overflow); + + [OpCodeDef(OpCode = 0x10, Cycles = 2)] + private void BPL() => Branch(!F.Negative); + + [OpCodeDef(OpCode = 0x30, Cycles = 2)] + private void BMI() => Branch(F.Negative); + + [OpCodeDef(OpCode = 0x81, Mode = IndirectX, Cycles = 6)] + [OpCodeDef(OpCode = 0x91, Mode = IndirectY, Cycles = 6)] + [OpCodeDef(OpCode = 0x95, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0x99, Mode = AbsoluteY, Cycles = 5)] + [OpCodeDef(OpCode = 0x9D, Mode = AbsoluteX, Cycles = 5)] + [OpCodeDef(OpCode = 0x85, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x8D, Mode = Absolute, Cycles = 4)] + private void STA() => AddressWrite(A); + + [OpCodeDef(OpCode = 0x96, Mode = ZeroPageY, Cycles = 4)] + [OpCodeDef(OpCode = 0x86, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x8E, Mode = Absolute, Cycles = 4)] + private void STX() => AddressWrite(X); + + [OpCodeDef(OpCode = 0x94, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0x84, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x8C, Mode = Absolute, Cycles = 4)] + private void STY() => AddressWrite(Y); + + [OpCodeDef(OpCode = 0x18, Cycles = 2)] + private void CLC() => F.Carry = false; + + [OpCodeDef(OpCode = 0x38, Cycles = 2)] + private void SEC() => F.Carry = true; + + [OpCodeDef(OpCode = 0x58, Cycles = 2)] + private void CLI() => F.InterruptsDisabled = false; + + [OpCodeDef(OpCode = 0x78, Cycles = 2)] + private void SEI() => F.InterruptsDisabled = true; + + [OpCodeDef(OpCode = 0xB8, Cycles = 2)] + private void CLV() => F.Overflow = false; + + [OpCodeDef(OpCode = 0xD8, Cycles = 2)] + private void CLD() => F.DecimalMode = false; + + [OpCodeDef(OpCode = 0xF8, Cycles = 2)] + private void SED() => F.DecimalMode = true; + + [OpCodeDef(OpCode = 0xEA, Cycles = 2)] + [OpCodeDef(OpCode = 0x1A, Cycles = 2)] // Unofficial + [OpCodeDef(OpCode = 0x3A, Cycles = 2)] // Unofficial + [OpCodeDef(OpCode = 0x5A, Cycles = 2)] // Unofficial + [OpCodeDef(OpCode = 0x7A, Cycles = 2)] // Unofficial + [OpCodeDef(OpCode = 0xDA, Cycles = 2)] // Unofficial + [OpCodeDef(OpCode = 0xFA, Cycles = 2)] // Unofficial + private void NOP() { } + + [OpCodeDef(OpCode = 0xA1, Mode = IndirectX, Cycles = 6)] + [OpCodeDef(OpCode = 0xA5, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0xA9, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0xAD, Mode = Absolute, Cycles = 4)] + [OpCodeDef(OpCode = 0xB1, Mode = IndirectY, Cycles = 5, PageBoundary = true)] + [OpCodeDef(OpCode = 0xB5, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0xB9, Mode = AbsoluteY, Cycles = 4, PageBoundary = true)] + [OpCodeDef(OpCode = 0xBD, Mode = AbsoluteX, Cycles = 4, PageBoundary = true)] + private void LDA() => A = AddressRead(); + + [OpCodeDef(OpCode = 0xA0, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0xA4, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0xAC, Mode = Absolute, Cycles = 4)] + [OpCodeDef(OpCode = 0xB4, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0xBC, Mode = AbsoluteX, Cycles = 4, PageBoundary = true)] + private void LDY() => Y = AddressRead(); + + [OpCodeDef(OpCode = 0xA2, Mode = Immediate, Cycles = 2, RMW = true)] + [OpCodeDef(OpCode = 0xA6, Mode = ZeroPage, Cycles = 3, RMW = true)] + [OpCodeDef(OpCode = 0xAE, Mode = Absolute, Cycles = 4, RMW = true)] + [OpCodeDef(OpCode = 0xB6, Mode = ZeroPageY, Cycles = 4, RMW = true)] + [OpCodeDef(OpCode = 0xBE, Mode = AbsoluteY, Cycles = 4, PageBoundary = true, RMW = true)] + private void LDX() => X = AddressRead(); + + [OpCodeDef(OpCode = 0x01, Mode = IndirectX, Cycles = 6)] + [OpCodeDef(OpCode = 0x05, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x09, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0x0D, Mode = Absolute, Cycles = 4)] + [OpCodeDef(OpCode = 0x11, Mode = IndirectY, Cycles = 5, PageBoundary = true)] + [OpCodeDef(OpCode = 0x15, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0x19, Mode = AbsoluteY, Cycles = 4, PageBoundary = true)] + [OpCodeDef(OpCode = 0x1D, Mode = AbsoluteX, Cycles = 4, PageBoundary = true)] + private void ORA() => A |= AddressRead(); + + [OpCodeDef(OpCode = 0x21, Mode = IndirectX, Cycles = 6)] + [OpCodeDef(OpCode = 0x25, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x29, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0x2D, Mode = Absolute, Cycles = 4)] + [OpCodeDef(OpCode = 0x31, Mode = IndirectY, Cycles = 5, PageBoundary = true)] + [OpCodeDef(OpCode = 0x35, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0x39, Mode = AbsoluteY, Cycles = 4, PageBoundary = true)] + [OpCodeDef(OpCode = 0x3D, Mode = AbsoluteX, Cycles = 4, PageBoundary = true)] + private void AND() => A &= AddressRead(); + + [OpCodeDef(OpCode = 0x41, Mode = IndirectX, Cycles = 6)] + [OpCodeDef(OpCode = 0x45, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x49, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0x4D, Mode = Absolute, Cycles = 4)] + [OpCodeDef(OpCode = 0x51, Mode = IndirectY, Cycles = 5, PageBoundary = true)] + [OpCodeDef(OpCode = 0x55, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0x59, Mode = AbsoluteY, Cycles = 4, PageBoundary = true)] + [OpCodeDef(OpCode = 0x5D, Mode = AbsoluteX, Cycles = 4, PageBoundary = true)] + private void EOR() => A ^= AddressRead(); + + [OpCodeDef(OpCode = 0xE1, Mode = IndirectX, Cycles = 6)] + [OpCodeDef(OpCode = 0xE5, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x69, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0xE9, Mode = Immediate, Cycles = 2)] // Official duplicate of $69 + [OpCodeDef(OpCode = 0xEB, Mode = Immediate, Cycles = 2)] // Unofficial duplicate of $69 + [OpCodeDef(OpCode = 0xED, Mode = Absolute, Cycles = 4)] + [OpCodeDef(OpCode = 0xF1, Mode = IndirectY, Cycles = 5, PageBoundary = true)] + [OpCodeDef(OpCode = 0xF5, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0xF9, Mode = AbsoluteY, Cycles = 4, PageBoundary = true)] + [OpCodeDef(OpCode = 0xFD, Mode = AbsoluteX, Cycles = 4, PageBoundary = true)] + private void SBC() => ADCImpl((byte)~AddressRead()); + + [OpCodeDef(OpCode = 0x61, Mode = IndirectX, Cycles = 6)] + [OpCodeDef(OpCode = 0x65, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0x69, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0x6D, Mode = Absolute, Cycles = 4)] + [OpCodeDef(OpCode = 0x71, Mode = IndirectY, Cycles = 5, PageBoundary = true)] + [OpCodeDef(OpCode = 0x75, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0x79, Mode = AbsoluteY, Cycles = 4, PageBoundary = true)] + [OpCodeDef(OpCode = 0x7D, Mode = AbsoluteX, Cycles = 4, PageBoundary = true)] + private void ADC() => ADCImpl(AddressRead()); + + private void ADCImpl(uint val) + { + int nA = (sbyte)A + (sbyte)val + (sbyte)(F.Carry ? 1 : 0); + F.Overflow = nA < -128 || nA > 127; + F.Carry = (A + val + (F.Carry ? 1 : 0)) > 0xFF; + A = (byte)(nA & 0xFF); + } + + [OpCodeDef(OpCode = 0x00, Cycles = 7)] + private void BRK() + { + NextByte(); + Push(P | BreakSourceBit); + F.InterruptsDisabled = true; + PC = ReadByte(0xFFFE) | (ReadByte(0xFFFF) << 8); + } + + [OpCodeDef(OpCode = 0xC1, Mode = IndirectX, Cycles = 6)] + [OpCodeDef(OpCode = 0xC5, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0xC9, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0xCD, Mode = Absolute, Cycles = 4)] + [OpCodeDef(OpCode = 0xD1, Mode = IndirectY, Cycles = 5, PageBoundary = true)] + [OpCodeDef(OpCode = 0xD5, Mode = ZeroPageX, Cycles = 4)] + [OpCodeDef(OpCode = 0xD9, Mode = AbsoluteY, Cycles = 4, PageBoundary = true)] + [OpCodeDef(OpCode = 0xDD, Mode = AbsoluteX, Cycles = 4, PageBoundary = true)] + private void CMP() => CMPImpl(A); + + [OpCodeDef(OpCode = 0xE0, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0xE4, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0xEC, Mode = Absolute, Cycles = 4)] + private void CPX() => CMPImpl(X); + + [OpCodeDef(OpCode = 0xC0, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0xC4, Mode = ZeroPage, Cycles = 3)] + [OpCodeDef(OpCode = 0xCC, Mode = Absolute, Cycles = 4)] + private void CPY() => CMPImpl(Y); + + private void CMPImpl(uint reg) + { + long d = reg - (int)AddressRead(); + + F.Negative = (d & 0x80) > 0 && d != 0; + F.Carry = d >= 0; + F.Zero = d == 0; + } + + [OpCodeDef(OpCode = 0x46, Mode = ZeroPage, Cycles = 5, RMW = true)] + [OpCodeDef(OpCode = 0x4E, Mode = Absolute, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0x56, Mode = ZeroPageX, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0x5E, Mode = AbsoluteX, Cycles = 7, RMW = true)] + [OpCodeDef(OpCode = 0x4A, Mode = Direct, Cycles = 2, RMW = true)] + private void LSR() + { + uint D = AddressRead(); + F.Carry = (D & 0x1) > 0; + D >>= 1; + _F(D); + AddressWrite(D); + } + + [OpCodeDef(OpCode = 0x06, Mode = ZeroPage, Cycles = 5, RMW = true)] + [OpCodeDef(OpCode = 0x0E, Mode = Absolute, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0x16, Mode = ZeroPageX, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0x1E, Mode = AbsoluteX, Cycles = 7, RMW = true)] + [OpCodeDef(OpCode = 0x0A, Mode = Direct, Cycles = 2, RMW = true)] + private void ASL() + { + uint D = AddressRead(); + F.Carry = (D & 0x80) > 0; + D <<= 1; + _F(D); + AddressWrite(D); + } + + [OpCodeDef(OpCode = 0x66, Mode = ZeroPage, Cycles = 5, RMW = true)] + [OpCodeDef(OpCode = 0x6E, Mode = Absolute, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0x76, Mode = ZeroPageX, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0x7E, Mode = AbsoluteX, Cycles = 7, RMW = true)] + [OpCodeDef(OpCode = 0x6A, Mode = Direct, Cycles = 2, RMW = true)] + private void ROR() + { + uint D = AddressRead(); + bool c = F.Carry; + F.Carry = (D & 0x1) > 0; + D >>= 1; + if (c) D |= 0x80; + _F(D); + AddressWrite(D); + } + + [OpCodeDef(OpCode = 0x26, Mode = ZeroPage, Cycles = 5, RMW = true)] + [OpCodeDef(OpCode = 0x2E, Mode = Absolute, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0x36, Mode = ZeroPageX, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0x3E, Mode = AbsoluteX, Cycles = 7, RMW = true)] + [OpCodeDef(OpCode = 0x2A, Mode = Direct, Cycles = 2, RMW = true)] + private void ROL() + { + uint D = AddressRead(); + bool c = F.Carry; + F.Carry = (D & 0x80) > 0; + D <<= 1; + if (c) D |= 0x1; + _F(D); + AddressWrite(D); + } + + [OpCodeDef(OpCode = 0xE6, Mode = ZeroPage, Cycles = 5, RMW = true)] + [OpCodeDef(OpCode = 0xEE, Mode = Absolute, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0xF6, Mode = ZeroPageX, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0xFE, Mode = AbsoluteX, Cycles = 7, RMW = true)] + private void INC() + { + byte D = (byte)(AddressRead() + 1); + _F(D); + AddressWrite(D); + } + + [OpCodeDef(OpCode = 0xC6, Mode = ZeroPage, Cycles = 5, RMW = true)] + [OpCodeDef(OpCode = 0xCE, Mode = Absolute, Cycles = 3, RMW = true)] + [OpCodeDef(OpCode = 0xD6, Mode = ZeroPageX, Cycles = 6, RMW = true)] + [OpCodeDef(OpCode = 0xDE, Mode = AbsoluteX, Cycles = 7, RMW = true)] + private void DEC() + { + byte D = (byte)(AddressRead() - 1); + _F(D); + AddressWrite(D); + } + + #region Unofficial Opcodes + + [OpCodeDef(OpCode = 0x80, Cycles = 2)] + [OpCodeDef(OpCode = 0x82, Cycles = 2)] + [OpCodeDef(OpCode = 0x89, Cycles = 2)] + [OpCodeDef(OpCode = 0xC2, Cycles = 2)] + [OpCodeDef(OpCode = 0xE2, Cycles = 2)] + private void SKB() => NextByte(); // Essentially a 2-byte NOP + + [OpCodeDef(OpCode = 0x0B, Mode = Immediate, Cycles = 2)] + [OpCodeDef(OpCode = 0x2B, Mode = Immediate, Cycles = 2)] + private void ANC() + { + A &= AddressRead(); + F.Carry = F.Negative; + } + + [OpCodeDef(OpCode = 0x4B, Mode = Immediate, Cycles = 2)] + private void ALR() + { + A &= AddressRead(); + F.Carry = (A & 0x1) > 0; + A >>= 1; + _F(A); + } + + [OpCodeDef(OpCode = 0x6B, Mode = Immediate, Cycles = 2)] + private void ARR() + { + A &= AddressRead(); + bool c = F.Carry; + F.Carry = (A & 0x1) > 0; + A >>= 1; + if (c) A |= 0x80; + _F(A); + } + + [OpCodeDef(OpCode = 0xAB, Mode = Immediate, Cycles = 2)] + private void ATX() + { + // This opcode ORs the A register with #$EE, ANDs the result with an immediate + // value, and then stores the result in both A and X. + A |= ReadByte(0xEE); + A &= AddressRead(); + X = A; + } + + #endregion + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Instructions.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Instructions.cs.meta new file mode 100644 index 0000000..7817694 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Instructions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cae8846694d75f04a8214a484032d533 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Memory.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Memory.cs new file mode 100644 index 0000000..5a628b7 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Memory.cs @@ -0,0 +1,165 @@ +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using static AxibugEmuOnline.Client.UNES.CPU.AddressingMode; + +namespace AxibugEmuOnline.Client.UNES +{ + sealed partial class CPU + { + public enum AddressingMode + { + None, + Direct, + Immediate, + ZeroPage, + Absolute, + ZeroPageX, + ZeroPageY, + AbsoluteX, + AbsoluteY, + IndirectX, + IndirectY + } + + private uint? _currentMemoryAddress; + private uint _rmwValue; + + private void ResetInstructionAddressingMode() => _currentMemoryAddress = null; + + private uint _Address() + { + var def = _opCodeDefs[_currentInstruction]; + switch (def.Mode) + { + case Immediate: + return PC++; + case ZeroPage: + return NextByte(); + case Absolute: + return NextWord(); + case ZeroPageX: + return (NextByte() + X) & 0xFF; + case ZeroPageY: + return (NextByte() + Y) & 0xFF; + case AbsoluteX: + var address = NextWord(); + if (def.PageBoundary && (address & 0xFF00) != ((address + X) & 0xFF00)) + { + Cycle += 1; + } + + return address + X; + case AbsoluteY: + address = NextWord(); + if (def.PageBoundary && (address & 0xFF00) != ((address + Y) & 0xFF00)) + { + Cycle += 1; + } + + return address + Y; + case IndirectX: + var off = (NextByte() + X) & 0xFF; + return ReadByte(off) | (ReadByte((off + 1) & 0xFF) << 8); + case IndirectY: + off = NextByte() & 0xFF; + address = ReadByte(off) | (ReadByte((off + 1) & 0xFF) << 8); + if (def.PageBoundary && (address & 0xFF00) != ((address + Y) & 0xFF00)) + { + Cycle += 1; + } + + return (address + Y) & 0xFFFF; + } + throw new NotImplementedException(); + } + + public uint AddressRead() + { + if (_opCodeDefs[_currentInstruction].Mode == Direct) + { + return _rmwValue = A; + } + + if (_currentMemoryAddress == null) + { + _currentMemoryAddress = _Address(); + } + + return _rmwValue = ReadByte((uint)_currentMemoryAddress) & 0xFF; + } + + public void AddressWrite(uint val) + { + if (_opCodeDefs[_currentInstruction].Mode == Direct) + { + A = val; + } + else + { + if (_currentMemoryAddress == null) + { + _currentMemoryAddress = _Address(); + } + + if (_opCodeDefs[_currentInstruction].RMW) + { + WriteByte((uint)_currentMemoryAddress, _rmwValue); + } + + WriteByte((uint)_currentMemoryAddress, val); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private uint ReadWord(uint address) => ReadByte(address) | (ReadByte(address + 1) << 8); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private uint NextByte() => ReadByte(PC++); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private uint NextWord() => NextByte() | (NextByte() << 8); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private sbyte NextSByte() => (sbyte)NextByte(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Push(uint what) + { + WriteByte(0x100 + SP, what); + SP--; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private uint Pop() + { + SP++; + return ReadByte(0x100 + SP); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void PushWord(uint what) + { + Push(what >> 8); + Push(what & 0xFF); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private uint PopWord() => Pop() | (Pop() << 8); + + protected override void InitializeMemoryMap() + { + base.InitializeMemoryMap(); + + MapReadHandler(0x0000, 0x1FFF, address => _ram[address & 0x07FF]); + MapReadHandler(0x2000, 0x3FFF, address => _emulator.PPU.ReadRegister((address & 0x7) - 0x2000)); + MapReadHandler(0x4000, 0x4017, ReadIORegister); + + MapWriteHandler(0x0000, 0x1FFF, (address, val) => _ram[address & 0x07FF] = val); + MapWriteHandler(0x2000, 0x3FFF, (address, val) => _emulator.PPU.WriteRegister((address & 0x7) - 0x2000, val)); + MapWriteHandler(0x4000, 0x401F, WriteIoRegister); + + _emulator.Mapper.InitializeMemoryMap(this); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Memory.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Memory.cs.meta new file mode 100644 index 0000000..e10a02a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Memory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc18416f50415b74e96092e235a95f92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Registers.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Registers.cs new file mode 100644 index 0000000..3179eb6 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Registers.cs @@ -0,0 +1,89 @@ +using System.Runtime.CompilerServices; + +namespace AxibugEmuOnline.Client.UNES +{ + sealed partial class CPU + { + private const int CarryBit = 0x1; + private const int ZeroBit = 0x2; + private const int InterruptDisabledBit = 0x4; + private const int DecimalModeBit = 0x8; + private const int BreakSourceBit = 0x10; + private const int OverflowBit = 0x40; + private const int NegativeBit = 0x80; + + public class CPUFlags + { + public bool Negative; + public bool Overflow; + public bool BreakSource; + public bool DecimalMode; + public bool InterruptsDisabled; + public bool Zero; + public bool Carry; + } + + public readonly CPUFlags F = new CPUFlags(); + + public uint _A, _X, _Y, _SP; + public uint PC; + + public uint A + { + get => _A; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private set => _A = _F(value & 0xFF); + } + + public uint X + { + get => _X; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private set => _X = _F(value & 0xFF); + } + + public uint Y + { + get => _Y; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private set => _Y = _F(value & 0xFF); + } + + public uint SP + { + get => _SP; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private set => _SP = value & 0xFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private uint _F(uint val) + { + F.Zero = (val & 0xFF) == 0; + F.Negative = (val & 0x80) > 0; + return val; + } + + public uint P + { + get => (uint) ((F.Carry.AsByte() << 0) | + (F.Zero.AsByte() << 1) | + (F.InterruptsDisabled.AsByte() << 2) | + (F.DecimalMode.AsByte() << 3) | + (F.BreakSource.AsByte() << 4) | + (1 << 5) | + (F.Overflow.AsByte() << 6) | + (F.Negative.AsByte() << 7)); + set + { + F.Carry = (value & CarryBit) > 0; + F.Zero = (value & ZeroBit) > 0; + F.InterruptsDisabled = (value & InterruptDisabledBit) > 0; + F.DecimalMode = (value & DecimalModeBit) > 0; + F.BreakSource = (value & BreakSourceBit) > 0; + F.Overflow = (value & OverflowBit) > 0; + F.Negative = (value & NegativeBit) > 0; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Registers.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Registers.cs.meta new file mode 100644 index 0000000..508a0d9 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.Registers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57750595bb8e0d24babf00d51e4624bb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.cs new file mode 100644 index 0000000..6bd0564 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace AxibugEmuOnline.Client.UNES +{ + sealed partial class CPU : Addressable + { + private readonly byte[] _ram = new byte[0x800]; + public int Cycle; + private uint _currentInstruction; + + public delegate void OpCode(); + + private readonly OpCode[] _opCodes = new OpCode[256]; + private readonly string[] _opCodeNames = new string[256]; + private readonly OpCodeDef[] _opCodeDefs = new OpCodeDef[256]; + + public CPU(Emulator emulator) : base(emulator, 0xFFFF) + { + InitializeOpCodes(); + InitializeMemoryMap(); + Initialize(); + } + + private void InitializeOpCodes() + { + var opCodeBindings = from opCode in GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + let defs = opCode.GetCustomAttributes(typeof(OpCodeDef), false) + where defs.Length > 0 + select new + { + binding = (OpCode)Delegate.CreateDelegate(typeof(OpCode), this, opCode.Name), + name = opCode.Name, + defs = (from d in defs select (OpCodeDef)d) + }; + + foreach (var opCode in opCodeBindings) + { + foreach (var def in opCode.defs) + { + _opCodes[def.OpCode] = opCode.binding; + _opCodeNames[def.OpCode] = opCode.name; + _opCodeDefs[def.OpCode] = def; + } + } + } + + public void Execute() + { + for (var i = 0; i < 5000; i++) + { + ExecuteSingleInstruction(); + } + + uint w; + ushort x = 6000; + string z = ""; + while ((w = ReadByte(x)) != '\0') + { + z += (char)w; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.cs.meta new file mode 100644 index 0000000..f80ca2b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/CPU.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa724afa42498dd4e80f44c3da2a5881 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/Cartridge.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/Cartridge.cs new file mode 100644 index 0000000..bb1c6bc --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/Cartridge.cs @@ -0,0 +1,66 @@ +using System; + +namespace AxibugEmuOnline.Client.UNES +{ + public class Cartridge + { + public readonly byte[] Raw; + public readonly int PRGROMSize; + public readonly int CHRROMSize; + public readonly int PRGRAMSize; + public readonly int PRGROMOffset; + public readonly int MapperNumber; + public readonly byte[] PRGROM; + public readonly byte[] CHRROM; + public VRAMMirroringMode MirroringMode; + + public enum VRAMMirroringMode + { + Horizontal, Vertical, All, Upper, Lower + } + + public Cartridge(byte[] bytes) + { + Raw = bytes; + + var header = BitConverter.ToInt32(Raw, 0); + if (header != 0x1A53454E) // "NES" + { + throw new FormatException("unexpected header value " + header.ToString("X")); + } + + PRGROMSize = Raw[4] * 0x4000; // 16kb units + CHRROMSize = Raw[5] * 0x2000; // 8kb units + PRGRAMSize = Raw[8] * 0x2000; + + var hasTrainer = (Raw[6] & 0b100) > 0; + PRGROMOffset = 16 + (hasTrainer ? 512 : 0); + + MirroringMode = (Raw[6] & 0x1) > 0 ? VRAMMirroringMode.Vertical : VRAMMirroringMode.Horizontal; + if ((Raw[6] & 0x8) > 0) + { + MirroringMode = VRAMMirroringMode.All; + } + + MapperNumber = (Raw[6] >> 4) | (Raw[7] & 0xF0); + + PRGROM = new byte[PRGROMSize]; + Array.Copy(Raw, PRGROMOffset, PRGROM, 0, PRGROMSize); + + if (CHRROMSize == 0) + { + CHRROM = new byte[0x2000]; + } + else + { + CHRROM = new byte[CHRROMSize]; + Array.Copy(Raw, PRGROMOffset + PRGROMSize, CHRROM, 0, CHRROMSize); + } + } + + public override string ToString() + { + return $"Cartridge{{PRGROMSize={PRGROMSize}, CHRROMSize={CHRROMSize}, PRGROMOffset={PRGROMOffset}, MapperNumber={MapperNumber}}}"; + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/Cartridge.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/Cartridge.cs.meta new file mode 100644 index 0000000..4fcccd7 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/Cartridge.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b45675095efbb224cad44ee16c2587ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/Emulator.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/Emulator.cs new file mode 100644 index 0000000..271c287 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/Emulator.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using AxibugEmuOnline.Client.UNES.Controller; +using AxibugEmuOnline.Client.UNES.Mapper; + +namespace AxibugEmuOnline.Client.UNES +{ + public class Emulator + { + private static readonly Dictionary> Mappers = (from type in Assembly.GetExecutingAssembly().GetTypes() + let def = (MapperDef)type.GetCustomAttributes(typeof(MapperDef), true).FirstOrDefault() + where def != null + select new { def, type }).ToDictionary(a => a.def.Id, a => new KeyValuePair(a.type, a.def)); + + public IController Controller; + + public readonly CPU CPU; + + public readonly PPU PPU; + + public readonly BaseMapper Mapper; + + public readonly Cartridge Cartridge; + + public Emulator(byte[] bytes, IController controller) + { + Cartridge = new Cartridge(bytes); + if (!Mappers.ContainsKey(Cartridge.MapperNumber)) + { + throw new NotImplementedException($"unsupported mapper {Cartridge.MapperNumber}"); + } + + Mapper = (BaseMapper)Activator.CreateInstance(Mappers[Cartridge.MapperNumber].Key, this); + CPU = new CPU(this); + PPU = new PPU(this); + Controller = controller; + + // Load(); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/Emulator.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/Emulator.cs.meta new file mode 100644 index 0000000..49774f0 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/Emulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9c56d6518f6443745a10dd7dda0ac7f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Core.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Core.cs new file mode 100644 index 0000000..5ead01e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Core.cs @@ -0,0 +1,394 @@ +using System; + +namespace AxibugEmuOnline.Client.UNES +{ + partial class PPU + { + private const int GameWidth = 256, GameHeight = 240; + + private uint _bufferPos; + public readonly uint[] RawBitmap = new uint[GameWidth * GameHeight]; + //查找表的Idx网络缓存 + public readonly byte[] RawBitmap_paletteIdxCache = new byte[GameWidth * GameHeight]; + private readonly uint[] _priority = new uint[GameWidth * GameHeight]; + + // TODO: use real chroma/luma decoding + private readonly uint[] _palette = { + 0x7C7C7C, 0x0000FC, 0x0000BC, 0x4428BC, 0x940084, 0xA80020, 0xA81000, 0x881400, + 0x503000, 0x007800, 0x006800, 0x005800, 0x004058, 0x000000, 0x000000, 0x000000, + 0xBCBCBC, 0x0078F8, 0x0058F8, 0x6844FC, 0xD800CC, 0xE40058, 0xF83800, 0xE45C10, + 0xAC7C00, 0x00B800, 0x00A800, 0x00A844, 0x008888, 0x000000, 0x000000, 0x000000, + 0xF8F8F8, 0x3CBCFC, 0x6888FC, 0x9878F8, 0xF878F8, 0xF85898, 0xF87858, 0xFCA044, + 0xF8B800, 0xB8F818, 0x58D854, 0x58F898, 0x00E8D8, 0x787878, 0x000000, 0x000000, + 0xFCFCFC, 0xA4E4FC, 0xB8B8F8, 0xD8B8F8, 0xF8B8F8, 0xF8A4C0, 0xF0D0B0, 0xFCE0A8, + 0xF8D878, 0xD8F878, 0xB8F8B8, 0xB8F8D8, 0x00FCFC, 0xF8D8F8, 0x000000, 0x000000 + }; + + private int _scanLineCount = 261; + private int _cyclesPerLine = 341; + private int _cpuSyncCounter; + private readonly uint[] _scanLineOAM = new uint[8 * 4]; + private readonly bool[] _isSprite0 = new bool[8]; + private int _spriteCount; + + private long _tileShiftRegister; + private uint _currentNameTableByte; + private uint _currentHighTile, _currentLowTile; + private uint _currentColor; + + public void ProcessPixel(int x, int y) + { + ProcessBackgroundForPixel(x, y); + if (F.DrawSprites) + { + ProcessSpritesForPixel(x, y); + } + + if (y != -1) + { + _bufferPos++; + } + } + + private void CountSpritesOnLine(int scanLine) + { + _spriteCount = 0; + var height = F.TallSpritesEnabled ? 16 : 8; + + for (var idx = 0; idx < _oam.Length; idx += 4) + { + var y = _oam[idx] + 1; + if (scanLine >= y && scanLine < y + height) + { + _isSprite0[_spriteCount] = idx == 0; + _scanLineOAM[_spriteCount * 4 + 0] = _oam[idx + 0]; + _scanLineOAM[_spriteCount * 4 + 1] = _oam[idx + 1]; + _scanLineOAM[_spriteCount * 4 + 2] = _oam[idx + 2]; + _scanLineOAM[_spriteCount * 4 + 3] = _oam[idx + 3]; + _spriteCount++; + } + + if (_spriteCount == 8) + { + break; + } + } + } + + private void NextNameTableByte() + { + _currentNameTableByte = ReadByte(0x2000 | (V & 0x0FFF)); + } + + private void NextTileByte(bool hi) + { + var tileIdx = _currentNameTableByte * 16; + var address = F.PatternTableAddress + tileIdx + FineY; + + if (hi) + { + _currentHighTile = ReadByte(address + 8); + } + else + { + _currentLowTile = ReadByte(address); + } + } + + private void NextAttributeByte() + { + // Bless nesdev + var address = 0x23C0 | (V & 0x0C00) | ((V >> 4) & 0x38) | ((V >> 2) & 0x07); + _currentColor = (ReadByte(address) >> (int)((CoarseX & 2) | ((CoarseY & 2) << 1))) & 0x3; + } + + private void ShiftTileRegister() + { + for (var x = 0; x < 8; x++) + { + uint palette = ((_currentHighTile & 0x80) >> 6) | ((_currentLowTile & 0x80) >> 7); + _tileShiftRegister |= (palette + _currentColor * 4) << ((7 - x) * 4); + _currentLowTile <<= 1; + _currentHighTile <<= 1; + } + } + + private void ProcessBackgroundForPixel(int cycle, int scanLine) + { + if (cycle < 8 && !F.DrawLeftBackground || !F.DrawBackground && scanLine != -1) + { + // Maximally sketchy: if current address is in the PPU palette, then it draws that palette entry if rendering is disabled + // Otherwise, it draws $3F00 (universal bg color) + // https://www.romhacking.net/forum/index.php?topic=20554.0 + // Don't know if any game actually uses it, but a test ROM I wrote unexpectedly showed this + // corner case + + //RawBitmap[_bufferPos] = _palette[ReadByte(0x3F00 + ((F.BusAddress & 0x3F00) == 0x3F00 ? F.BusAddress & 0x001F : 0)) & 0x3F]; + byte pIdx = (byte)(ReadByte(0x3F00 + ((F.BusAddress & 0x3F00) == 0x3F00 ? F.BusAddress & 0x001F : 0)) & 0x3F); + //RawBitmap[_bufferPos] = _palette[pIdx]; + RawBitmap_paletteIdxCache[_bufferPos] = pIdx; + return; + } + + var paletteEntry = (uint)(_tileShiftRegister >> 32 >> (int)((7 - X) * 4)) & 0x0F; + if (paletteEntry % 4 == 0) paletteEntry = 0; + + if (scanLine != -1) + { + _priority[_bufferPos] = paletteEntry; + //RawBitmap[_bufferPos] = _palette[ReadByte(0x3F00u + paletteEntry) & 0x3F]; + byte pIdx = (byte)(ReadByte(0x3F00u + paletteEntry) & 0x3F); + //RawBitmap[_bufferPos] = _palette[pIdx]; + RawBitmap_paletteIdxCache[_bufferPos] = pIdx; + } + } + + private void ProcessSpritesForPixel(int x, int scanLine) + { + for (var idx = _spriteCount * 4 - 4; idx >= 0; idx -= 4) + { + var spriteX = _scanLineOAM[idx + 3]; + var spriteY = _scanLineOAM[idx] + 1; + + // Don't draw this sprite if... + if (spriteY == 0 || // it's located at y = 0 + spriteY > 239 || // it's located past y = 239 ($EF) + x >= spriteX + 8 || // it's behind the current dot + x < spriteX || // it's ahead of the current dot + x < 8 && !F.DrawLeftSprites) // it's in the clip area, and clipping is enabled + { + continue; + } + + // amusingly enough, the PPU's palette handling is basically identical + // to that of the Gameboy / Gameboy Color, so I've sort of just copy/pasted + // handling code wholesale from my GBC emulator at + // https://github.com/Xyene/Nitrous-Emulator/blob/master/src/main/java/nitrous/lcd/LCD.java#L642 + var tileIdx = _scanLineOAM[idx + 1]; + if (F.TallSpritesEnabled) + { + tileIdx &= ~0x1u; + } + + tileIdx *= 16; + + var attribute = _scanLineOAM[idx + 2] & 0xE3; + + var palette = attribute & 0x3; + var front = (attribute & 0x20) == 0; + var flipX = (attribute & 0x40) > 0; + var flipY = (attribute & 0x80) > 0; + + var px = (int) (x - spriteX); + var line = (int) (scanLine - spriteY); + + var tableBase = F.TallSpritesEnabled ? (_scanLineOAM[idx + 1] & 1) * 0x1000 : F.SpriteTableAddress; + + if (F.TallSpritesEnabled) + { + if (line >= 8) + { + line -= 8; + if (!flipY) + { + tileIdx += 16; + } + + flipY = false; + } + + if (flipY) + { + tileIdx += 16; + } + } + + // here we handle the x and y flipping by tweaking the indices we are accessing + var logicalX = flipX ? 7 - px : px; + var logicalLine = flipY ? 7 - line : line; + + var address = (uint) (tableBase + tileIdx + logicalLine); + + // this looks bad, but it's about as readable as it's going to get + var color = (uint) ( + ( + ( + ( + // fetch upper bit from 2nd bit plane + ReadByte(address + 8) & (0x80 >> logicalX) + ) >> (7 - logicalX) + ) << 1 // this is the upper bit of the color number + ) | + ( + ( + ReadByte(address) & (0x80 >> logicalX) + ) >> (7 - logicalX) + )); // << 0, this is the lower bit of the color number + + if (color > 0) + { + var backgroundPixel = _priority[_bufferPos]; + // Sprite 0 hits... + if (!(!_isSprite0[idx / 4] || // do not occur on not-0 sprite + x < 8 && !F.DrawLeftSprites || // or if left clipping is enabled + backgroundPixel == 0 || // or if bg pixel is transparent + F.Sprite0Hit || // or if it fired this frame already + x == 255)) // or if x is 255, "for an obscure reason related to the pixel pipeline" + { + F.Sprite0Hit = true; + } + + if (F.DrawBackground && (front || backgroundPixel == 0)) + { + if (scanLine != -1) + { + //RawBitmap[_bufferPos] = _palette[ReadByte(0x3F10 + palette * 4 + color) & 0x3F]; + + byte pIdx = (byte)(ReadByte(0x3F10 + palette * 4 + color) & 0x3F); + RawBitmap[_bufferPos] = _palette[pIdx]; + RawBitmap_paletteIdxCache[_bufferPos] = pIdx; + } + } + } + } + } + + public void ProcessFrame() + { + RawBitmap.Fill(0u); + RawBitmap_paletteIdxCache.Fill((byte)0); + _priority.Fill(0u); + _bufferPos = 0; + + for (var i = -1; i < _scanLineCount; i++) + { + ProcessScanLine(i); + } + } + + public void ProcessScanLine(int line) + { + for (var i = 0; i < _cyclesPerLine; i++) + { + ProcessCycle(line, i); + } + } + + private int _cpuClocksSinceVBL; + private int _ppuClocksSinceVBL; + + public void ProcessCycle(int scanLine, int cycle) + { + var visibleCycle = 1 <= cycle && cycle <= 256; + var prefetchCycle = 321 <= cycle && cycle <= 336; + var fetchCycle = visibleCycle || prefetchCycle; + + if (F.VBlankStarted) + { + _ppuClocksSinceVBL++; + } + + if (0 <= scanLine && scanLine < 240 || scanLine == -1) + { + if (visibleCycle) + { + ProcessPixel(cycle - 1, scanLine); + } + + // During pixels 280 through 304 of this scanline, the vertical scroll bits are reloaded TODO: if rendering is enabled. + if (scanLine == -1 && 280 <= cycle && cycle <= 304) + { + ReloadScrollY(); + } + + if (fetchCycle) + { + _tileShiftRegister <<= 4; + + // See https://wiki.nesdev.com/w/images/d/d1/Ntsc_timing.png + // Takes 8 cycles for tile to be read, 2 per "step" + switch (cycle & 7) + { + case 1: // NT + NextNameTableByte(); + break; + case 3: // AT + NextAttributeByte(); + break; + case 5: // Tile low + NextTileByte(false); + break; + case 7: // Tile high + NextTileByte(true); + break; + case 0: // 2nd cycle of tile high fetch + if (cycle == 256) + IncrementScrollY(); + else + IncrementScrollX(); + // Begin rendering a brand new tile + ShiftTileRegister(); + break; + } + } + + if (cycle == 257) + { + ReloadScrollX(); + // 257 - 320 + // The tile data for the sprites on the next scanline are fetched here. + // TODO: stagger this over all the cycles as opposed to only on 257 + CountSpritesOnLine(scanLine + 1); + } + } + + // TODO: this is a hack; VBlank should be cleared on dot 1 of the pre-render line, + // but for some reason we're at 2272-2273 CPU clocks at that time + // (i.e., our PPU timing is off somewhere by 6-9 PPU cycles per frame) + if (F.VBlankStarted && _cpuClocksSinceVBL == 2270) + { + F.VBlankStarted = false; + _cpuClocksSinceVBL = 0; + } + + if (cycle == 1) + { + if (scanLine == 241) + { + F.VBlankStarted = true; + if (F.NMIEnabled) + { + _emulator.CPU.TriggerInterrupt(CPU.InterruptType.NMI); + } + } + + // Happens at the same time as 1st cycle of NT byte fetch + if (scanLine == -1) + { + // Console.WriteLine(_ppuClocksSinceVBL); + _ppuClocksSinceVBL = 0; + F.VBlankStarted = false; + F.Sprite0Hit = false; + F.SpriteOverflow = false; + } + } + + _emulator.Mapper.ProcessCycle(scanLine, cycle); + + if (_cpuSyncCounter + 1 == 3) + { + if (F.VBlankStarted) + { + _cpuClocksSinceVBL++; + } + + _emulator.CPU.TickFromPPU(); + _cpuSyncCounter = 0; + } + else + { + _cpuSyncCounter++; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Core.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Core.cs.meta new file mode 100644 index 0000000..db54fa8 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Core.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c6d671ac51126cd43981be89a0309422 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Memory.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Memory.cs new file mode 100644 index 0000000..10afa10 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Memory.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.CompilerServices; + +namespace AxibugEmuOnline.Client.UNES +{ + partial class PPU + { + private readonly byte[] _oam = new byte[0x100]; + private readonly byte[] _vRam = new byte[0x2000]; + private readonly byte[] _paletteRAM = new byte[0x20]; + + private static readonly uint[][] _vRamMirrorLookup = + { + new uint[]{0, 0, 1, 1}, // H + new uint[]{0, 1, 0, 1}, // V + new uint[]{0, 1, 2, 3}, // All + new uint[]{0, 0, 0, 0}, // Upper + new uint[]{1, 1, 1, 1}, // Lower + }; + + private int _lastWrittenRegister; + + public void WriteRegister(uint reg, byte val) + { + reg &= 0xF; + _lastWrittenRegister = val & 0xFF; + switch (reg) + { + case 0x0000: + PPUCTRL = val; + return; + case 0x0001: + PPUMASK = val; + return; + case 0x0002: return; + case 0x0003: + OAMADDR = val; + return; + case 0x0004: + OAMDATA = val; + return; + case 0x005: + PPUSCROLL = val; + return; + case 0x0006: + PPUADDR = val; + return; + case 0x0007: + PPUDATA = val; + return; + } + + throw new NotImplementedException($"{reg:X4} = {val:X2}"); + } + + public byte ReadRegister(uint reg) + { + reg &= 0xF; + switch (reg) + { + case 0x0000: return (byte)_lastWrittenRegister; + case 0x0001: return (byte)_lastWrittenRegister; + case 0x0002: + return (byte)PPUSTATUS; + case 0x0003: + return (byte)OAMADDR; + case 0x0004: + return (byte)OAMDATA; + case 0x0005: return (byte)_lastWrittenRegister; + case 0x0006: return (byte)_lastWrittenRegister; + case 0x0007: + return (byte)PPUDATA; + } + throw new NotImplementedException(reg.ToString("X2")); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint GetVRamMirror(long address) + { + long entry; + var table = Math.DivRem(address - 0x2000, 0x400, out entry); + return _vRamMirrorLookup[(int)_emulator.Cartridge.MirroringMode][table] * 0x400 + (uint)entry; + } + + protected override void InitializeMemoryMap() + { + base.InitializeMemoryMap(); + + MapReadHandler(0x2000, 0x2FFF, address => _vRam[GetVRamMirror(address)]); + MapReadHandler(0x3000, 0x3EFF, address => _vRam[GetVRamMirror(address - 0x1000)]); + MapReadHandler(0x3F00, 0x3FFF, address => + { + if (address == 0x3F10 || address == 0x3F14 || address == 0x3F18 || address == 0x3F0C) + { + address -= 0x10; + } + + return _paletteRAM[(address - 0x3F00) & 0x1F]; + }); + + MapWriteHandler(0x2000, 0x2FFF, (address, val) => _vRam[GetVRamMirror(address)] = val); + MapWriteHandler(0x3000, 0x3EFF, (address, val) => _vRam[GetVRamMirror(address - 0x1000)] = val); + MapWriteHandler(0x3F00, 0x3FFF, (address, val) => + { + if (address == 0x3F10 || address == 0x3F14 || address == 0x3F18 || address == 0x3F0C) + { + address -= 0x10; + } + + _paletteRAM[(address - 0x3F00) & 0x1F] = val; + }); + + _emulator.Mapper.InitializeMemoryMap(this); + } + + public void PerformDMA(uint from) + { + //Console.WriteLine("OAM DMA"); + from <<= 8; + for (uint i = 0; i <= 0xFF; i++) + { + _oam[F.OAMAddress] = (byte)_emulator.CPU.ReadByte(from); + from++; + F.OAMAddress++; + } + + _emulator.CPU.Cycle += 513 + _emulator.CPU.Cycle % 2; + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Memory.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Memory.cs.meta new file mode 100644 index 0000000..cf6d09f --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Memory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa442177f52c5ff4a805fb4e1a30703b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Registers.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Registers.cs new file mode 100644 index 0000000..338d1dc --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Registers.cs @@ -0,0 +1,261 @@ +using System; + +namespace AxibugEmuOnline.Client.UNES +{ + partial class PPU + { + public class PPUFlags + { + /* PPUCTRL register */ + public bool NMIEnabled; + public bool IsMaster; + public bool TallSpritesEnabled; + public uint PatternTableAddress; + public uint SpriteTableAddress; + public uint VRAMIncrement; + + /* PPUMASK register */ + public bool GrayscaleEnabled; + public bool DrawLeftBackground; + public bool DrawLeftSprites; + public bool DrawBackground; + public bool DrawSprites; + // Flipped for PAL/Dendy + public bool EmphasizeRed; + public bool EmphasizeGreen; + public bool EmphasizeBlue; + + /* PPUSTATUS register */ + public bool VBlankStarted; + public bool Sprite0Hit; + public bool SpriteOverflow; + public bool AddressLatch; + + /* PPUADDR register */ + private uint _busAddress; + public uint BusAddress + { + get => _busAddress; + set => _busAddress = value & 0x3FFF; + } + + /* PPUDATA register */ + public uint BusData; + + /* OAMADDR register */ + private uint _oamAddress; + public uint OAMAddress + { + get => _oamAddress; + set => _oamAddress = value & 0xFF; + } + + /* PPUSCROLL registers */ + [Obsolete] + public uint ScrollX; + [Obsolete] + public uint ScrollY; + + public bool RenderingEnabled => DrawBackground || DrawSprites; + } + + public PPUFlags F = new PPUFlags(); + + private uint _v; + public uint V + { + get => _v; + set => _v = value & 0x7FFF; + } + public uint T, X; + + public uint CoarseX => V & 0x1F; + + public uint CoarseY => (V >> 5) & 0x1F; + + public uint FineY => (V >> 12) & 0x7; + + public void ReloadScrollX() => V = (V & 0xFBE0) | (T & 0x041F); + + public void ReloadScrollY() => V = (V & 0x841F) | (T & 0x7BE0); + + public void IncrementScrollX() + { + if ((V & 0x001F) == 31) // if coarse X == 31 + { + V &= ~0x001Fu; // coarse X = 0 + V ^= 0x0400; // switch horizontal nametable + } + else + { + V += 1; // increment coarse X + } + } + + public void IncrementScrollY() + { + if ((V & 0x7000) != 0x7000) // if fine Y < 7 + { + V += 0x1000; // increment fine Y + } + else + { + V &= ~0x7000u; // fine Y = 0 + + uint y = (V & 0x03E0) >> 5; // let y = coarse Y + if (y == 29) + { + y = 0; // coarse Y = 0 + V ^= 0x0800; + } + // switch vertical nametable + else if (y == 31) + { + y = 0; // coarse Y = 0, nametable not switched + } + else + { + y += 1; // increment coarse Y + } + + V = (V & ~0x03E0u) | (y << 5); // put coarse Y back into v + } + } + + public uint PPUCTRL + { + set + { + F.NMIEnabled = (value & 0x80) > 0; + F.IsMaster = (value & 0x40) > 0; + F.TallSpritesEnabled = (value & 0x20) > 0; + F.PatternTableAddress = (value & 0x10) > 0 ? 0x1000u : 0x0000; + F.SpriteTableAddress = (value & 0x08) > 0 ? 0x1000u : 0x0000; + F.VRAMIncrement = (value & 0x04) > 0 ? 32u : 1; + // yyy NN YYYYY XXXXX + // ||| || ||||| +++++--coarse X scroll + // ||| || +++++--------coarse Y scroll + // ||| ++--------------nametable select + // +++-----------------fine Y scroll + T = (T & 0xF3FF) | ((value & 0x3) << 10); // Bits 10-11 hold the base address of the nametable minus $2000 + } + } + + public uint PPUMASK + { + set + { + F.GrayscaleEnabled = (value & 0x1) > 0; + F.DrawLeftBackground = (value & 0x2) > 0; + F.DrawLeftSprites = (value & 0x4) > 0; + F.DrawBackground = (value & 0x8) > 0; + F.DrawSprites = (value & 0x10) > 0; + F.EmphasizeRed = (value & 0x20) > 0; + F.EmphasizeGreen = (value & 0x40) > 0; + F.EmphasizeBlue = (value & 0x80) > 0; + } + } + + /** $2002 **/ + public uint PPUSTATUS + { + get + { + F.AddressLatch = false; + var ret = (F.VBlankStarted.AsByte() << 7) | + (F.Sprite0Hit.AsByte() << 6) | + (F.SpriteOverflow.AsByte() << 5) | + (_lastWrittenRegister & 0x1F); + F.VBlankStarted = false; + return (uint)ret; + } + } + + /** $2006 **/ + public uint PPUADDR + { + set + { + if (F.AddressLatch) + { + T = (T & 0xFF00) | value; + F.BusAddress = T; + V = T; + } + else + { + T = (T & 0x80FF) | ((value & 0x3F) << 8); + } + + F.AddressLatch ^= true; + } + } + + /** $2005 **/ + public uint PPUSCROLL + { + set + { + if (F.AddressLatch) + { + F.ScrollY = value; + T = (T & 0x8FFF) | ((value & 0x7) << 12); + T = (T & 0xFC1F) | (value & 0xF8) << 2; + } + else + { + F.ScrollX = value; + X = value & 0x7; + T = (T & 0xFFE0) | (value >> 3); + } + + F.AddressLatch ^= true; + } + } + + private uint _readBuffer; + public uint PPUDATA + { + get + { + uint ret = ReadByte(F.BusAddress); + if (F.BusAddress < 0x3F00) + { + uint temp = _readBuffer; + _readBuffer = ret; + ret = temp; + } + else + { + // Palette read should also read VRAM into read buffer + _readBuffer = ReadByte(F.BusAddress - 0x1000); + } + + F.BusAddress += F.VRAMIncrement; + return ret; + } + set + { + F.BusData = value; + WriteByte(F.BusAddress, value); + F.BusAddress += F.VRAMIncrement; + } + } + + public uint OAMADDR + { + get => F.OAMAddress; + set => F.OAMAddress = value; + } + + public uint OAMDATA + { + get => _oam[F.OAMAddress]; + set + { + _oam[F.OAMAddress] = (byte)value; + F.OAMAddress++; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Registers.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Registers.cs.meta new file mode 100644 index 0000000..24948ac --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.Registers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0db554be3f472c940bde6ce34fc9ed7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.cs new file mode 100644 index 0000000..bd1fdb2 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.cs @@ -0,0 +1,10 @@ +namespace AxibugEmuOnline.Client.UNES +{ + public sealed partial class PPU : Addressable + { + public PPU(Emulator emulator) : base(emulator, 0x3FFF) + { + InitializeMemoryMap(); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.cs.meta new file mode 100644 index 0000000..5006815 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/PPU.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd14757720b242e4b92425c9d6aae16a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/Utility.cs b/AxibugEmuOnline.Client/Assets/Runtime/Core/Utility.cs new file mode 100644 index 0000000..0455b41 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/Utility.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace AxibugEmuOnline.Client.UNES +{ + public static class Utility + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte AsByte(this bool to) + { + unsafe + { + var result = *((byte*) &to); + return result; + } + } + + public static void Fill(this T[] arr, T value) + { + for (var i = 0; i < arr.Length; i++) + { + arr[i] = value; + } + } + + public static void Map(this IEnumerable enumerator, Action go) + { + foreach (var e in enumerator) + { + go(e); + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Core/Utility.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Core/Utility.cs.meta new file mode 100644 index 0000000..2d1f3ff --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Core/Utility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b326828d1b42abc419d9fe125eed8205 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Input.meta b/AxibugEmuOnline.Client/Assets/Runtime/Input.meta new file mode 100644 index 0000000..9b5bc16 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Input.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1d2ce2c96b7d7ad4cb099e5ce0ea885d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Input/BaseInput.cs b/AxibugEmuOnline.Client/Assets/Runtime/Input/BaseInput.cs new file mode 100644 index 0000000..1a96398 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Input/BaseInput.cs @@ -0,0 +1,11 @@ +using System; +using UnityEngine; + +namespace AxibugEmuOnline.Client.UNES.Input +{ + public abstract class BaseInput + { + public abstract void HandlerKeyDown(Action onKeyDown); + public abstract void HandlerKeyUp(Action onKeyUp); + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Input/BaseInput.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Input/BaseInput.cs.meta new file mode 100644 index 0000000..fefd734 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Input/BaseInput.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b60ded3f5a662a14a8a50795bae8121b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Input/DefaultInput.cs b/AxibugEmuOnline.Client/Assets/Runtime/Input/DefaultInput.cs new file mode 100644 index 0000000..43cbb96 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Input/DefaultInput.cs @@ -0,0 +1,49 @@ +using System; +using UnityEngine; + +namespace AxibugEmuOnline.Client.UNES.Input +{ + public class DefaultInput : BaseInput + { + private KeyCode[] _keyCodes + { + get + { + if (_keyCodeCache == null) + { + var array = Enum.GetValues(typeof(KeyCode)); + _keyCodeCache = new KeyCode[array.Length]; + for (var i = 0; i < array.Length; i++) + { + _keyCodeCache[i] = (KeyCode)array.GetValue(i); + } + } + + return _keyCodeCache; + } + } + private KeyCode[] _keyCodeCache; + + public override void HandlerKeyDown(Action onKeyDown) + { + foreach (var keyCode in _keyCodes) + { + if (UnityEngine.Input.GetKeyDown(keyCode)) + { + onKeyDown(keyCode); + } + } + } + + public override void HandlerKeyUp(Action onKeyUp) + { + foreach (var keyCode in _keyCodes) + { + if (UnityEngine.Input.GetKeyUp(keyCode)) + { + onKeyUp(keyCode); + } + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Input/DefaultInput.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Input/DefaultInput.cs.meta new file mode 100644 index 0000000..01b9f33 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Input/DefaultInput.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 028fda8a78366f347b421062b3604d20 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper.meta new file mode 100644 index 0000000..f05ed2d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d22acc98b0a7fa042bb19b9894899fb7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/AxROM.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/AxROM.cs new file mode 100644 index 0000000..76e4b7b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/AxROM.cs @@ -0,0 +1,26 @@ +using static AxibugEmuOnline.Client.UNES.Cartridge.VRAMMirroringMode; + +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(7)] + public class AxROM : BaseMapper + { + protected int _bankOffset; + private readonly Cartridge.VRAMMirroringMode[] _mirroringModes = { Lower, Upper }; + + public AxROM(Emulator emulator) : base(emulator) + { + _emulator.Cartridge.MirroringMode = _mirroringModes[0]; + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_bankOffset + (address - 0x8000)]); + cpu.MapWriteHandler(0x8000, 0xFFFF, (address, val) => + { + _bankOffset = (val & 0x7) * 0x8000; + _emulator.Cartridge.MirroringMode = _mirroringModes[(val >> 4) & 0x1]; + }); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/AxROM.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/AxROM.cs.meta new file mode 100644 index 0000000..67ab997 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/AxROM.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 632b907c0f30abb47b218545e40ff06d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/BaseMapper.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/BaseMapper.cs new file mode 100644 index 0000000..05d3e3f --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/BaseMapper.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; + +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public class MapperDef : Attribute + { + public int Id; + public string Name; + public string Description; + + public MapperDef() + { + + } + + public MapperDef(int id) + { + Id = id; + } + } + + public abstract class BaseMapper + { + protected readonly Emulator _emulator; + protected readonly byte[] _prgROM; + protected readonly byte[] _prgRAM = new byte[0x2000]; + protected readonly byte[] _chrROM; + protected readonly uint _lastBankOffset; + + protected BaseMapper(Emulator emulator) + { + _emulator = emulator; + var cart = emulator.Cartridge; + _prgROM = cart.PRGROM; + _chrROM = cart.CHRROM; + _lastBankOffset = (uint) _prgROM.Length - 0x4000; + } + + public virtual void InitializeMemoryMap(CPU cpu) + { + + } + + public virtual void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[address]); + ppu.MapWriteHandler(0x0000, 0x1FFF, (address, val) => _chrROM[address] = val); + } + + public virtual void ProcessCycle(int scanLine, int cycle) + { + + } + + public virtual byte[] GetSaveData() + { + return _prgRAM; + } + + public virtual void LoadSaveData(byte[] saveData) + { + Array.Copy(saveData, _prgRAM, saveData.Length); + } + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/BaseMapper.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/BaseMapper.cs.meta new file mode 100644 index 0000000..6e44d0d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/BaseMapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a468573a1ef266542a055638aeef33cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/CNROM.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/CNROM.cs new file mode 100644 index 0000000..0ac1eda --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/CNROM.cs @@ -0,0 +1,32 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(3)] + public class CNROM : BaseMapper + { + protected int _bankOffset; + + public CNROM(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_bankOffset + address]); + } + + public override void InitializeMemoryMap(CPU cpu) + { + if (_prgROM.Length == 0x8000) + { + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[address - 0x8000]); + } + else + { + cpu.MapReadHandler(0x8000, 0xBFFF, address => _prgROM[address - 0x8000]); + cpu.MapReadHandler(0xC000, 0xFFFF, address => _prgROM[address - 0xC000]); + } + + cpu.MapWriteHandler(0x8000, 0xFFFF, (address, val) => _bankOffset = (val & 0x3) * 0x2000); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/CNROM.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/CNROM.cs.meta new file mode 100644 index 0000000..15d040c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/CNROM.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fdaa43625af49e54aacf69d2ed92544f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Camerica.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Camerica.cs new file mode 100644 index 0000000..8f0cf99 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Camerica.cs @@ -0,0 +1,27 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(71)] + public class Camerica : BaseMapper + { + protected int _prgBankOffset; + + public Camerica(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x8000, 0xBFFF, address => _prgROM[_prgBankOffset + (address - 0x8000)]); + cpu.MapReadHandler(0xC000, 0xFFFF, address => _prgROM[_prgROM.Length - 0x4000 + (address - 0xC000)]); + + // Actually starts at 0x8000, but use 0x9000 for compatibility w/o submapper + cpu.MapWriteHandler(0x9000, 0x9FFF, (address, val) => + { + // TODO: Fire Hawk mirroring + }); + + // The number of bits available vary: 4 for the BF9093, 3 for the BF9097, and 2 for the BF9096. + cpu.MapWriteHandler(0xC000, 0xFFFF, (address, val) => _prgBankOffset = (val & 0xF) * 0x4000 % _prgROM.Length); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Camerica.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Camerica.cs.meta new file mode 100644 index 0000000..2d735de --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Camerica.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a879b12017bd97949a00317043b96f54 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/ColorDreams.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/ColorDreams.cs new file mode 100644 index 0000000..fae07ee --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/ColorDreams.cs @@ -0,0 +1,29 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(11)] + public class ColorDreams : BaseMapper + { + protected int _prgBankOffset; + protected int _chrBankOffset; + + public ColorDreams(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_chrBankOffset + address]); + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_prgBankOffset + (address - 0x8000)]); + + cpu.MapWriteHandler(0x8000, 0xFFFF, (address, val) => + { + _prgBankOffset = (val & 0x3) * 0x8000; + _chrBankOffset = (val >> 4) * 0x2000; + }); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/ColorDreams.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/ColorDreams.cs.meta new file mode 100644 index 0000000..172a6aa --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/ColorDreams.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 50a4c8ff195db574585c43ba079d8195 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/DxROM.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/DxROM.cs new file mode 100644 index 0000000..b4f8c62 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/DxROM.cs @@ -0,0 +1,39 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(206)] + public class DxROM : MMC3 + { + public DxROM(Emulator emulator) : base(emulator) + { + _prgBankingMode = PRGBankingMode.SwitchFix; + _chrBankingMode = CHRBankingMode.TwoFour; + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_prgBankOffsets[(address - 0x8000) / 0x2000] + address % 0x2000]); + + cpu.MapWriteHandler(0x8000, 0x9FFF, (address, val) => + { + if ((address & 0x1) == 0) + { + _currentBank = val & 0x7u; + } + else + { + if (_currentBank <= 1) val &= 0x1F; + else if (_currentBank <= 5) val &= 0x3F; + else val &= 0xF; + + _banks[_currentBank] = val; + UpdateOffsets(); + } + }); + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_chrBankOffsets[address / 0x400] + address % 0x400]); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/DxROM.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/DxROM.cs.meta new file mode 100644 index 0000000..dd2b1c3 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/DxROM.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8ae98e170ab70143bc7284dc01b9627 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/GxROM.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/GxROM.cs new file mode 100644 index 0000000..92a063a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/GxROM.cs @@ -0,0 +1,29 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(66)] + public class GxROM : BaseMapper + { + protected int _prgBankOffset; + protected int _chrBankOffset; + + public GxROM(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_chrBankOffset + address]); + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_prgBankOffset + (address - 0x8000)]); + + cpu.MapWriteHandler(0x8000, 0xFFFF, (address, val) => + { + _prgBankOffset = ((val >> 4) & 0x3) * 0x8000; + _chrBankOffset = (val & 0x3) * 0x2000; + }); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/GxROM.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/GxROM.cs.meta new file mode 100644 index 0000000..2810704 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/GxROM.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a06e1a753215b64886b90ecde825cec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Jaleco.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Jaleco.cs new file mode 100644 index 0000000..b85b047 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Jaleco.cs @@ -0,0 +1,29 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(140)] + public class Jaleco : BaseMapper + { + protected int _prgBankOffset; + protected int _chrBankOffset; + + public Jaleco(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_chrBankOffset + address]); + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_prgBankOffset + (address - 0x8000)]); + + cpu.MapWriteHandler(0x6000, 0x7FFF, (address, val) => + { + _prgBankOffset = ((val >> 4) & 0x3) * 0x8000; + _chrBankOffset = (val & 0x3) * 0x2000; + }); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Jaleco.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Jaleco.cs.meta new file mode 100644 index 0000000..836c62f --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Jaleco.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac2f62d216cf70643919c921900c6e70 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC1.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC1.cs new file mode 100644 index 0000000..a420edf --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC1.cs @@ -0,0 +1,170 @@ +using static AxibugEmuOnline.Client.UNES.Cartridge.VRAMMirroringMode; + +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(1)] + public class MMC1 : BaseMapper + { + // TODO: are MMC1 and MMC1A even different chip types? + public enum ChipType { MMC1, MMC1A, MMC1B, MMC1C } + public enum CHRBankingMode { Single, Double } + public enum PRGBankingMode { Switch32Kb, Switch16KbFixFirst, Switch16KbFixLast } + + private readonly Cartridge.VRAMMirroringMode[] _mirroringModes = { Lower, Upper, Vertical, Horizontal }; + + private readonly ChipType _type; + private CHRBankingMode _chrBankingMode; + private PRGBankingMode _prgBankingMode; + + private uint _serialData; + private int _serialPos; + + private uint _control; + + private readonly uint[] _chrBankOffsets = new uint[2]; + private readonly uint[] _chrBanks = new uint[2]; + + private readonly uint[] _prgBankOffsets = new uint[2]; + private uint _prgBank; + + private bool _prgRAMEnabled; + + private uint? _lastWritePC; + + public MMC1(Emulator emulator) : this(emulator, ChipType.MMC1B) + { + + } + + public MMC1(Emulator emulator, ChipType chipType) : base(emulator) + { + _type = chipType; + if (chipType == ChipType.MMC1B) _prgRAMEnabled = true; + UpdateControl(0x0F); + _emulator.Cartridge.MirroringMode = Horizontal; + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x6000, 0x7FFF, address => _prgRAM[address - 0x6000]); + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_prgBankOffsets[(address - 0x8000) / 0x4000] + address % 0x4000]); + + cpu.MapWriteHandler(0x6000, 0x7FFF, (address, val) => + { + // PRG RAM is always enabled on MMC1A + if (_type == ChipType.MMC1A || _prgRAMEnabled) + _prgRAM[address - 0x6000] = val; + }); + + cpu.MapWriteHandler(0x8000, 0xFFFF, (address, val) => + { + // Explicitly ignore the second write happening on consecutive cycles + // of an RMW instruction + var cycle = _emulator.CPU.PC; + if (cycle == _lastWritePC) + return; + _lastWritePC = cycle; + + if ((val & 0x80) > 0) + { + _serialData = 0; + _serialPos = 0; + UpdateControl(_control | 0x0C); + } + else + { + _serialData |= (uint)((val & 0x1) << _serialPos); + _serialPos++; + + if (_serialPos == 5) + { + // Address is incompletely decoded + address &= 0x6000; + if (address == 0x0000) + UpdateControl(_serialData); + else if (address == 0x2000) + UpdateCHRBank(0, _serialData); + else if (address == 0x4000) + UpdateCHRBank(1, _serialData); + else if (address == 0x6000) + UpdatePRGBank(_serialData); + + _serialData = 0; + _serialPos = 0; + } + } + }); + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_chrBankOffsets[address / 0x1000] + address % 0x1000]); + ppu.MapWriteHandler(0x0000, 0x1FFF, (address, val) => _chrROM[_chrBankOffsets[address / 0x1000] + address % 0x1000] = val); + } + + private void UpdateControl(uint value) + { + _control = value; + + _emulator.Cartridge.MirroringMode = _mirroringModes[value & 0x3]; + + _chrBankingMode = (CHRBankingMode)((value >> 4) & 0x1); + + var prgMode = (value >> 2) & 0x3; + // Both 0 and 1 are 32Kb switch + if (prgMode == 0) prgMode = 1; + _prgBankingMode = (PRGBankingMode)(prgMode - 1); + + UpdateCHRBank(1, _chrBanks[1]); + UpdateCHRBank(0, _chrBanks[0]); + UpdatePRGBank(_prgBank); + } + + private void UpdatePRGBank(uint value) + { + _prgBank = value; + + _prgRAMEnabled = (value & 0x10) == 0; + value &= 0xF; + + switch (_prgBankingMode) + { + case PRGBankingMode.Switch32Kb: + value >>= 1; + value *= 0x4000; + _prgBankOffsets[0] = value; + _prgBankOffsets[1] = value + 0x4000; + break; + case PRGBankingMode.Switch16KbFixFirst: + _prgBankOffsets[0] = 0; + _prgBankOffsets[1] = value * 0x4000; + break; + case PRGBankingMode.Switch16KbFixLast: + _prgBankOffsets[0] = value * 0x4000; + _prgBankOffsets[1] = _lastBankOffset; + break; + } + } + + private void UpdateCHRBank(uint bank, uint value) + { + _chrBanks[bank] = value; + + // TODO FIXME: I feel like this branch should only be taken + // when bank == 0, but this breaks Final Fantasy + // When can banking mode change without UpdateCHRBank being called? + if (_chrBankingMode == CHRBankingMode.Single) + { + value = _chrBanks[0]; + value >>= 1; + value *= 0x1000; + _chrBankOffsets[0] = value; + _chrBankOffsets[1] = value + 0x1000; + } + else + { + _chrBankOffsets[bank] = value * 0x1000; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC1.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC1.cs.meta new file mode 100644 index 0000000..4c02868 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC1.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d3ab06f7819df254491a7afa6a82bb43 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC2.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC2.cs new file mode 100644 index 0000000..2aa4a06 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC2.cs @@ -0,0 +1,33 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(Id = 9, Description = "Mike Tyson's Punch-Out!!")] + public class MMC2 : MMC4 + { + public MMC2(Emulator emulator) : base(emulator) + { + + } + + public override void InitializeMemoryMap(CPU cpu) + { + base.InitializeMemoryMap(cpu); + + cpu.MapReadHandler(0x8000, 0xBFFF, address => _prgROM[_prgBankOffset + (address - 0x8000)]); + cpu.MapReadHandler(0xA000, 0xFFFF, address => _prgROM[_prgROM.Length - 0x4000 - 0x2000 + (address - 0xA000)]); + + cpu.MapWriteHandler(0xA000, 0xAFFF, (address, val) => _prgBankOffset = (val & 0xF) * 0x2000); + } + + protected override void GetLatch(uint address, out uint latch, out bool? on) + { + base.GetLatch(address, out latch, out on); + + // For MMC2, only 0xFD8 and 0xFE8 trigger the latch, + // not the whole range like in MMC4 + if (latch == 0 && (address & 0x3) != 0) + { + on = null; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC2.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC2.cs.meta new file mode 100644 index 0000000..3270f4a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC2.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 522a51dcf1b92d2498013b4d6f9accb0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC3.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC3.cs new file mode 100644 index 0000000..ff9bc22 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC3.cs @@ -0,0 +1,182 @@ +using static AxibugEmuOnline.Client.UNES.Cartridge.VRAMMirroringMode; + +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(4)] + public class MMC3 : BaseMapper + { + // Different PRG RAM write/enable controls + public enum ChipType { MMC3, MMC6 } + public enum CHRBankingMode { TwoFour, FourTwo } + public enum PRGBankingMode { SwitchFix, FixSwitch } + + private readonly Cartridge.VRAMMirroringMode[] _mirroringModes = { Vertical, Horizontal }; + + private readonly ChipType _type; + protected CHRBankingMode _chrBankingMode; + protected PRGBankingMode _prgBankingMode; + + + protected readonly uint[] _chrBankOffsets = new uint[8]; + protected uint[] _prgBankOffsets; + protected readonly uint[] _banks = new uint[8]; + protected uint _currentBank; + + private uint _irqReloadValue; + private uint _irqCounter; + protected bool _irqEnabled; + + private bool _prgRAMEnabled; + + public MMC3(Emulator emulator) : this(emulator, ChipType.MMC3) + { + + } + + public MMC3(Emulator emulator, ChipType chipType) : base(emulator) + { + _type = chipType; + _prgBankOffsets = new uint[] { 0, 0x2000, _lastBankOffset, _lastBankOffset + 0x2000 }; + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x6000, 0x7FFF, address => _prgRAM[address - 0x6000]); + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_prgBankOffsets[(address - 0x8000) / 0x2000] + address % 0x2000]); + + cpu.MapWriteHandler(0x6000, 0xFFFF, WriteByte); + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_chrBankOffsets[address / 0x400] + address % 0x400]); + ppu.MapWriteHandler(0x0000, 0x1FFF, (address, val) => _chrROM[_chrBankOffsets[address / 0x400] + address % 0x400] = val); + } + + public override void ProcessCycle(int scanLine, int cycle) + { + if (_emulator.PPU.F.RenderingEnabled && cycle == 260 && (0 <= scanLine && scanLine < 240 || scanLine == -1)) + { + if (_irqCounter == 0) + { + _irqCounter = _irqReloadValue; + } + else + { + _irqCounter--; + if (_irqEnabled && _irqCounter == 0) + { + _emulator.CPU.TriggerInterrupt(CPU.InterruptType.IRQ); + } + } + } + } + + protected void WriteByte(uint addr, byte value) + { + bool even = (addr & 0x1) == 0; + + if (addr < 0x8000) + { + if (_prgRAMEnabled) + { + _prgRAM[addr - 0x6000] = value; + } + } + else if (addr < 0xA000) + { + if (even) + { + _currentBank = value & 0x7u; + _prgBankingMode = (PRGBankingMode)((value >> 6) & 0x1); + _chrBankingMode = (CHRBankingMode)((value >> 7) & 0x1); + } + else + { + _banks[_currentBank] = value; + } + + UpdateOffsets(); + } + else if (addr < 0xC000) + { + if (even) + { + _emulator.Cartridge.MirroringMode = _mirroringModes[value & 0x1]; + } + else + { + _prgRAMEnabled = (value & 0xC0) == 0x80; + } + } + else if (addr < 0xE000) + { + if (even) + { + _irqReloadValue = value; + } + else + { + _irqCounter = 0; + } + } + else + { + _irqEnabled = !even; + } + } + + protected void UpdateOffsets() + { + switch (_prgBankingMode) + { + case PRGBankingMode.SwitchFix: + _prgBankOffsets[0] = _banks[6] * 0x2000; + _prgBankOffsets[1] = _banks[7] * 0x2000; + _prgBankOffsets[2] = _lastBankOffset; + _prgBankOffsets[3] = _lastBankOffset + 0x2000; + break; + case PRGBankingMode.FixSwitch: + _prgBankOffsets[0] = _lastBankOffset; + _prgBankOffsets[1] = _banks[7] * 0x2000; + _prgBankOffsets[2] = _banks[6] * 0x2000; + _prgBankOffsets[3] = _lastBankOffset + 0x2000; + break; + } + + switch (_chrBankingMode) + { + case CHRBankingMode.TwoFour: + _chrBankOffsets[0] = _banks[0] & 0xFE; + _chrBankOffsets[1] = _banks[0] | 0x01; + _chrBankOffsets[2] = _banks[1] & 0xFE; + _chrBankOffsets[3] = _banks[1] | 0x01; + _chrBankOffsets[4] = _banks[2]; + _chrBankOffsets[5] = _banks[3]; + _chrBankOffsets[6] = _banks[4]; + _chrBankOffsets[7] = _banks[5]; + break; + case CHRBankingMode.FourTwo: + _chrBankOffsets[0] = _banks[2]; + _chrBankOffsets[1] = _banks[3]; + _chrBankOffsets[2] = _banks[4]; + _chrBankOffsets[3] = _banks[5]; + _chrBankOffsets[4] = _banks[0] & 0xFE; + _chrBankOffsets[5] = _banks[0] | 0x01; + _chrBankOffsets[6] = _banks[1] & 0xFE; + _chrBankOffsets[7] = _banks[1] | 0x01; + break; + } + + for (var i = 0; i < _prgBankOffsets.Length; i++) + { + _prgBankOffsets[i] %= (uint)_prgROM.Length; + } + + for (var i = 0; i < _chrBankOffsets.Length; i++) + { + _chrBankOffsets[i] = (uint) (_chrBankOffsets[i] * 0x400 % _chrROM.Length); + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC3.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC3.cs.meta new file mode 100644 index 0000000..0adfec7 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82fb09fbeba58ae458f58e52898fedb1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC4.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC4.cs new file mode 100644 index 0000000..9d817e1 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC4.cs @@ -0,0 +1,73 @@ +using static AxibugEmuOnline.Client.UNES.Cartridge.VRAMMirroringMode; + +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(10)] + public class MMC4 : BaseMapper + { + protected readonly Cartridge.VRAMMirroringMode[] _mirroringModes = { Vertical, Horizontal }; + + protected int _prgBankOffset; + protected int[,] _chrBankOffsets = new int[2, 2]; + protected bool[] _latches = new bool[2]; + + public MMC4(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x6000, 0x7FFF, address => _prgRAM[address - 0x6000]); + cpu.MapReadHandler(0x8000, 0xBFFF, address => _prgROM[_prgBankOffset + (address - 0x8000)]); + cpu.MapReadHandler(0xC000, 0xFFFF, address => _prgROM[_prgROM.Length - 0x4000 + (address - 0xC000)]); + + cpu.MapWriteHandler(0x6000, 0x7FFF, (address, val) => _prgRAM[address - 0x6000] = val); + cpu.MapWriteHandler(0xA000, 0xAFFF, (address, val) => _prgBankOffset = (val & 0xF) * 0x4000); + cpu.MapWriteHandler(0xB000, 0xEFFF, (address, val) => + { + var bank = (address - 0xB000) / 0x2000; + var latch = ((address & 0x1FFF) == 0).AsByte(); + _chrBankOffsets[bank, latch] = (val & 0x1F) * 0x1000; + }); + + cpu.MapWriteHandler(0xF000, 0xFFFF, (address, val) => _emulator.Cartridge.MirroringMode = _mirroringModes[val & 0x1]); + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => + { + var bank = address / 0x1000; + var ret = _chrROM[_chrBankOffsets[bank, _latches[bank].AsByte()] + address % 0x1000]; + if ((address & 0x08) > 0) + { + GetLatch(address, out uint latch, out bool? on); + + if (on != null) + { + _latches[latch] = (bool)on; + } + } + + return ret; + }); + } + + protected virtual void GetLatch(uint address, out uint latch, out bool? on) + { + latch = (address >> 12) & 0x1; + on = null; + + address = (address >> 4) & 0xFF; + + if (address == 0xFE) + { + on = true; + } + else if (address == 0xFD) + { + on = false; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC4.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC4.cs.meta new file mode 100644 index 0000000..81f2dfe --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/MMC4.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54d8f341641616d45b20c65ec4a75ff2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper094.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper094.cs new file mode 100644 index 0000000..4baaefe --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper094.cs @@ -0,0 +1,17 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(Id = 94, Description = "Senjou no Ookami")] + public class Mapper094 : UxROM + { + public Mapper094(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(CPU cpu) + { + base.InitializeMemoryMap(cpu); + + cpu.MapWriteHandler(0x8000, 0xFFFF, (address, val) => _bankOffset = (val & 0x1C) << 12); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper094.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper094.cs.meta new file mode 100644 index 0000000..70a9d00 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper094.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0abeb567769f715418ae9125bd029206 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper155.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper155.cs new file mode 100644 index 0000000..44fb15b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper155.cs @@ -0,0 +1,11 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(Id = 155, Description = "MMC1A")] + public class Mapper155 : MMC1 + { + // Mapper for games requiring MMC1A + public Mapper155(Emulator emulator) : base(emulator, ChipType.MMC1A) + { + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper155.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper155.cs.meta new file mode 100644 index 0000000..a775951 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper155.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc90c4e04b12a134c884ca3431375aac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper180.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper180.cs new file mode 100644 index 0000000..e7a81fe --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper180.cs @@ -0,0 +1,21 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + // Mapper used strictly for Crazy Climber; logic is slighly different + [MapperDef(Id = 180, Description = "Crazy Climber")] + public class Mapper180 : UxROM + { + public Mapper180(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(CPU cpu) + { + base.InitializeMemoryMap(cpu); + + // $8000-$C000 is fixed to *first* bank + cpu.MapReadHandler(0x8000, 0xBFFF, address => _prgROM[address - 0x8000]); + // $C000-$FFFF is switchable, controlled the same as UxROM + cpu.MapReadHandler(0xC000, 0xFFFF, address => _prgROM[_bankOffset + (address - 0xC000)]); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper180.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper180.cs.meta new file mode 100644 index 0000000..41282e8 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Mapper180.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ed79f8d409c2864bab9bc85848bc36a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/NROM.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/NROM.cs new file mode 100644 index 0000000..bf7ad3d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/NROM.cs @@ -0,0 +1,23 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(0)] + public class NROM : BaseMapper + { + private readonly byte[] _addressSpace = new byte[0x2000 + 0x8000]; // Space for $2000 VRAM + $8000 PRG + + public NROM(Emulator emulator) : base(emulator) + { + for (var i = 0; i < 0x8000; i++) + { + var offset = _emulator.Cartridge.PRGROMSize == 0x4000 ? i & 0xBFFF : i; + _addressSpace[0x2000 + i] = _prgROM[offset]; + } + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x6000, 0xFFFF, address => _addressSpace[address - 0x6000]); + cpu.MapWriteHandler(0x6000, 0x7FFF, (address, val) => _addressSpace[address - 0x6000] = val); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/NROM.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/NROM.cs.meta new file mode 100644 index 0000000..f09b0e2 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/NROM.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7781217265cd36043acd57aa3a049d25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Nina003006.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Nina003006.cs new file mode 100644 index 0000000..d26761b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Nina003006.cs @@ -0,0 +1,32 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(79)] + public class Nina003006 : BaseMapper + { + protected int _prgBankOffset; + protected int _chrBankOffset; + + public Nina003006(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(PPU ppu) + { + ppu.MapReadHandler(0x0000, 0x1FFF, address => _chrROM[_chrBankOffset + address]); + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x8000, 0xFFFF, address => _prgROM[_prgBankOffset + (address - 0x8000)]); + + cpu.MapWriteHandler(0x4000, 0x5FFF, (address, val) => + { + if ((address & 0b1110_0001_0000_0000) == 0b0100_0001_0000_0000) + { + _prgBankOffset = ((val >> 4) & 0x3) * 0x8000; + _chrBankOffset = (val & 0x3) * 0x2000; + } + }); + } + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Nina003006.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Nina003006.cs.meta new file mode 100644 index 0000000..128d6ad --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/Nina003006.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a935d078b54a6a4b9434e3478863251 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/UxROM.cs b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/UxROM.cs new file mode 100644 index 0000000..fa24992 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/UxROM.cs @@ -0,0 +1,22 @@ +namespace AxibugEmuOnline.Client.UNES.Mapper +{ + [MapperDef(2)] + public class UxROM : BaseMapper + { + protected int _bankOffset; + + public UxROM(Emulator emulator) : base(emulator) + { + } + + public override void InitializeMemoryMap(CPU cpu) + { + cpu.MapReadHandler(0x6000, 0x7FFF, address => _prgRAM[address - 0x6000]); + cpu.MapReadHandler(0x8000, 0xBFFF, address => _prgROM[_bankOffset + (address - 0x8000)]); + cpu.MapReadHandler(0xC000, 0xFFFF, address => _prgROM[_prgROM.Length - 0x4000 + (address - 0xC000)]); + + cpu.MapWriteHandler(0x6000, 0x7FFF, (address, val) => _prgRAM[address - 0x6000] = val); + cpu.MapWriteHandler(0x8000, 0xFFFF, (address, val) => _bankOffset = (val & 0xF) * 0x4000); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Mapper/UxROM.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/UxROM.cs.meta new file mode 100644 index 0000000..9b0f9dc --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Mapper/UxROM.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9fa6ddb08aec2014fada61a92af067ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Renderer.meta b/AxibugEmuOnline.Client/Assets/Runtime/Renderer.meta new file mode 100644 index 0000000..ede6704 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Renderer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ff3612d2884e82145a83b869175fe872 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Renderer/IRenderer.cs b/AxibugEmuOnline.Client/Assets/Runtime/Renderer/IRenderer.cs new file mode 100644 index 0000000..de133db --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Renderer/IRenderer.cs @@ -0,0 +1,13 @@ +namespace AxibugEmuOnline.Client.UNES.Renderer +{ + public interface IRenderer + { + string Name { get; } + + void HandleRender(); + + void Init(UNESBehaviour nes); + + void End(); + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Renderer/IRenderer.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Renderer/IRenderer.cs.meta new file mode 100644 index 0000000..5bad6e1 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Renderer/IRenderer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e02ede35ded0294fb7f13f60387557f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Renderer/UnityRenderer.cs b/AxibugEmuOnline.Client/Assets/Runtime/Renderer/UnityRenderer.cs new file mode 100644 index 0000000..d48923b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Renderer/UnityRenderer.cs @@ -0,0 +1,71 @@ +using System.Runtime.InteropServices; +using UnityEngine; + +namespace AxibugEmuOnline.Client.UNES.Renderer +{ + public class UnityRenderer : IRenderer + { + public UNESBehaviour UnesBehaviour; + public RenderTexture RenderTexture; + + public string Name => "Unity"; + + private Texture2D _drawTexture; + private Color[] _pixelCache; + + private Texture2D _clearTexture; + + public void Init(UNESBehaviour nes) + { + UnesBehaviour = nes; + RenderTexture = nes.RenderTexture; + _drawTexture = new Texture2D(UNESBehaviour.GameWidth, UNESBehaviour.GameHeight); + _pixelCache = new Color[UNESBehaviour.GameWidth * UNESBehaviour.GameHeight]; + + _clearTexture = new Texture2D(1, 1); + _clearTexture.SetPixel(0, 0, Color.clear); + _clearTexture.Apply(); + } + + public void HandleRender() + { + if (RenderTexture.filterMode != UnesBehaviour.FilterMode) + { + RenderTexture.filterMode = UnesBehaviour.FilterMode; + } + + for (var y = 0; y < UNESBehaviour.GameHeight; y++) + { + for (var x = 0; x < UNESBehaviour.GameWidth; x++) + { + var rawIndex = UNESBehaviour.GameWidth * y + x; + var color = GetColor(UnesBehaviour.RawBitmap[rawIndex]); + var texIndex = UNESBehaviour.GameWidth * (UNESBehaviour.GameHeight - y - 1) + x; + _pixelCache[texIndex] = color; + } + } + + _drawTexture.SetPixels(_pixelCache); + _drawTexture.Apply(); + + Graphics.Blit(_drawTexture, RenderTexture); + } + + public Color GetColor(uint value) + { + var r = 0xFF0000 & value; + r >>= 16; + var b = 0xFF & value; + var g = 0xFF00 & value; + g >>= 8; + var color = new Color(r / 255f, g / 255f, b / 255f); + return color; + } + + public void End() + { + Graphics.Blit(_clearTexture, RenderTexture); + } + } +} + diff --git a/AxibugEmuOnline.Client/Assets/Runtime/Renderer/UnityRenderer.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/Renderer/UnityRenderer.cs.meta new file mode 100644 index 0000000..13e31a8 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/Renderer/UnityRenderer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f751adeb00750a43a0d2534a2301da2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Runtime/UNESBehaviour.cs b/AxibugEmuOnline.Client/Assets/Runtime/UNESBehaviour.cs new file mode 100644 index 0000000..ac1fc93 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/UNESBehaviour.cs @@ -0,0 +1,281 @@ +using System; +using System.Diagnostics; +using System.Threading; +using UnityEngine; +using AxibugEmuOnline.Client.UNES.Controller; +using AxibugEmuOnline.Client.UNES.Input; +using AxibugEmuOnline.Client.UNES.Renderer; +using AxibugEmuOnline.Client.ClientCore; +using AxibugEmuOnline.Client.Sample; +using System.Collections.Generic; +using System.Collections; +using System.Linq; + +namespace AxibugEmuOnline.Client.UNES +{ + public class UNESBehaviour : MonoBehaviour + { + [Header("Render")] + public RenderTexture RenderTexture; + public const int GameWidth = 256; + public const int GameHeight = 240; + public FilterMode FilterMode = FilterMode.Point; + public bool LogicThread = true; + + [Header("Input")] + public KeyConfig KeyConfig; + + public BaseInput Input { get; set; } + public IRenderer Renderer { get; set; } + public uint[] RawBitmap { get; set; } = new uint[GameWidth * GameHeight]; + public bool Ready { get; set; } + public bool GameStarted { get; set; } + + private bool _rendererRunning = true; + private IController _controller; + private Emulator _emu; + private bool _suspended; + private Thread _renderThread; + private int _activeSpeed = 1; + + public void Boot(byte[] romData) + { + InitInput(); + InitRenderer(); + BootCartridge(romData); + } + + public void Boot_Obs() + { + InitInput(); + InitRenderer(); + BootCartridge_Obs(); + } + + public void LoadSaveData(byte[] saveData) + { + _emu?.Mapper.LoadSaveData(saveData); + } + + public byte[] GetSaveData() + { + return _emu?.Mapper.GetSaveData(); + } + + Queue _queue = new Queue(); + + + private void BootCartridge(byte[] romData) + { + _emu = new Emulator(romData, _controller); + if (LogicThread) + { + _renderThread = new Thread(() => + { + GameStarted = true; + var s = new Stopwatch(); + var s0 = new Stopwatch(); + while (_rendererRunning) + { + if (_suspended) + { + Thread.Sleep(100); + continue; + } + + s.Restart(); + for (var i = 0; i < 60 && !_suspended; i++) + { + s0.Restart(); + lock (RawBitmap) + { + _emu.PPU.ProcessFrame(); + RawBitmap = _emu.PPU.RawBitmap; + //塞进发送队列 + _queue.Enqueue(_emu.PPU.RawBitmap_paletteIdxCache); + } + + s0.Stop(); + Thread.Sleep(Math.Max((int)(980 / 60.0 - s0.ElapsedMilliseconds), 0) / _activeSpeed); + } + + s.Stop(); + } + }); + + _renderThread.Start(); + } + else + { + GameStarted = true; + } + } + + private void BootCartridge_Obs() + { + if (LogicThread) + { + _renderThread = new Thread(() => + { + GameStarted = true; + var s = new Stopwatch(); + var s0 = new Stopwatch(); + while (_rendererRunning) + { + if (_suspended) + { + Thread.Sleep(100); + continue; + } + + s.Restart(); + for (var i = 0; i < 60 && !_suspended; i++) + { + s0.Restart(); + lock (RawBitmap) + { + RawBitmap = AppAxibugEmuOnline.game.RawBitmap; + } + + s0.Stop(); + Thread.Sleep(Math.Max((int)(980 / 60.0 - s0.ElapsedMilliseconds), 0) / _activeSpeed); + } + + s.Stop(); + } + }); + + _renderThread.Start(); + } + else + { + GameStarted = true; + } + } + + #region Monobehaviour + + public void Awake() + { + } + + public void OnEnable() + { + + } + + public void OnDisable() + { + _rendererRunning = false; + Renderer?.End(); + } + + public void Update() + { + if (!GameStarted) return; + UpdateInput(); + + if (UNESTest.instance.PlayerP1) + { + UpdateRender(); + } + else + { + UpdateRender_Obs(); + } + } + + #endregion + + #region Input + + private void InitInput() + { + _controller = new NesController(this); + Input = new DefaultInput(); + } + + public void UpdateInput() + { + Input.HandlerKeyDown(keyCode => + { + switch (keyCode) + { + case KeyCode.F2: + _suspended = false; + break; + case KeyCode.F3: + _suspended = true; + break; + default: + _controller.PressKey(keyCode); + break; + } + }); + + Input.HandlerKeyUp(keyCode => + { + _controller.ReleaseKey(keyCode); + }); + } + + #endregion + + #region Render + + public void UpdateRender() + { + while (_queue.Count() > 0) + { + //发送 + AppAxibugEmuOnline.game.SendScreen(_queue.Dequeue()); + } + if (!_rendererRunning) return; + if (_suspended) return; + + if (!LogicThread) + { + _emu.PPU.ProcessFrame(); + RawBitmap = _emu.PPU.RawBitmap; + Renderer.HandleRender(); + + } + else + { + lock (RawBitmap) + { + Renderer.HandleRender(); + } + } + } + + public void UpdateRender_Obs() + { + if (!_rendererRunning) return; + if (_suspended) return; + + if (!LogicThread) + { + RawBitmap = AppAxibugEmuOnline.game.RawBitmap; + Renderer.HandleRender(); + } + else + { + lock (RawBitmap) + { + Renderer.HandleRender(); + } + } + } + + private void InitRenderer() + { + Renderer?.End(); + + Renderer = new UnityRenderer(); + Renderer.Init(this); + } + + #endregion + } +} diff --git a/AxibugEmuOnline.Client/Assets/Runtime/UNESBehaviour.cs.meta b/AxibugEmuOnline.Client/Assets/Runtime/UNESBehaviour.cs.meta new file mode 100644 index 0000000..99998ce --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Runtime/UNESBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5948479853ec1194497c60babb6bc213 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Scene.meta b/AxibugEmuOnline.Client/Assets/Scene.meta new file mode 100644 index 0000000..a2af5ba --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Scene.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 97287d4650544eb4daa041e4a384a06b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Scene/AxibugEmuOnline.Client.unity b/AxibugEmuOnline.Client/Assets/Scene/AxibugEmuOnline.Client.unity new file mode 100644 index 0000000..40d817e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Scene/AxibugEmuOnline.Client.unity @@ -0,0 +1,660 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &537454904 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 537454906} + - component: {fileID: 537454905} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &537454905 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 537454904} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &537454906 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 537454904} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &589359925 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 589359928} + - component: {fileID: 589359927} + - component: {fileID: 589359926} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &589359926 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 589359925} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &589359927 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 589359925} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &589359928 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 589359925} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &622947734 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 622947735} + - component: {fileID: 622947737} + - component: {fileID: 622947736} + m_Layer: 5 + m_Name: Game + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &622947735 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 622947734} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1361633018} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &622947736 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 622947734} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 0 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Texture: {fileID: 8400000, guid: 6645567e4c11d9447b1aee2406f681c5, type: 2} + m_UVRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 +--- !u!222 &622947737 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 622947734} + m_CullTransparentMesh: 0 +--- !u!1 &1355724343 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1355724346} + - component: {fileID: 1355724345} + - component: {fileID: 1355724344} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1355724344 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1355724343} + m_Enabled: 1 +--- !u!20 &1355724345 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1355724343} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1355724346 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1355724343} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1361633014 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1361633018} + - component: {fileID: 1361633017} + - component: {fileID: 1361633016} + - component: {fileID: 1361633015} + m_Layer: 5 + m_Name: UI + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1361633015 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1361633014} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &1361633016 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1361633014} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &1361633017 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1361633014} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &1361633018 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1361633014} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 622947735} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &1434955706 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1434955708} + - component: {fileID: 1434955709} + - component: {fileID: 1434955707} + m_Layer: 0 + m_Name: UNES + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1434955707 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1434955706} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5948479853ec1194497c60babb6bc213, type: 3} + m_Name: + m_EditorClassIdentifier: + RenderTexture: {fileID: 8400000, guid: 6645567e4c11d9447b1aee2406f681c5, type: 2} + FilterMode: 1 + LogicThread: 1 + KeyConfig: + Up: 119 + Down: 115 + Left: 97 + Right: 100 + A: 107 + B: 106 + Start: 98 + Select: 118 + Debug: 112 +--- !u!4 &1434955708 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1434955706} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1434955709 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1434955706} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8e94e6b3864b9034b89b70aa411a2894, type: 3} + m_Name: + m_EditorClassIdentifier: + RomFile: Mario + PlayerP1: 0 +--- !u!1 &1498586261 +GameObject: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1498586263} + - component: {fileID: 1498586262} + m_Layer: 0 + m_Name: UndoProRecords + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1498586262 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1498586261} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: df9ab10aeab793d47a27405557d0b929, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1498586263 +Transform: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1498586261} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/AxibugEmuOnline.Client/Assets/Scene/AxibugEmuOnline.Client.unity.meta b/AxibugEmuOnline.Client/Assets/Scene/AxibugEmuOnline.Client.unity.meta new file mode 100644 index 0000000..fa040cd --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Scene/AxibugEmuOnline.Client.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: eb0c18a619175384d95147898a43054b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script.meta b/AxibugEmuOnline.Client/Assets/Script.meta new file mode 100644 index 0000000..6d4a585 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cb2f15a5cdbcfcc498fecad6768c5ec9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs b/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs new file mode 100644 index 0000000..11c7815 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs @@ -0,0 +1,47 @@ +using AxibugEmuOnline.Client.Manager; +using AxibugEmuOnline.Client.Network; +using System; +using UnityEngine; +using static AxibugEmuOnline.Client.Manager.LogManager; + +namespace AxibugEmuOnline.Client.ClientCore +{ + public class AppAxibugEmuOnline + { + public static string TokenStr; + public static long RID = -1; + public static string IP; + public static int Port; + public static LogManager log; + public static NetworkHelper networkHelper; + public static AppLogin login; + public static AppChat chat; + public static UserDataManager user; + public static AppGame game; + + public static void Init() + { + log = new LogManager(); + LogManager.OnLog += OnNoSugarNetLog; + networkHelper = new NetworkHelper(); + login = new AppLogin(); + chat = new AppChat(); + user = new UserDataManager(); + game = new AppGame(); + } + + public static bool Connect(string IP, int port) + { + return networkHelper.Init(IP, port); + } + + public static void Close() + { + AppAxibugEmuOnline.log.Info("停止"); + } + static void OnNoSugarNetLog(int LogLevel, string msg) + { + Debug.Log("[AxibugEmuOnline]:"+msg); + } + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs.meta b/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs.meta new file mode 100644 index 0000000..308de77 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/AppAxibugEmuOnline.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 37a06c0960dd38d48a93b9642efa11f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Common.meta b/AxibugEmuOnline.Client/Assets/Script/Common.meta new file mode 100644 index 0000000..5c2638c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Common.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 22c07079f08c63e45929f98330d60397 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Common/Helper.cs b/AxibugEmuOnline.Client/Assets/Script/Common/Helper.cs new file mode 100644 index 0000000..f6dfde4 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Common/Helper.cs @@ -0,0 +1,22 @@ +using System; + +namespace AxibugEmuOnline.Client.Common +{ + public static class Helper + { + public static long GetNowTimeStamp() + { + return GetTimeStamp(DateTime.Now); + } + + /// + /// 获取时间戳 + /// + /// + public static long GetTimeStamp(DateTime dt) + { + TimeSpan ts = dt - new DateTime(1970, 1, 1, 0, 0, 0, 0); + return Convert.ToInt64(ts.TotalSeconds); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Common/Helper.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Common/Helper.cs.meta new file mode 100644 index 0000000..685e18e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Common/Helper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee9183acb0656474293cd9015f34b254 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Common/ProtoBufHelper.cs b/AxibugEmuOnline.Client/Assets/Script/Common/ProtoBufHelper.cs new file mode 100644 index 0000000..4ff0eab --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Common/ProtoBufHelper.cs @@ -0,0 +1,21 @@ +using Google.Protobuf; +using System; + +namespace AxibugEmuOnline.Client.Common +{ + public static class ProtoBufHelper + { + public static byte[] Serizlize(IMessage msg) + { + return msg.ToByteArray(); + } + public static T DeSerizlize(byte[] bytes) + { + var msgType = typeof(T); + object msg = Activator.CreateInstance(msgType); + ((IMessage)msg).MergeFrom(bytes); + return (T)msg; + } + } + +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Common/ProtoBufHelper.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Common/ProtoBufHelper.cs.meta new file mode 100644 index 0000000..e3edf51 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Common/ProtoBufHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6977f12ad405a314eacc4d9fbe1c631f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Event.meta b/AxibugEmuOnline.Client/Assets/Script/Event.meta new file mode 100644 index 0000000..fdb98cb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Event.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7fc94f95c97ea2947aae884b009b742c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Event/EEvent.cs b/AxibugEmuOnline.Client/Assets/Script/Event/EEvent.cs new file mode 100644 index 0000000..3896d91 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Event/EEvent.cs @@ -0,0 +1,8 @@ +namespace AxibugEmuOnline.Client.Event +{ + public enum EEvent + { + // 添加你自己需要的事件类型 + OnChatMsg + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Event/EEvent.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Event/EEvent.cs.meta new file mode 100644 index 0000000..8578a44 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Event/EEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb895131261b61748978c83bf9b27920 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs b/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs new file mode 100644 index 0000000..f729da0 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs @@ -0,0 +1,242 @@ +using AxibugEmuOnline.Client.ClientCore; +using System; +using System.Collections.Generic; + +namespace AxibugEmuOnline.Client.Event +{ + public class EventData + { + private static long BaseUid = 0; + private static long GenUid() + { + return (++BaseUid); + } + + /// + /// 唯一id + /// + public long uid { get; private set; } + + /// + /// 回调 + /// + public Delegate callback { get; private set; } + + public EventData(Delegate d) + { + uid = GenUid(); + callback = d; + } + } + + public class EventSystem + { + private static EventSystem instance = new EventSystem(); + public static EventSystem Instance { get { return instance; } } + + private Dictionary> eventDic = new Dictionary>(128); + + private EventSystem() { } + + + #region RegisterEvent + public void RegisterEvent(EEvent evt, Action callback) + { + InterRegisterEvent(evt, callback); + } + + public void RegisterEvent(EEvent evt, Action callback) + { + InterRegisterEvent(evt, callback); + } + + public void RegisterEvent(EEvent evt, Action callback) + { + InterRegisterEvent(evt, callback); + } + + public void RegisterEvent(EEvent evt, Action callback) + { + InterRegisterEvent(evt, callback); + } + + public void RegisterEvent(EEvent evt, Action callback) + { + InterRegisterEvent(evt, callback); + } + + private void InterRegisterEvent(EEvent evt, Delegate callback) + { + if (eventDic.ContainsKey(evt)) + { + if (eventDic[evt].IndexOf(callback) < 0) + { + eventDic[evt].Add(callback); + } + } + else + { + eventDic.Add(evt, new List() { callback }); + } + } + #endregion + + #region UnregisterEvent + + public void UnregisterEvent(EEvent evt, Action callback) + { + Delegate tempDelegate = callback; + InterUnregisterEvent(evt, tempDelegate); + } + + public void UnregisterEvent(EEvent evt, Action callback) + { + Delegate tempDelegate = callback; + InterUnregisterEvent(evt, tempDelegate); + } + + public void UnregisterEvent(EEvent evt, Action callback) + { + Delegate tempDelegate = callback; + InterUnregisterEvent(evt, tempDelegate); + } + + public void UnregisterEvent(EEvent evt, Action callback) + { + Delegate tempDelegate = callback; + InterUnregisterEvent(evt, tempDelegate); + } + + public void UnregisterEvent(EEvent evt, Action callback) + { + Delegate tempDelegate = callback; + InterUnregisterEvent(evt, tempDelegate); + } + + private void InterUnregisterEvent(EEvent evt, Delegate callback) + { + if (eventDic.ContainsKey(evt)) + { + eventDic[evt].Remove(callback); + if (eventDic[evt].Count == 0) eventDic.Remove(evt); + } + } + #endregion + + #region PostEvent + public void PostEvent(EEvent evt, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + List eventList = GetEventList(evt); + if (eventList != null) + { + foreach (Delegate callback in eventList) + { + try + { + ((Action)callback)(arg1, arg2, arg3, arg4); + } + catch (Exception e) + { + AppAxibugEmuOnline.log.Error(e.Message); + } + } + } + } + + public void PostEvent(EEvent evt, T1 arg1, T2 arg2, T3 arg3) + { + List eventList = GetEventList(evt); + if (eventList != null) + { + foreach (Delegate callback in eventList) + { + try + { + ((Action)callback)(arg1, arg2, arg3); + } + catch (Exception e) + { + AppAxibugEmuOnline.log.Error(e.Message); + } + } + } + } + + public void PostEvent(EEvent evt, T1 arg1, T2 arg2) + { + List eventList = GetEventList(evt); + if (eventList != null) + { + foreach (Delegate callback in eventList) + { + try + { + ((Action)callback)(arg1, arg2); + } + catch (Exception e) + { + AppAxibugEmuOnline.log.Error(e.Message); + } + } + } + } + + public void PostEvent(EEvent evt, T arg) + { + List eventList = GetEventList(evt); + if (eventList != null) + { + foreach (Delegate callback in eventList) + { + try + { + ((Action)callback)(arg); + } + catch (Exception e) + { + AppAxibugEmuOnline.log.Error(e.Message + ", method name : " + callback.Method); + } + } + } + + } + + public void PostEvent(EEvent evt) + { + List eventList = GetEventList(evt); + if (eventList != null) + { + foreach (Delegate callback in eventList) + { + try + { + ((Action)callback)(); + } + catch (Exception e) + { + AppAxibugEmuOnline.log.Error(e.Message); + } + } + } + } + #endregion + + /// + /// 获取所有事件 + /// + /// + /// + private List GetEventList(EEvent evt) + { + if (eventDic.ContainsKey(evt)) + { + List tempList = eventDic[evt]; + if (null != tempList) + { + return tempList; + } + } + return null; + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs.meta new file mode 100644 index 0000000..f540b19 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Event/EventSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a03cd2c9855b3647b7b2cd9cb9119ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager.meta b/AxibugEmuOnline.Client/Assets/Script/Manager.meta new file mode 100644 index 0000000..7cf740f --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 437646a1943eaf34dbe1a56eb38494bb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs new file mode 100644 index 0000000..a1b5333 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs @@ -0,0 +1,31 @@ +using AxibugEmuOnline.Client.ClientCore; +using AxibugEmuOnline.Client.Common; +using AxibugEmuOnline.Client.Event; +using AxibugEmuOnline.Client.Network; +using AxibugProtobuf; + +namespace AxibugEmuOnline.Client.Manager +{ + public class AppChat + { + public AppChat() + { + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdChatmsg, RecvChatMsg); + } + + public void SendChatMsg(string ChatMsg) + { + Protobuf_ChatMsg msg = new Protobuf_ChatMsg() + { + ChatMsg = ChatMsg, + }; + AppAxibugEmuOnline.networkHelper.SendToServer((int)CommandID.CmdChatmsg, ProtoBufHelper.Serizlize(msg)); + } + + public void RecvChatMsg(byte[] reqData) + { + Protobuf_ChatMsg_RESP msg = ProtoBufHelper.DeSerizlize(reqData); + EventSystem.Instance.PostEvent(EEvent.OnChatMsg, msg.NickName, msg.ChatMsg); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs.meta new file mode 100644 index 0000000..be2baff --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppChat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7e9dbada150393488fa949affc5fe3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppGame.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/AppGame.cs new file mode 100644 index 0000000..3258c36 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppGame.cs @@ -0,0 +1,76 @@ +using AxibugEmuOnline.Client.ClientCore; +using AxibugEmuOnline.Client.Common; +using AxibugEmuOnline.Client.Network; +using AxibugProtobuf; +using AxibugEmuOnline.Client.UNES; +using Google.Protobuf; +using System.IO; +using System.IO.Compression; +using System.Linq; + +namespace AxibugEmuOnline.Client.Manager +{ + public class AppGame + { + public AppGame() + { + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdScreen, OnScreen); + } + Protobuf_Screnn_Frame _Protobuf_Screnn_Frame = new Protobuf_Screnn_Frame(); + + public uint[] RawBitmap { get; set; } = new uint[UNESBehaviour.GameWidth * UNESBehaviour.GameHeight]; + // TODO: use real chroma/luma decoding + private readonly uint[] _palette = { + 0x7C7C7C, 0x0000FC, 0x0000BC, 0x4428BC, 0x940084, 0xA80020, 0xA81000, 0x881400, + 0x503000, 0x007800, 0x006800, 0x005800, 0x004058, 0x000000, 0x000000, 0x000000, + 0xBCBCBC, 0x0078F8, 0x0058F8, 0x6844FC, 0xD800CC, 0xE40058, 0xF83800, 0xE45C10, + 0xAC7C00, 0x00B800, 0x00A800, 0x00A844, 0x008888, 0x000000, 0x000000, 0x000000, + 0xF8F8F8, 0x3CBCFC, 0x6888FC, 0x9878F8, 0xF878F8, 0xF85898, 0xF87858, 0xFCA044, + 0xF8B800, 0xB8F818, 0x58D854, 0x58F898, 0x00E8D8, 0x787878, 0x000000, 0x000000, + 0xFCFCFC, 0xA4E4FC, 0xB8B8F8, 0xD8B8F8, 0xF8B8F8, 0xF8A4C0, 0xF0D0B0, 0xFCE0A8, + 0xF8D878, 0xD8F878, 0xB8F8B8, 0xB8F8D8, 0x00FCFC, 0xF8D8F8, 0x000000, 0x000000 + }; + public void SendScreen(byte[] ScreenData) + { + byte[] comData = CompressByteArray(ScreenData); + _Protobuf_Screnn_Frame.FrameID = 0; + _Protobuf_Screnn_Frame.RawBitmap = ByteString.CopyFrom(comData); + AppAxibugEmuOnline.networkHelper.SendToServer((int)CommandID.CmdScreen, ProtoBufHelper.Serizlize(_Protobuf_Screnn_Frame)); + } + + public void OnScreen(byte[] reqData) + { + Protobuf_Screnn_Frame msg = ProtoBufHelper.DeSerizlize(reqData); + lock (RawBitmap) + { + byte[] data = DecompressByteArray(msg.RawBitmap.ToArray()); + for (int i = 0; i < data.Length; i++) + { + RawBitmap[i] = _palette[data[i]]; + } + } + } + + public static byte[] CompressByteArray(byte[] bytesToCompress) + { + using (var compressedMemoryStream = new MemoryStream()) + using (var gzipStream = new GZipStream(compressedMemoryStream, CompressionMode.Compress)) + { + gzipStream.Write(bytesToCompress, 0, bytesToCompress.Length); + gzipStream.Close(); + return compressedMemoryStream.ToArray(); + } + } + + public static byte[] DecompressByteArray(byte[] compressedBytes) + { + using (var compressedMemoryStream = new MemoryStream(compressedBytes)) + using (var gzipStream = new GZipStream(compressedMemoryStream, CompressionMode.Decompress)) + using (var resultMemoryStream = new MemoryStream()) + { + gzipStream.CopyTo(resultMemoryStream); + return resultMemoryStream.ToArray(); + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppGame.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Manager/AppGame.cs.meta new file mode 100644 index 0000000..22078bd --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppGame.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f7ca4d4fcf6c0f41a6ead44beed31dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppLogin.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/AppLogin.cs new file mode 100644 index 0000000..4185a62 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppLogin.cs @@ -0,0 +1,46 @@ +using AxibugEmuOnline.Client.ClientCore; +using AxibugEmuOnline.Client.Common; +using AxibugEmuOnline.Client.Network; +using AxibugProtobuf; +using System; + +namespace AxibugEmuOnline.Client.Manager +{ + public class AppLogin + { + static string LastLoginGuid = ""; + public AppLogin() + { + NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdLogin, RecvLoginMsg); + } + + public void Login() + { + AppAxibugEmuOnline.log.Debug("-->Login"); + if(string.IsNullOrEmpty(LastLoginGuid)) + LastLoginGuid = Guid.NewGuid().ToString(); + + AppAxibugEmuOnline.user.userdata.Account = LastLoginGuid; + Protobuf_Login msg = new Protobuf_Login() + { + LoginType = 0, + Account = AppAxibugEmuOnline.user.userdata.Account, + }; + AppAxibugEmuOnline.networkHelper.SendToServer((int)CommandID.CmdLogin, ProtoBufHelper.Serizlize(msg)); + } + + public void RecvLoginMsg(byte[] reqData) + { + Protobuf_Login_RESP msg = ProtoBufHelper.DeSerizlize(reqData); + if (msg.Status == LoginResultStatus.Ok) + { + AppAxibugEmuOnline.log.Info("登录成功"); + AppAxibugEmuOnline.user.InitMainUserData(AppAxibugEmuOnline.user.userdata.Account,msg.UID); + } + else + { + AppAxibugEmuOnline.log.Info("登录失败"); + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/AppLogin.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Manager/AppLogin.cs.meta new file mode 100644 index 0000000..aeed966 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/AppLogin.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8bd5c1c74a8a42749809545ab685696e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/LogManager.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/LogManager.cs new file mode 100644 index 0000000..6326887 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/LogManager.cs @@ -0,0 +1,53 @@ +namespace AxibugEmuOnline.Client.Manager +{ + public class LogManager + { + public enum E_LogType:byte + { + Info = 0, + Debug = 1, + Warning = 2, + Error = 3, + } + /// + /// 日志 + /// + /// + public delegate void OnLogHandler(int debuglv,string msg); + + /// + /// 内部输出 + /// + public static event OnLogHandler OnLog; + + public void Info(string str) + { + Log(E_LogType.Info, str); + } + + public void Debug(string str) + { + Log(E_LogType.Debug, str); + } + + public void Warning(string str) + { + Log(E_LogType.Warning, str); + } + + public void Error(string str) + { + Log(E_LogType.Error, str); + } + + public void Log(E_LogType logtype,string str) + { + OnLog?.Invoke((int)logtype, str); + } + + public void Log(int logtype, string str) + { + OnLog?.Invoke(logtype, str); + } + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/LogManager.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Manager/LogManager.cs.meta new file mode 100644 index 0000000..c6bb5f5 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/LogManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc0abf46c60a4d64381ea53f9ddc89b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/UserDataManager.cs b/AxibugEmuOnline.Client/Assets/Script/Manager/UserDataManager.cs new file mode 100644 index 0000000..6816739 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/UserDataManager.cs @@ -0,0 +1,58 @@ +using AxibugEmuOnline.Client.ClientCore; +using AxibugProtobuf; + +namespace AxibugEmuOnline.Client.Manager +{ + public class UserDataBase + { + public long UID { get; set; } + public string Account { get; set; } + } + + public class MainUserDataBase : UserDataBase + { + public bool IsLoggedIn { get; set; } = false; + } + + public class UserDataManager + { + public UserDataManager() + { + //注册重连成功事件,以便后续自动登录 + AppAxibugEmuOnline.networkHelper.OnReConnected += OnReConnected; + } + public MainUserDataBase userdata { get;private set; } = new MainUserDataBase(); + public bool IsLoggedIn => userdata.IsLoggedIn; + + public void InitMainUserData(string UName,long UID) + { + userdata.Account = UName; + userdata.IsLoggedIn = true; + userdata.UID = UID; + //以及其他数据初始化 + //... + } + + /// + /// 登出 + /// + public void LoginOutData() + { + userdata.IsLoggedIn = false; + //以及其他数据清理 + //... + } + + /// + /// 当重连成功 + /// + public void OnReConnected() + { + //如果之前已登录,则重新登录 + if (userdata.IsLoggedIn) + { + AppAxibugEmuOnline.login.Login(); + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Manager/UserDataManager.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Manager/UserDataManager.cs.meta new file mode 100644 index 0000000..d925551 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Manager/UserDataManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: db9b113b5261d564aa86816443a02e25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Network.meta b/AxibugEmuOnline.Client/Assets/Script/Network.meta new file mode 100644 index 0000000..86d9106 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Network.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 06db94811d3239e4e820a598c934ddb5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Network/NetMsg.cs b/AxibugEmuOnline.Client/Assets/Script/Network/NetMsg.cs new file mode 100644 index 0000000..c696c9a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Network/NetMsg.cs @@ -0,0 +1,98 @@ +using AxibugEmuOnline.Client.ClientCore; +using System; +using System.Collections.Generic; + +namespace AxibugEmuOnline.Client.Network +{ + + public class NetMsg + { + private static NetMsg instance = new NetMsg(); + public static NetMsg Instance { get { return instance; } } + + private Dictionary> netEventDic = new Dictionary>(128); + + private NetMsg() { } + + + #region RegisterMsgEvent + + public void RegNetMsgEvent(int cmd, Action callback) + { + InterRegNetMsgEvent(cmd, callback); + } + + private void InterRegNetMsgEvent(int cmd, Delegate callback) + { + if (netEventDic.ContainsKey(cmd)) + { + if (netEventDic[cmd].IndexOf(callback) < 0) + { + netEventDic[cmd].Add(callback); + } + } + else + { + netEventDic.Add(cmd, new List() { callback }); + } + } + #endregion + + #region UnregisterCMD + + public void UnregisterCMD(int evt, Action callback) + { + Delegate tempDelegate = callback; + InterUnregisterCMD(evt, tempDelegate); + } + + private void InterUnregisterCMD(int cmd, Delegate callback) + { + if (netEventDic.ContainsKey(cmd)) + { + netEventDic[cmd].Remove(callback); + if (netEventDic[cmd].Count == 0) netEventDic.Remove(cmd); + } + } + #endregion + + #region PostEvent + public void PostNetMsgEvent(int cmd, byte[] arg) + { + List eventList = GetNetEventDicList(cmd); + if (eventList != null) + { + foreach (Delegate callback in eventList) + { + try + { + ((Action)callback)(arg); + } + catch (Exception e) + { + AppAxibugEmuOnline.log.Error(e.Message); + } + } + } + } + #endregion + + /// + /// 获取所有事件 + /// + /// + /// + private List GetNetEventDicList(int cmd) + { + if (netEventDic.ContainsKey(cmd)) + { + List tempList = netEventDic[cmd]; + if (null != tempList) + { + return tempList; + } + } + return null; + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Network/NetMsg.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Network/NetMsg.cs.meta new file mode 100644 index 0000000..a288a04 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Network/NetMsg.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1e196e9f86b5d194a8ac78430e1c9d5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Network/NetworkHelper.cs b/AxibugEmuOnline.Client/Assets/Script/Network/NetworkHelper.cs new file mode 100644 index 0000000..ab6b305 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Network/NetworkHelper.cs @@ -0,0 +1,131 @@ +using AxibugEmuOnline.Client.ClientCore; +using HaoYueNet.ClientNetworkNet.Standard2; +using System; +using System.Threading; + +namespace AxibugEmuOnline.Client.Network +{ + /// + /// 继承网络库,以支持网络功能 + /// + public class NetworkHelper : NetworkHelperCore + { + public NetworkHelper() + { + //指定接收服务器数据事件 + OnReceiveData += GetDataCallBack; + //断开连接 + OnClose += OnConnectClose; + OnConnected += NetworkConnected; + //网络库调试信息输出事件,用于打印网络内容 + OnLogOut += NetworkDeBugLog; + } + + public delegate void OnReConnectedHandler(); + /// + /// 重连成功事件 + /// + public event OnReConnectedHandler OnReConnected; + /// + /// 是否自动重连 + /// + public bool bAutoReConnect = false; + /// + /// 重连尝试时间 + /// + const int ReConnectTryTime = 1000; + + public void NetworkConnected(bool IsConnect) + { + NetworkDeBugLog($"NetworkConnected:{IsConnect}"); + if (IsConnect) + { + //从未登录过 + if (!AppAxibugEmuOnline.user.IsLoggedIn) + { + //首次登录 + AppAxibugEmuOnline.login.Login(); + } + } + else + { + //连接失败 + NetworkDeBugLog("连接失败!"); + + + //自动重连开关 + if (bAutoReConnect) + ReConnect(); + } + } + + public void NetworkDeBugLog(string str) + { + //用于Unity内的输出 + //Debug.Log("NetCoreDebug >> "+str); + AppAxibugEmuOnline.log.Info("NetCoreDebug >> " + str); + } + + /// + /// 接受包回调 + /// + /// 协议ID + /// 错误编号 + /// 业务数据 + public void GetDataCallBack(int CMDID, int ERRCODE, byte[] data) + { + //NetworkDeBugLog("收到消息 CMDID =>" + CMDID + " ERRCODE =>" + ERRCODE + " 数据长度=>" + data.Length); + try + { + //抛出网络数据 + NetMsg.Instance.PostNetMsgEvent(CMDID, data); + } + catch (Exception ex) + { + NetworkDeBugLog("逻辑处理错误:" + ex.ToString()); + } + } + + /// + /// 关闭连接 + /// + public void OnConnectClose() + { + NetworkDeBugLog("OnConnectClose"); + + + //自动重连开关 + if (bAutoReConnect) + ReConnect(); + } + + + bool bInReConnecting = false; + /// + /// 自动重连 + /// + void ReConnect() + { + if (bInReConnecting) + return; + bInReConnecting = true; + + bool bflagDone = false; + do + { + //等待时间 + Thread.Sleep(ReConnectTryTime); + AppAxibugEmuOnline.log.Info($"尝试自动重连{LastConnectIP}:{LastConnectPort}……"); + //第一步 + if (Init(LastConnectIP, LastConnectPort)) + { + AppAxibugEmuOnline.log.Info($"自动重连成功!"); + bflagDone = true; + AppAxibugEmuOnline.log.Info($"触发重连后的自动逻辑!"); + OnReConnected?.Invoke(); + } + } while (!bflagDone); + bInReConnecting = false; + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/Network/NetworkHelper.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Network/NetworkHelper.cs.meta new file mode 100644 index 0000000..4e948d9 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Network/NetworkHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ffc8e86b25d7476429d566e86268e99f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Protobuf.meta b/AxibugEmuOnline.Client/Assets/Script/Protobuf.meta new file mode 100644 index 0000000..c612430 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Protobuf.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 982424ed2d905a74e94341fa6547e304 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/Protobuf/ProtobufAxibugEmuOnline.cs b/AxibugEmuOnline.Client/Assets/Script/Protobuf/ProtobufAxibugEmuOnline.cs new file mode 100644 index 0000000..b23e13b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Protobuf/ProtobufAxibugEmuOnline.cs @@ -0,0 +1,1398 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: protobuf_AxibugEmuOnline.proto +// +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace AxibugProtobuf { + + /// Holder for reflection information generated from protobuf_AxibugEmuOnline.proto + public static partial class ProtobufAxibugEmuOnlineReflection { + + #region Descriptor + /// File descriptor for protobuf_AxibugEmuOnline.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static ProtobufAxibugEmuOnlineReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "Ch5wcm90b2J1Zl9BeGlidWdFbXVPbmxpbmUucHJvdG8SDkF4aWJ1Z1Byb3Rv", + "YnVmIiMKEFByb3RvYnVmX0NoYXRNc2cSDwoHQ2hhdE1zZxgBIAEoCSJIChVQ", + "cm90b2J1Zl9DaGF0TXNnX1JFU1ASEAoITmlja05hbWUYASABKAkSDwoHQ2hh", + "dE1zZxgCIAEoCRIMCgREYXRlGAMgASgDIpEBCg5Qcm90b2J1Zl9Mb2dpbhIs", + "Cglsb2dpblR5cGUYASABKA4yGS5BeGlidWdQcm90b2J1Zi5Mb2dpblR5cGUS", + "LgoKZGV2aWNlVHlwZRgCIAEoDjIaLkF4aWJ1Z1Byb3RvYnVmLkRldmljZVR5", + "cGUSDwoHQWNjb3VudBgDIAEoCRIQCghQYXNzd29yZBgEIAEoCSKMAQoTUHJv", + "dG9idWZfTG9naW5fUkVTUBINCgVUb2tlbhgBIAEoCRIVCg1MYXN0TG9naW5E", + "YXRlGAIgASgJEg8KB1JlZ0RhdGUYAyABKAkSMQoGU3RhdHVzGAQgASgOMiEu", + "QXhpYnVnUHJvdG9idWYuTG9naW5SZXN1bHRTdGF0dXMSCwoDVUlEGAUgASgD", + "IjsKFVByb3RvYnVmX1NjcmVubl9GcmFtZRIPCgdGcmFtZUlEGAEgASgFEhEK", + "CVJhd0JpdG1hcBgCIAEoDCpOCglDb21tYW5kSUQSDgoKQ01EX0RFRkFVTBAA", + "Eg4KCUNNRF9MT0dJThDRDxIQCgtDTURfQ0hBVE1TRxChHxIPCgpDTURfU2Ny", + "ZWVuEIknKisKCUVycm9yQ29kZRIQCgxFUlJPUl9ERUZBVUwQABIMCghFUlJP", + "Ul9PSxABKhwKCUxvZ2luVHlwZRIPCgtCYXNlRGVmYXVsdBAAKksKCkRldmlj", + "ZVR5cGUSFgoSRGV2aWNlVHlwZV9EZWZhdWx0EAASBgoCUEMQARILCgdBbmRy", + "b2lkEAISBwoDSU9TEAMSBwoDUFNWEAQqTgoRTG9naW5SZXN1bHRTdGF0dXMS", + "IQodTG9naW5SZXN1bHRTdGF0dXNfQmFzZURlZmF1bHQQABIGCgJPSxABEg4K", + "CkFjY291bnRFcnIQAkICSAFiBnByb3RvMw==")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(new[] {typeof(global::AxibugProtobuf.CommandID), typeof(global::AxibugProtobuf.ErrorCode), typeof(global::AxibugProtobuf.LoginType), typeof(global::AxibugProtobuf.DeviceType), typeof(global::AxibugProtobuf.LoginResultStatus), }, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::AxibugProtobuf.Protobuf_ChatMsg), global::AxibugProtobuf.Protobuf_ChatMsg.Parser, new[]{ "ChatMsg" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::AxibugProtobuf.Protobuf_ChatMsg_RESP), global::AxibugProtobuf.Protobuf_ChatMsg_RESP.Parser, new[]{ "NickName", "ChatMsg", "Date" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::AxibugProtobuf.Protobuf_Login), global::AxibugProtobuf.Protobuf_Login.Parser, new[]{ "LoginType", "DeviceType", "Account", "Password" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::AxibugProtobuf.Protobuf_Login_RESP), global::AxibugProtobuf.Protobuf_Login_RESP.Parser, new[]{ "Token", "LastLoginDate", "RegDate", "Status", "UID" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::AxibugProtobuf.Protobuf_Screnn_Frame), global::AxibugProtobuf.Protobuf_Screnn_Frame.Parser, new[]{ "FrameID", "RawBitmap" }, null, null, null, null) + })); + } + #endregion + + } + #region Enums + public enum CommandID { + /// + ///缺省不使用 + /// + [pbr::OriginalName("CMD_DEFAUL")] CmdDefaul = 0, + /// + ///自动登录上行 | 下行 对应 Protobuf_Login | Protobuf_Login_RESP + /// + [pbr::OriginalName("CMD_LOGIN")] CmdLogin = 2001, + /// + ///广播信息上行 | 下行 对应 Protobuf_ChatMsg | Protobuf_ChatMsg_RESP + /// + [pbr::OriginalName("CMD_CHATMSG")] CmdChatmsg = 4001, + /// + ///画面同步 | 同步广播 对应 Protobuf_Screnn_Frame + /// + [pbr::OriginalName("CMD_Screen")] CmdScreen = 5001, + } + + public enum ErrorCode { + /// + ///缺省不使用 + /// + [pbr::OriginalName("ERROR_DEFAUL")] ErrorDefaul = 0, + /// + ///成功 + /// + [pbr::OriginalName("ERROR_OK")] ErrorOk = 1, + } + + public enum LoginType { + /// + ///缺省不使用 + /// + [pbr::OriginalName("BaseDefault")] BaseDefault = 0, + } + + public enum DeviceType { + /// + ///缺省不使用 + /// + [pbr::OriginalName("DeviceType_Default")] Default = 0, + [pbr::OriginalName("PC")] Pc = 1, + [pbr::OriginalName("Android")] Android = 2, + [pbr::OriginalName("IOS")] Ios = 3, + [pbr::OriginalName("PSV")] Psv = 4, + } + + public enum LoginResultStatus { + /// + ///缺省不使用 + /// + [pbr::OriginalName("LoginResultStatus_BaseDefault")] BaseDefault = 0, + [pbr::OriginalName("OK")] Ok = 1, + [pbr::OriginalName("AccountErr")] AccountErr = 2, + } + + #endregion + + #region Messages + /// + ///聊天 上行 + /// + public sealed partial class Protobuf_ChatMsg : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Protobuf_ChatMsg()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::AxibugProtobuf.ProtobufAxibugEmuOnlineReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_ChatMsg() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_ChatMsg(Protobuf_ChatMsg other) : this() { + chatMsg_ = other.chatMsg_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_ChatMsg Clone() { + return new Protobuf_ChatMsg(this); + } + + /// Field number for the "ChatMsg" field. + public const int ChatMsgFieldNumber = 1; + private string chatMsg_ = ""; + /// + ///消息 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string ChatMsg { + get { return chatMsg_; } + set { + chatMsg_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Protobuf_ChatMsg); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Protobuf_ChatMsg other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ChatMsg != other.ChatMsg) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (ChatMsg.Length != 0) hash ^= ChatMsg.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (ChatMsg.Length != 0) { + output.WriteRawTag(10); + output.WriteString(ChatMsg); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (ChatMsg.Length != 0) { + output.WriteRawTag(10); + output.WriteString(ChatMsg); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (ChatMsg.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ChatMsg); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Protobuf_ChatMsg other) { + if (other == null) { + return; + } + if (other.ChatMsg.Length != 0) { + ChatMsg = other.ChatMsg; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + ChatMsg = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + ChatMsg = input.ReadString(); + break; + } + } + } + } + #endif + + } + + /// + ///聊天 下行 + /// + public sealed partial class Protobuf_ChatMsg_RESP : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Protobuf_ChatMsg_RESP()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::AxibugProtobuf.ProtobufAxibugEmuOnlineReflection.Descriptor.MessageTypes[1]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_ChatMsg_RESP() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_ChatMsg_RESP(Protobuf_ChatMsg_RESP other) : this() { + nickName_ = other.nickName_; + chatMsg_ = other.chatMsg_; + date_ = other.date_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_ChatMsg_RESP Clone() { + return new Protobuf_ChatMsg_RESP(this); + } + + /// Field number for the "NickName" field. + public const int NickNameFieldNumber = 1; + private string nickName_ = ""; + /// + ///昵称 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string NickName { + get { return nickName_; } + set { + nickName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "ChatMsg" field. + public const int ChatMsgFieldNumber = 2; + private string chatMsg_ = ""; + /// + ///消息 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string ChatMsg { + get { return chatMsg_; } + set { + chatMsg_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "Date" field. + public const int DateFieldNumber = 3; + private long date_; + /// + ///时间 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public long Date { + get { return date_; } + set { + date_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Protobuf_ChatMsg_RESP); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Protobuf_ChatMsg_RESP other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (NickName != other.NickName) return false; + if (ChatMsg != other.ChatMsg) return false; + if (Date != other.Date) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (NickName.Length != 0) hash ^= NickName.GetHashCode(); + if (ChatMsg.Length != 0) hash ^= ChatMsg.GetHashCode(); + if (Date != 0L) hash ^= Date.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (NickName.Length != 0) { + output.WriteRawTag(10); + output.WriteString(NickName); + } + if (ChatMsg.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ChatMsg); + } + if (Date != 0L) { + output.WriteRawTag(24); + output.WriteInt64(Date); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (NickName.Length != 0) { + output.WriteRawTag(10); + output.WriteString(NickName); + } + if (ChatMsg.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ChatMsg); + } + if (Date != 0L) { + output.WriteRawTag(24); + output.WriteInt64(Date); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (NickName.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(NickName); + } + if (ChatMsg.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ChatMsg); + } + if (Date != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Date); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Protobuf_ChatMsg_RESP other) { + if (other == null) { + return; + } + if (other.NickName.Length != 0) { + NickName = other.NickName; + } + if (other.ChatMsg.Length != 0) { + ChatMsg = other.ChatMsg; + } + if (other.Date != 0L) { + Date = other.Date; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + NickName = input.ReadString(); + break; + } + case 18: { + ChatMsg = input.ReadString(); + break; + } + case 24: { + Date = input.ReadInt64(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + NickName = input.ReadString(); + break; + } + case 18: { + ChatMsg = input.ReadString(); + break; + } + case 24: { + Date = input.ReadInt64(); + break; + } + } + } + } + #endif + + } + + /// + ///登录数据上行 + /// + public sealed partial class Protobuf_Login : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Protobuf_Login()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::AxibugProtobuf.ProtobufAxibugEmuOnlineReflection.Descriptor.MessageTypes[2]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Login() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Login(Protobuf_Login other) : this() { + loginType_ = other.loginType_; + deviceType_ = other.deviceType_; + account_ = other.account_; + password_ = other.password_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Login Clone() { + return new Protobuf_Login(this); + } + + /// Field number for the "loginType" field. + public const int LoginTypeFieldNumber = 1; + private global::AxibugProtobuf.LoginType loginType_ = global::AxibugProtobuf.LoginType.BaseDefault; + /// + ///登录操作类型 [0]皓月通行证 [3] 皓月BF3 [4] 皓月BF4 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::AxibugProtobuf.LoginType LoginType { + get { return loginType_; } + set { + loginType_ = value; + } + } + + /// Field number for the "deviceType" field. + public const int DeviceTypeFieldNumber = 2; + private global::AxibugProtobuf.DeviceType deviceType_ = global::AxibugProtobuf.DeviceType.Default; + /// + ///设备类型 [0]PC [1]AndroidPad预留 [3]IPad预留 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::AxibugProtobuf.DeviceType DeviceType { + get { return deviceType_; } + set { + deviceType_ = value; + } + } + + /// Field number for the "Account" field. + public const int AccountFieldNumber = 3; + private string account_ = ""; + /// + ///用户名 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Account { + get { return account_; } + set { + account_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "Password" field. + public const int PasswordFieldNumber = 4; + private string password_ = ""; + /// + ///密码 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Password { + get { return password_; } + set { + password_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Protobuf_Login); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Protobuf_Login other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (LoginType != other.LoginType) return false; + if (DeviceType != other.DeviceType) return false; + if (Account != other.Account) return false; + if (Password != other.Password) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (LoginType != global::AxibugProtobuf.LoginType.BaseDefault) hash ^= LoginType.GetHashCode(); + if (DeviceType != global::AxibugProtobuf.DeviceType.Default) hash ^= DeviceType.GetHashCode(); + if (Account.Length != 0) hash ^= Account.GetHashCode(); + if (Password.Length != 0) hash ^= Password.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (LoginType != global::AxibugProtobuf.LoginType.BaseDefault) { + output.WriteRawTag(8); + output.WriteEnum((int) LoginType); + } + if (DeviceType != global::AxibugProtobuf.DeviceType.Default) { + output.WriteRawTag(16); + output.WriteEnum((int) DeviceType); + } + if (Account.Length != 0) { + output.WriteRawTag(26); + output.WriteString(Account); + } + if (Password.Length != 0) { + output.WriteRawTag(34); + output.WriteString(Password); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (LoginType != global::AxibugProtobuf.LoginType.BaseDefault) { + output.WriteRawTag(8); + output.WriteEnum((int) LoginType); + } + if (DeviceType != global::AxibugProtobuf.DeviceType.Default) { + output.WriteRawTag(16); + output.WriteEnum((int) DeviceType); + } + if (Account.Length != 0) { + output.WriteRawTag(26); + output.WriteString(Account); + } + if (Password.Length != 0) { + output.WriteRawTag(34); + output.WriteString(Password); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (LoginType != global::AxibugProtobuf.LoginType.BaseDefault) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) LoginType); + } + if (DeviceType != global::AxibugProtobuf.DeviceType.Default) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) DeviceType); + } + if (Account.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Account); + } + if (Password.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Password); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Protobuf_Login other) { + if (other == null) { + return; + } + if (other.LoginType != global::AxibugProtobuf.LoginType.BaseDefault) { + LoginType = other.LoginType; + } + if (other.DeviceType != global::AxibugProtobuf.DeviceType.Default) { + DeviceType = other.DeviceType; + } + if (other.Account.Length != 0) { + Account = other.Account; + } + if (other.Password.Length != 0) { + Password = other.Password; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + LoginType = (global::AxibugProtobuf.LoginType) input.ReadEnum(); + break; + } + case 16: { + DeviceType = (global::AxibugProtobuf.DeviceType) input.ReadEnum(); + break; + } + case 26: { + Account = input.ReadString(); + break; + } + case 34: { + Password = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + LoginType = (global::AxibugProtobuf.LoginType) input.ReadEnum(); + break; + } + case 16: { + DeviceType = (global::AxibugProtobuf.DeviceType) input.ReadEnum(); + break; + } + case 26: { + Account = input.ReadString(); + break; + } + case 34: { + Password = input.ReadString(); + break; + } + } + } + } + #endif + + } + + /// + ///登录数据下行 + /// + public sealed partial class Protobuf_Login_RESP : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Protobuf_Login_RESP()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::AxibugProtobuf.ProtobufAxibugEmuOnlineReflection.Descriptor.MessageTypes[3]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Login_RESP() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Login_RESP(Protobuf_Login_RESP other) : this() { + token_ = other.token_; + lastLoginDate_ = other.lastLoginDate_; + regDate_ = other.regDate_; + status_ = other.status_; + uID_ = other.uID_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Login_RESP Clone() { + return new Protobuf_Login_RESP(this); + } + + /// Field number for the "Token" field. + public const int TokenFieldNumber = 1; + private string token_ = ""; + /// + ///登录凭据 (本次登录之后,所有业务请求凭据,需要存储在内存中) + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Token { + get { return token_; } + set { + token_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "LastLoginDate" field. + public const int LastLoginDateFieldNumber = 2; + private string lastLoginDate_ = ""; + /// + ///上次登录时间(只用于呈现的字符串,若界面需求需要) + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string LastLoginDate { + get { return lastLoginDate_; } + set { + lastLoginDate_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "RegDate" field. + public const int RegDateFieldNumber = 3; + private string regDate_ = ""; + /// + ///注册时间(只用于呈现的字符串,若界面需求需要) + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string RegDate { + get { return regDate_; } + set { + regDate_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "Status" field. + public const int StatusFieldNumber = 4; + private global::AxibugProtobuf.LoginResultStatus status_ = global::AxibugProtobuf.LoginResultStatus.BaseDefault; + /// + ///账号状态 (预留) [1]正常[0]被禁封 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::AxibugProtobuf.LoginResultStatus Status { + get { return status_; } + set { + status_ = value; + } + } + + /// Field number for the "UID" field. + public const int UIDFieldNumber = 5; + private long uID_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public long UID { + get { return uID_; } + set { + uID_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Protobuf_Login_RESP); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Protobuf_Login_RESP other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Token != other.Token) return false; + if (LastLoginDate != other.LastLoginDate) return false; + if (RegDate != other.RegDate) return false; + if (Status != other.Status) return false; + if (UID != other.UID) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (Token.Length != 0) hash ^= Token.GetHashCode(); + if (LastLoginDate.Length != 0) hash ^= LastLoginDate.GetHashCode(); + if (RegDate.Length != 0) hash ^= RegDate.GetHashCode(); + if (Status != global::AxibugProtobuf.LoginResultStatus.BaseDefault) hash ^= Status.GetHashCode(); + if (UID != 0L) hash ^= UID.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (Token.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Token); + } + if (LastLoginDate.Length != 0) { + output.WriteRawTag(18); + output.WriteString(LastLoginDate); + } + if (RegDate.Length != 0) { + output.WriteRawTag(26); + output.WriteString(RegDate); + } + if (Status != global::AxibugProtobuf.LoginResultStatus.BaseDefault) { + output.WriteRawTag(32); + output.WriteEnum((int) Status); + } + if (UID != 0L) { + output.WriteRawTag(40); + output.WriteInt64(UID); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (Token.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Token); + } + if (LastLoginDate.Length != 0) { + output.WriteRawTag(18); + output.WriteString(LastLoginDate); + } + if (RegDate.Length != 0) { + output.WriteRawTag(26); + output.WriteString(RegDate); + } + if (Status != global::AxibugProtobuf.LoginResultStatus.BaseDefault) { + output.WriteRawTag(32); + output.WriteEnum((int) Status); + } + if (UID != 0L) { + output.WriteRawTag(40); + output.WriteInt64(UID); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (Token.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Token); + } + if (LastLoginDate.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(LastLoginDate); + } + if (RegDate.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(RegDate); + } + if (Status != global::AxibugProtobuf.LoginResultStatus.BaseDefault) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Status); + } + if (UID != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(UID); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Protobuf_Login_RESP other) { + if (other == null) { + return; + } + if (other.Token.Length != 0) { + Token = other.Token; + } + if (other.LastLoginDate.Length != 0) { + LastLoginDate = other.LastLoginDate; + } + if (other.RegDate.Length != 0) { + RegDate = other.RegDate; + } + if (other.Status != global::AxibugProtobuf.LoginResultStatus.BaseDefault) { + Status = other.Status; + } + if (other.UID != 0L) { + UID = other.UID; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + Token = input.ReadString(); + break; + } + case 18: { + LastLoginDate = input.ReadString(); + break; + } + case 26: { + RegDate = input.ReadString(); + break; + } + case 32: { + Status = (global::AxibugProtobuf.LoginResultStatus) input.ReadEnum(); + break; + } + case 40: { + UID = input.ReadInt64(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + Token = input.ReadString(); + break; + } + case 18: { + LastLoginDate = input.ReadString(); + break; + } + case 26: { + RegDate = input.ReadString(); + break; + } + case 32: { + Status = (global::AxibugProtobuf.LoginResultStatus) input.ReadEnum(); + break; + } + case 40: { + UID = input.ReadInt64(); + break; + } + } + } + } + #endif + + } + + public sealed partial class Protobuf_Screnn_Frame : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Protobuf_Screnn_Frame()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::AxibugProtobuf.ProtobufAxibugEmuOnlineReflection.Descriptor.MessageTypes[4]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Screnn_Frame() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Screnn_Frame(Protobuf_Screnn_Frame other) : this() { + frameID_ = other.frameID_; + rawBitmap_ = other.rawBitmap_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Protobuf_Screnn_Frame Clone() { + return new Protobuf_Screnn_Frame(this); + } + + /// Field number for the "FrameID" field. + public const int FrameIDFieldNumber = 1; + private int frameID_; + /// + ///帧编号 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int FrameID { + get { return frameID_; } + set { + frameID_ = value; + } + } + + /// Field number for the "RawBitmap" field. + public const int RawBitmapFieldNumber = 2; + private pb::ByteString rawBitmap_ = pb::ByteString.Empty; + /// + ///渲染层画面 + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public pb::ByteString RawBitmap { + get { return rawBitmap_; } + set { + rawBitmap_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Protobuf_Screnn_Frame); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Protobuf_Screnn_Frame other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (FrameID != other.FrameID) return false; + if (RawBitmap != other.RawBitmap) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (FrameID != 0) hash ^= FrameID.GetHashCode(); + if (RawBitmap.Length != 0) hash ^= RawBitmap.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (FrameID != 0) { + output.WriteRawTag(8); + output.WriteInt32(FrameID); + } + if (RawBitmap.Length != 0) { + output.WriteRawTag(18); + output.WriteBytes(RawBitmap); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (FrameID != 0) { + output.WriteRawTag(8); + output.WriteInt32(FrameID); + } + if (RawBitmap.Length != 0) { + output.WriteRawTag(18); + output.WriteBytes(RawBitmap); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (FrameID != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(FrameID); + } + if (RawBitmap.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(RawBitmap); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Protobuf_Screnn_Frame other) { + if (other == null) { + return; + } + if (other.FrameID != 0) { + FrameID = other.FrameID; + } + if (other.RawBitmap.Length != 0) { + RawBitmap = other.RawBitmap; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + FrameID = input.ReadInt32(); + break; + } + case 18: { + RawBitmap = input.ReadBytes(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + FrameID = input.ReadInt32(); + break; + } + case 18: { + RawBitmap = input.ReadBytes(); + break; + } + } + } + } + #endif + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/AxibugEmuOnline.Client/Assets/Script/Protobuf/ProtobufAxibugEmuOnline.cs.meta b/AxibugEmuOnline.Client/Assets/Script/Protobuf/ProtobufAxibugEmuOnline.cs.meta new file mode 100644 index 0000000..fd85ae8 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/Protobuf/ProtobufAxibugEmuOnline.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49b94fc04c9e78343a56ad63eda4db56 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/UNESTest.cs b/AxibugEmuOnline.Client/Assets/Script/UNESTest.cs new file mode 100644 index 0000000..1776792 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/UNESTest.cs @@ -0,0 +1,45 @@ +using UnityEngine; +using AxibugEmuOnline.Client.UNES; +using AxibugEmuOnline.Client.ClientCore; + +namespace AxibugEmuOnline.Client.Sample +{ + public class UNESTest : MonoBehaviour + { + public string RomFile; + + public bool PlayerP1; + + public static UNESTest instance; + public UNESBehaviour UNES { get; set; } + + public void Awake() + { + Screen.SetResolution(640, 480,false); + + instance = this; + AppAxibugEmuOnline.Init(); + AppAxibugEmuOnline.Connect("127.0.0.1", 10492); + } + + public void Start() + { + UNES = GetComponent(); + if (PlayerP1) + { + var data = Resources.Load(RomFile).bytes; + UNES.Boot(data); + } + else + { + UNES.Boot_Obs(); + } + } + + public void OnDisable() + { + AppAxibugEmuOnline.Close(); + } + + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/UNESTest.cs.meta b/AxibugEmuOnline.Client/Assets/Script/UNESTest.cs.meta new file mode 100644 index 0000000..5c34fdc --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/UNESTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e94e6b3864b9034b89b70aa411a2894 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/AxibugEmuOnline.Client.sln b/AxibugEmuOnline.Client/AxibugEmuOnline.Client.sln new file mode 100644 index 0000000..84d1c1e --- /dev/null +++ b/AxibugEmuOnline.Client/AxibugEmuOnline.Client.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AxibugEmuOnline.Client.UNES", "AxibugEmuOnline.Client.UNES.csproj", "{02571108-3006-F6EF-03C9-9DCFC8015A55}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {02571108-3006-F6EF-03C9-9DCFC8015A55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02571108-3006-F6EF-03C9-9DCFC8015A55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02571108-3006-F6EF-03C9-9DCFC8015A55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02571108-3006-F6EF-03C9-9DCFC8015A55}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index 8c754d4..e8f1112 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,60 @@ # AxibugEmuOnline -用于游戏模拟器同步的联机.Net实现 \ No newline at end of file +用于游戏模拟器同步的联机.Net实现 + + +AxibugEmuOnline.Server 是服务端 .Net 8 + +AxibugEmuOnline.Client 是客户端 Unity + + + + +##就是一种联机方式的探索 + +验证了一下 把模拟器帧缓存 走公网同步,实现联机的另一种方式 + +云游戏,但是不是视频流的方式,是同步模拟器帧缓存,+GZIP压缩。NES这种低分辨率+颜色查找表的方式。画面传输只需要9k/s + + +##TODO: + +1.目前只同步了画面,操作CMD同步还没做。 + +2.以及多用户自行创建房间,和玩家选择要加入的房间列表还没做。 + + + +##简述客户端逻辑: + +Player1主机才跑模拟器实例,然后Player1 会把渲染层的数据上报服务器。服务器广播。 + +Player2即二号手柄玩家,不运行模拟器实例,画面渲染来自网络同步的数据。 + +*之前试过直接上报渲染层,但是这样会有6w左右大小的uint[] + +*初步优化之后,采用只上报每一个像素对应颜色查找表的下标,这样就是一个byte[]了 + +*PorotoBuf 传输使用的是bytes,但是Porotbuff只会对数组里每一个byte进行位压缩,整个byte[]不压缩。于是C#先GZIP压缩之后,在扔给protobuf。对面再解压。超级马里奥最复杂的画面情况是9k每秒的样子/。 + + + +##引用 + + +###本项目使用,我自构建的HaoYueNet高性能网络库作为基础而开发 + +[HaoYueNet-Github](https://github.com/Sin365/HaoYueNet "HaoYueNet-Github") + +[HaoYueNet-自建Git站点](http://git.axibug.com/sin365/HaoYueNet "HaoYueNet-自建Git站点") + +###模拟器内核 + +模拟器内核采用 Emulator.NES https://github.com/Xyene/Emulator.NES + +这是一个单机的 NES模拟器C#实现,我在此基础上做修改 + +做帧缓,颜色查找缓存,存同步,加上网络库,实现服务端。达到如上效果。 + + +