mirror of
https://github.com/kwsch/pkNX.git
synced 2026-07-18 16:31:36 -05:00
Initial Commit
This commit is contained in:
parent
c2cf579d4f
commit
52d70f0b9f
674
LICENSE
Normal file
674
LICENSE
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
73
pkNX.Containers/Container.cs
Normal file
73
pkNX.Containers/Container.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public static class Container
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a new <see cref="IFileContainer"/> for the provided <see cref="path"/> and type.
|
||||
/// </summary>
|
||||
/// <param name="path">File location</param>
|
||||
/// <param name="t">File type</param>
|
||||
public static IFileContainer GetContainer(string path, ContainerType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case ContainerType.GARC: return new GARC(path);
|
||||
case ContainerType.Mini: return MiniUtil.GetMini(path);
|
||||
case ContainerType.SARC: return new SARC(path);
|
||||
case ContainerType.Folder: return new FolderContainer(path);
|
||||
case ContainerType.SingleFile: return new SingleFileContainer(path);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(t), t, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IFileContainer"/> for the stream.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the binary data</param>
|
||||
public static IFileContainer GetContainer(string path)
|
||||
{
|
||||
var fs = new FileStream(path, FileMode.Open);
|
||||
var container = GetContainer(fs);
|
||||
if (!(container is LargeContainer)) // not kept
|
||||
fs.Dispose();
|
||||
if (container == null)
|
||||
return null;
|
||||
|
||||
container.FilePath = path;
|
||||
return container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IFileContainer"/> for the stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream for the binary data</param>
|
||||
public static IFileContainer GetContainer(Stream stream)
|
||||
{
|
||||
BinaryReader br = new BinaryReader(stream);
|
||||
var container = GetContainer(br);
|
||||
if (!(container is LargeContainer)) // not kept
|
||||
br.Dispose();
|
||||
return container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="IFileContainer"/> for the stream within the <see cref="BinaryReader"/>.
|
||||
/// </summary>
|
||||
/// <param name="br">Reader for the binary data</param>
|
||||
public static IFileContainer GetContainer(BinaryReader br)
|
||||
{
|
||||
IFileContainer container;
|
||||
if ((container = GARC.GetGARC(br)) != null)
|
||||
return container;
|
||||
if ((container = MiniUtil.GetMini(br)) != null)
|
||||
return container;
|
||||
if ((container = SARC.GetSARC(br)) != null)
|
||||
return container;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
pkNX.Containers/ContainerHandler/ContainerHandler.cs
Normal file
29
pkNX.Containers/ContainerHandler/ContainerHandler.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
// ReSharper disable EventNeverSubscribedTo.Global
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public sealed class ContainerHandler
|
||||
{
|
||||
public event EventHandler<FileCountDeterminedEventArgs> FileCountDetermined;
|
||||
public event EventHandler<FileProgressedEventArgs> FileProgressed;
|
||||
|
||||
private int count;
|
||||
|
||||
public void Initialize(int total)
|
||||
{
|
||||
count = total;
|
||||
var args = new FileCountDeterminedEventArgs {Total = total};
|
||||
FileCountDetermined?.Invoke(null, args);
|
||||
}
|
||||
|
||||
public void StepFile(int ctr, int total = -1, string fileName = null)
|
||||
{
|
||||
if (total < 0)
|
||||
total = count;
|
||||
var args = new FileProgressedEventArgs {Current = ctr, Total = total, CurrentFile = fileName};
|
||||
FileProgressed?.Invoke(null, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class FileCountDeterminedEventArgs : EventArgs
|
||||
{
|
||||
public int Total { get; set; }
|
||||
}
|
||||
}
|
||||
11
pkNX.Containers/ContainerHandler/FileProgressedEventArgs.cs
Normal file
11
pkNX.Containers/ContainerHandler/FileProgressedEventArgs.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class FileProgressedEventArgs : EventArgs
|
||||
{
|
||||
public int Current { get; set; }
|
||||
public int Total { get; set; }
|
||||
public string CurrentFile { get; set; }
|
||||
}
|
||||
}
|
||||
15
pkNX.Containers/ContainerParent.cs
Normal file
15
pkNX.Containers/ContainerParent.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
namespace pkNX.Containers
|
||||
{
|
||||
public enum ContainerParent
|
||||
{
|
||||
/// <summary>
|
||||
/// File is located in the RomFS.
|
||||
/// </summary>
|
||||
RomFS,
|
||||
|
||||
/// <summary>
|
||||
/// File is located in the ExeFS.
|
||||
/// </summary>
|
||||
ExeFS,
|
||||
}
|
||||
}
|
||||
11
pkNX.Containers/ContainerType.cs
Normal file
11
pkNX.Containers/ContainerType.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace pkNX.Containers
|
||||
{
|
||||
public enum ContainerType
|
||||
{
|
||||
GARC,
|
||||
Mini,
|
||||
SARC,
|
||||
Folder,
|
||||
SingleFile,
|
||||
}
|
||||
}
|
||||
90
pkNX.Containers/FolderContainer.cs
Normal file
90
pkNX.Containers/FolderContainer.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class FolderContainer : IFileContainer
|
||||
{
|
||||
private readonly List<string> Paths = new List<string>();
|
||||
private readonly List<byte[]> Data = new List<byte[]>();
|
||||
private readonly List<bool> TrackModify = new List<bool>();
|
||||
|
||||
public FolderContainer() { }
|
||||
public FolderContainer(IEnumerable<string> files) => AddFiles(files);
|
||||
|
||||
public FolderContainer(string path)
|
||||
{
|
||||
FilePath = path;
|
||||
AddFiles(Directory.GetFiles(path, "*", SearchOption.AllDirectories));
|
||||
}
|
||||
|
||||
public void AddFile(string file, byte[] data = null)
|
||||
{
|
||||
Paths.Add(file);
|
||||
Data.Add(data);
|
||||
TrackModify.Add(false);
|
||||
}
|
||||
|
||||
public void AddFiles(IEnumerable<string> files)
|
||||
{
|
||||
foreach (var f in files)
|
||||
AddFile(f);
|
||||
}
|
||||
|
||||
public byte[] GetFileData(string file)
|
||||
{
|
||||
var index = Paths.IndexOf(file);
|
||||
if (index < 0)
|
||||
return null;
|
||||
return Data[index] ?? (Data[index] = File.ReadAllBytes(file));
|
||||
}
|
||||
|
||||
public byte[] GetFileData(int index)
|
||||
{
|
||||
if (index < 0 || Data.Count >= index)
|
||||
return null;
|
||||
return Data[index] ?? (Data[index] = File.ReadAllBytes(Paths[index]));
|
||||
}
|
||||
|
||||
public byte[] this[int index]
|
||||
{
|
||||
get => GetFileData(index);
|
||||
set
|
||||
{
|
||||
if (value != null && Data[index] != null)
|
||||
TrackModify[index] = value.SequenceEqual(Data[index]);
|
||||
Data[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string FilePath { get; set; }
|
||||
public bool Modified => TrackModify.Count != 0;
|
||||
public int Count => Paths.Count;
|
||||
|
||||
public Task<byte[][]> GetFiles() => new Task<byte[][]>(() => Paths.Select(File.ReadAllBytes).ToArray());
|
||||
public Task<byte[]> GetFile(int file, int subFile = 0) => new Task<byte[]>(() => this[file]);
|
||||
public Task SetFile(int file, byte[] value, int subFile = 0) => new Task(() => this[file] = value);
|
||||
public Task SaveAs(string path, ContainerHandler handler, CancellationToken token) => new Task(SaveAll);
|
||||
|
||||
private void SaveAll()
|
||||
{
|
||||
for (int i = 0; i < Paths.Count; i++)
|
||||
{
|
||||
if (!TrackModify[i])
|
||||
continue;
|
||||
var data = Data[i];
|
||||
if (data == null)
|
||||
continue;
|
||||
File.WriteAllBytes(Paths[i], data);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dump(string path, ContainerHandler handler)
|
||||
{
|
||||
SaveAll(); // there's really nothing to dump, just save any modified
|
||||
}
|
||||
}
|
||||
}
|
||||
54
pkNX.Containers/GARC/FATB.cs
Normal file
54
pkNX.Containers/GARC/FATB.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
internal class FATB
|
||||
{
|
||||
private const uint MAGIC = 0x46415442;
|
||||
|
||||
private readonly uint Magic = MAGIC;
|
||||
private readonly int HeaderSize = 0xC;
|
||||
private readonly ushort EntryCount;
|
||||
private readonly short Padding = -1;
|
||||
|
||||
private FATBEntry[] Entries { get; }
|
||||
public FATBEntry this[int index] => Entries[index];
|
||||
|
||||
public FATB(BinaryReader br, int DataOffset)
|
||||
{
|
||||
Magic = br.ReadUInt32();
|
||||
HeaderSize = br.ReadInt32();
|
||||
EntryCount = br.ReadUInt16();
|
||||
Padding = br.ReadInt16();
|
||||
|
||||
Entries = new FATBEntry[EntryCount];
|
||||
for (int i = 0; i < EntryCount; i++)
|
||||
Entries[i] = new FATBEntry(br, DataOffset);
|
||||
}
|
||||
|
||||
public FATB(IReadOnlyList<string> files)
|
||||
{
|
||||
EntryCount = (ushort)files.Count;
|
||||
Entries = new FATBEntry[EntryCount];
|
||||
for (int i = 0; i < EntryCount; i++)
|
||||
{
|
||||
if (!Directory.Exists(files[i]))
|
||||
Entries[i] = new FATBEntry(files[i]);
|
||||
else
|
||||
Entries[i] = new FATBEntry(Directory.GetFiles(files[i]));
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Magic);
|
||||
bw.Write(HeaderSize);
|
||||
bw.Write(EntryCount);
|
||||
bw.Write(Padding);
|
||||
foreach (var entry in Entries)
|
||||
entry.Write(bw);
|
||||
// sub entry writing handled separately
|
||||
}
|
||||
}
|
||||
}
|
||||
128
pkNX.Containers/GARC/FATBEntry.cs
Normal file
128
pkNX.Containers/GARC/FATBEntry.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
internal class FATBEntry
|
||||
{
|
||||
private readonly uint Vector;
|
||||
public readonly bool IsFolder;
|
||||
public readonly FATBSubEntry[] SubEntries;
|
||||
|
||||
// ReSharper disable once UnusedMember.Local -- needed to init subentries
|
||||
private FATBEntry()
|
||||
{
|
||||
SubEntries = new FATBSubEntry[32];
|
||||
for (int i = 0; i < SubEntries.Length; i++)
|
||||
SubEntries[i] = new FATBSubEntry();
|
||||
}
|
||||
|
||||
public FATBEntry(string file)
|
||||
{
|
||||
IsFolder = false;
|
||||
Vector = 1;
|
||||
SubEntries[0].Exists = true;
|
||||
SubEntries[0].File = file;
|
||||
}
|
||||
|
||||
public FATBEntry(IEnumerable<string> files)
|
||||
{
|
||||
IsFolder = true;
|
||||
Vector = 0;
|
||||
foreach (var f in files)
|
||||
{
|
||||
var fn = Path.GetFileNameWithoutExtension(f);
|
||||
if (!int.TryParse(fn, out var val) || val >= SubEntries.Length)
|
||||
continue;
|
||||
if (FATBSubEntry.GetFileNumber(val) != fn)
|
||||
continue;
|
||||
|
||||
Vector |= (uint)(1 << val);
|
||||
|
||||
SubEntries[val].Exists = true;
|
||||
SubEntries[val].File = f;
|
||||
}
|
||||
}
|
||||
|
||||
public FATBEntry(BinaryReader br, int DataOffset)
|
||||
{
|
||||
Vector = br.ReadUInt32();
|
||||
|
||||
int ctr = 0;
|
||||
for (int b = 0; b < 32; b++)
|
||||
{
|
||||
SubEntries[b].Exists = (Vector & 1 << b) != 0;
|
||||
if (!SubEntries[b].Exists)
|
||||
continue;
|
||||
SubEntries[b].Start = br.ReadInt32();
|
||||
SubEntries[b].End = br.ReadInt32();
|
||||
SubEntries[b].Length = br.ReadInt32();
|
||||
SubEntries[b].ParentDataPosition = DataOffset;
|
||||
ctr++;
|
||||
}
|
||||
IsFolder = ctr > 1;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Vector);
|
||||
foreach (var s in SubEntries.Where(s => s.Exists))
|
||||
{
|
||||
bw.Write(s.Start);
|
||||
bw.Write(s.End);
|
||||
bw.Write(s.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteEntries(BinaryWriter bw, Stream originalStream, int padToNearest, int DataOffset, ref int max, ref int maxPad)
|
||||
{
|
||||
var dest = bw.BaseStream;
|
||||
|
||||
bw.Write(Vector);
|
||||
foreach (var entry in SubEntries.Where(z => z.Exists))
|
||||
{
|
||||
entry.Write(originalStream, dest, DataOffset);
|
||||
|
||||
int padding = GetPadding(entry.Length, padToNearest);
|
||||
if (max < entry.Length)
|
||||
{
|
||||
max = entry.Length;
|
||||
maxPad = entry.Length + padding;
|
||||
}
|
||||
|
||||
while (padding-- > 0)
|
||||
bw.Write((byte)0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetPadding(int length, int padTo)
|
||||
{
|
||||
var remain = length % padTo;
|
||||
return remain == 0 ? 0 : padTo - remain;
|
||||
}
|
||||
|
||||
public void Dump(string path, int index, string format, Stream parent, int DataOffset)
|
||||
{
|
||||
var fn = index.ToString(format);
|
||||
if (!IsFolder)
|
||||
{
|
||||
var loc = Path.Combine(path, fn + ".bin");
|
||||
SubEntries[0].Dump(parent, loc, DataOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
path = Path.Combine(path, fn);
|
||||
Directory.CreateDirectory(path);
|
||||
for (var i = 0; i < SubEntries.Length; i++)
|
||||
{
|
||||
var entry = SubEntries[i];
|
||||
if (!entry.Exists)
|
||||
continue;
|
||||
fn = i.ToString("D2");
|
||||
var loc = Path.Combine(path, fn + ".bin");
|
||||
entry.Dump(parent, loc, DataOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
pkNX.Containers/GARC/FATBSubEntry.cs
Normal file
8
pkNX.Containers/GARC/FATBSubEntry.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace pkNX.Containers
|
||||
{
|
||||
public class FATBSubEntry : LargeContainerEntry
|
||||
{
|
||||
public bool Exists;
|
||||
public static string GetFileNumber(int index) => $"{index:00}";
|
||||
}
|
||||
}
|
||||
47
pkNX.Containers/GARC/FATO.cs
Normal file
47
pkNX.Containers/GARC/FATO.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
internal class FATO
|
||||
{
|
||||
private const uint MAGIC = 0x4641544F;
|
||||
|
||||
private readonly uint Magic = MAGIC;
|
||||
private readonly int HeaderSize = 0xC;
|
||||
private readonly ushort EntryCount;
|
||||
private readonly short Padding = -1;
|
||||
|
||||
private FATOEntry[] Entries { get; }
|
||||
public FATOEntry this[int index] => Entries[index];
|
||||
|
||||
public FATO(BinaryReader br)
|
||||
{
|
||||
Magic = br.ReadUInt32();
|
||||
HeaderSize = br.ReadInt32();
|
||||
EntryCount = br.ReadUInt16();
|
||||
Padding = br.ReadInt16();
|
||||
|
||||
Entries = new FATOEntry[EntryCount];
|
||||
for (int i = 0; i < EntryCount; i++)
|
||||
Entries[i] = new FATOEntry(br);
|
||||
}
|
||||
|
||||
public FATO(int count)
|
||||
{
|
||||
EntryCount = (ushort)count;
|
||||
Entries = new FATOEntry[EntryCount];
|
||||
for (int i = 0; i < EntryCount; i++)
|
||||
Entries[i] = new FATOEntry();
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Magic);
|
||||
bw.Write(HeaderSize);
|
||||
bw.Write(EntryCount);
|
||||
bw.Write(Padding);
|
||||
foreach (var entry in Entries)
|
||||
entry.Write(bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
pkNX.Containers/GARC/FATOEntry.cs
Normal file
14
pkNX.Containers/GARC/FATOEntry.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
internal class FATOEntry
|
||||
{
|
||||
public uint Offset { get; set; }
|
||||
|
||||
internal FATOEntry(uint offset = 0) => Offset = offset;
|
||||
internal FATOEntry(BinaryReader br) => Offset = br.ReadUInt32();
|
||||
|
||||
internal void Write(BinaryWriter bw) => bw.Write(Offset);
|
||||
}
|
||||
}
|
||||
129
pkNX.Containers/GARC/GARC.cs
Normal file
129
pkNX.Containers/GARC/GARC.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class GARC : LargeContainer
|
||||
{
|
||||
private GARCHeader Header;
|
||||
private FATO FATO;
|
||||
private FATB FATB;
|
||||
|
||||
public GARC(BinaryReader br) => OpenRead(br);
|
||||
public GARC(string file) => OpenBinary(file);
|
||||
|
||||
public GARC(IReadOnlyList<string> files, GARCVersion version = GARCVersion.VER_6)
|
||||
{
|
||||
Header = new GARCHeader(version);
|
||||
FATO = new FATO(files.Count);
|
||||
FATB = new FATB(files);
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
Reader.BaseStream.Position = 0;
|
||||
Header = new GARCHeader(Reader);
|
||||
FATO = new FATO(Reader);
|
||||
FATB = new FATB(Reader, Header.DataOffset);
|
||||
}
|
||||
|
||||
protected override int GetFileOffset(int file, int subFile = 0)
|
||||
{
|
||||
var f = FATB[file].SubEntries[subFile];
|
||||
return f.Start;
|
||||
}
|
||||
|
||||
public override byte[] GetEntry(int index, int subFile)
|
||||
{
|
||||
var f = FATB[index].SubEntries[subFile];
|
||||
if (f.File is byte[] data)
|
||||
return data;
|
||||
|
||||
data = f.GetFileData(Reader.BaseStream);
|
||||
f.File = data; // cache for future fetches
|
||||
return data;
|
||||
}
|
||||
|
||||
public override void SetEntry(int index, byte[] value, int subFile)
|
||||
{
|
||||
var f = FATB[index].SubEntries[subFile];
|
||||
f.File = value;
|
||||
}
|
||||
|
||||
protected override Task Pack(BinaryWriter bw, ContainerHandler handler, CancellationToken token)
|
||||
{
|
||||
return Task.Run(() => PackGARC(bw, handler, token), token);
|
||||
}
|
||||
|
||||
private void PackGARC(BinaryWriter bw, ContainerHandler handler, CancellationToken token)
|
||||
{
|
||||
StartPack();
|
||||
handler.Initialize(Count);
|
||||
|
||||
WriteIntro(bw);
|
||||
int dataOffset = (int)bw.BaseStream.Position;
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
WriteEntry(bw, i, dataOffset);
|
||||
}
|
||||
WriteIntro(bw, true);
|
||||
bw.Flush();
|
||||
}
|
||||
|
||||
private void StartPack()
|
||||
{
|
||||
Header.ContentLargestUnpadded = 0;
|
||||
Header.ContentLargestPadded = 0;
|
||||
}
|
||||
|
||||
private void WriteIntro(BinaryWriter bw, bool lastPass = false)
|
||||
{
|
||||
Header.Write(bw);
|
||||
FATO.Write(bw);
|
||||
FATB.Write(bw);
|
||||
if (lastPass)
|
||||
Header.DataOffset = (int)bw.BaseStream.Position;
|
||||
}
|
||||
|
||||
private void WriteEntry(BinaryWriter bw, int i, int DataOffset)
|
||||
{
|
||||
FATO[i].Offset = (uint)bw.BaseStream.Position;
|
||||
FATB[i].WriteEntries(bw, Reader?.BaseStream, Header.ContentPadToNearest, DataOffset,
|
||||
ref Header.ContentLargestUnpadded, ref Header.ContentLargestPadded);
|
||||
}
|
||||
|
||||
public override void Dump(string path, ContainerHandler handler)
|
||||
{
|
||||
string format = this.GetFileFormatString();
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
handler.Initialize(Count);
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
FATB[i].Dump(path, i, format, Reader.BaseStream, Header.DataOffset);
|
||||
handler.StepFile(i+1);
|
||||
}
|
||||
}
|
||||
|
||||
public static GARC GetGARC(BinaryReader br)
|
||||
{
|
||||
if (br.BaseStream.Length < 20)
|
||||
return null;
|
||||
br.BaseStream.Position = 0;
|
||||
if (br.ReadUInt32() != GARCHeader.MAGIC)
|
||||
return null;
|
||||
br.BaseStream.Position = 10;
|
||||
var ver = br.ReadUInt16();
|
||||
|
||||
if (ver == 0x0600 || ver == 0x0400)
|
||||
return new GARC(br);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
98
pkNX.Containers/GARC/GARCHeader.cs
Normal file
98
pkNX.Containers/GARC/GARCHeader.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class GARCHeader
|
||||
{
|
||||
internal const uint MAGIC = 0x4E415243; // CRAG
|
||||
private const ushort VER_4 = (ushort)GARCVersion.VER_4;
|
||||
private const ushort VER_6 = (ushort)GARCVersion.VER_6;
|
||||
|
||||
private readonly uint Magic = MAGIC;
|
||||
private readonly int HeaderSize;
|
||||
private readonly ushort Endianess = 0xFEFF;
|
||||
private readonly ushort Version;
|
||||
private readonly uint ChunkCount = 4;
|
||||
|
||||
public int DataOffset { get; set; }
|
||||
public uint FileSize { get; }
|
||||
|
||||
public readonly int ContentPadToNearest = 4; // Format 6 Only (4 bytes is standard in VER_4, and is not stored)
|
||||
public int ContentLargestUnpadded;
|
||||
public int ContentLargestPadded; // Format 6 Only
|
||||
|
||||
private bool VER6 => Version == VER_6;
|
||||
private bool VER4 => Version == VER_4;
|
||||
|
||||
public GARCHeader(GARCVersion version)
|
||||
{
|
||||
if (version == GARCVersion.VER_6)
|
||||
Version = VER_6;
|
||||
else if (version == GARCVersion.VER_4)
|
||||
Version = VER_4;
|
||||
else
|
||||
Version = (ushort)version;
|
||||
HeaderSize = VER6 ? 0x24 : 0x1C;
|
||||
}
|
||||
|
||||
public GARCHeader(BinaryReader br)
|
||||
{
|
||||
Magic = br.ReadUInt32();
|
||||
HeaderSize = br.ReadInt32();
|
||||
Endianess = br.ReadUInt16();
|
||||
Version = br.ReadUInt16();
|
||||
ChunkCount = br.ReadUInt32();
|
||||
|
||||
if (ChunkCount != 4)
|
||||
throw new FormatException($"Invalid GARC Chunk Count: {ChunkCount}");
|
||||
|
||||
DataOffset = br.ReadInt32();
|
||||
FileSize = br.ReadUInt32();
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case VER_4:
|
||||
ContentLargestUnpadded = br.ReadInt32();
|
||||
ContentPadToNearest = 4;
|
||||
break;
|
||||
case VER_6:
|
||||
ContentLargestPadded = br.ReadInt32();
|
||||
ContentLargestUnpadded = br.ReadInt32();
|
||||
ContentPadToNearest = br.ReadInt32();
|
||||
break;
|
||||
default:
|
||||
throw new FormatException($"Invalid GARC Version: 0x{Version:X4}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Magic);
|
||||
bw.Write(HeaderSize);
|
||||
bw.Write(Endianess);
|
||||
bw.Write(Version);
|
||||
bw.Write(ChunkCount);
|
||||
|
||||
if (ChunkCount != 4)
|
||||
throw new FormatException($"Invalid GARC Chunk Count: {ChunkCount}");
|
||||
|
||||
bw.Write(DataOffset);
|
||||
bw.Write(FileSize);
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case VER_4:
|
||||
bw.Write(ContentLargestUnpadded);
|
||||
break;
|
||||
case VER_6:
|
||||
bw.Write(ContentLargestPadded);
|
||||
bw.Write(ContentLargestUnpadded);
|
||||
bw.Write(ContentPadToNearest);
|
||||
break;
|
||||
default:
|
||||
throw new FormatException($"Invalid GARC Version: 0x{Version:X4}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
pkNX.Containers/GARC/GARCVersion.cs
Normal file
8
pkNX.Containers/GARC/GARCVersion.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace pkNX.Containers
|
||||
{
|
||||
public enum GARCVersion : ushort
|
||||
{
|
||||
VER_4 = 0x0400,
|
||||
VER_6 = 0x0600,
|
||||
}
|
||||
}
|
||||
46
pkNX.Containers/IFileContainer.cs
Normal file
46
pkNX.Containers/IFileContainer.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of Files
|
||||
/// </summary>
|
||||
public interface IFileContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Path the <see cref="IFileContainer"/> was loaded from.
|
||||
/// </summary>
|
||||
string FilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indication if the contents of the <see cref="IFileContainer"/> have been modified.
|
||||
/// </summary>
|
||||
bool Modified { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Count of files inside the <see cref="IFileContainer"/>.
|
||||
/// </summary>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// File access for individually indexed files.
|
||||
/// </summary>
|
||||
/// <param name="index">File number to fetch</param>
|
||||
/// <returns>Data representing the file at the specified index.</returns>
|
||||
byte[] this[int index] { get; set; }
|
||||
|
||||
Task<byte[][]> GetFiles();
|
||||
Task<byte[]> GetFile(int file, int subFile = 0);
|
||||
Task SetFile(int file, byte[] value, int subFile = 0);
|
||||
Task SaveAs(string path, ContainerHandler handler, CancellationToken token);
|
||||
|
||||
void Dump(string path, ContainerHandler handler);
|
||||
}
|
||||
|
||||
public static partial class Extensions
|
||||
{
|
||||
public static string GetFileFormatString(this IFileContainer c) => "D" + Math.Ceiling(Math.Log10(c.Count));
|
||||
}
|
||||
}
|
||||
147
pkNX.Containers/LargeContainer.cs
Normal file
147
pkNX.Containers/LargeContainer.cs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using pkNX.Containers;
|
||||
|
||||
namespace pkNX
|
||||
{
|
||||
/// <summary>
|
||||
/// More complex container format for large long lived archives.
|
||||
/// </summary>
|
||||
public abstract class LargeContainer : IDisposable, IFileContainer
|
||||
{
|
||||
public virtual int Count => Files.Length;
|
||||
|
||||
public Task<byte[][]> GetFiles() => new Task<byte[][]>(() => { CacheAll(); return Files; });
|
||||
public Task<byte[]> GetFile(int file, int subFile = 0) => new Task<byte[]>(() => GetEntry(file, subFile));
|
||||
public Task SetFile(int file, byte[] value, int subFile = 0) => new Task(() => SetEntry(file, value, subFile));
|
||||
|
||||
public string Extension => FilePath == null ? null : Path.GetExtension(FilePath);
|
||||
public string FileName => FilePath == null ? null : Path.GetFileName(FilePath);
|
||||
public string FilePath { get; set; }
|
||||
public bool Modified { get; private set; }
|
||||
|
||||
protected byte[][] Files { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Packs the <see cref="LargeContainer"/> to the specified writing stream.
|
||||
/// </summary>
|
||||
/// <param name="bw">Stream Writer to write contents to.</param>
|
||||
/// <param name="handler">Manager for monitoring progress.</param>
|
||||
/// <param name="token">Cancellation object</param>
|
||||
/// <returns>Awaitable Task</returns>
|
||||
protected abstract Task Pack(BinaryWriter bw, ContainerHandler handler, CancellationToken token);
|
||||
|
||||
#region File Reading
|
||||
|
||||
protected BinaryReader Reader { get; private set; }
|
||||
private Stream Stream;
|
||||
|
||||
protected void OpenBinary(string path)
|
||||
{
|
||||
Stream = new FileStream(path, FileMode.Open);
|
||||
Reader = new BinaryReader(Stream);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
protected void OpenRead(BinaryReader br)
|
||||
{
|
||||
Reader = br;
|
||||
Stream = br.BaseStream;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
protected abstract void Initialize();
|
||||
|
||||
protected abstract int GetFileOffset(int file, int subFile = 0);
|
||||
|
||||
public BinaryReader Seek(int file, long offset = 0, int subFile = 0)
|
||||
{
|
||||
offset += GetFileOffset(file, subFile);
|
||||
Reader.BaseStream.Position = offset;
|
||||
return Reader;
|
||||
}
|
||||
|
||||
public abstract byte[] GetEntry(int index, int subFile);
|
||||
|
||||
public virtual void SetEntry(int index, byte[] value, int subFile)
|
||||
{
|
||||
Files[index] = value;
|
||||
Modified |= value != null;
|
||||
}
|
||||
|
||||
private byte[] GetCachedValue(int i, int subFile)
|
||||
{
|
||||
return Files[i] ?? (Files[i] = GetEntry(i, subFile));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public byte[] this[int index]
|
||||
{
|
||||
get => GetCachedValue(index, 0);
|
||||
set => SetEntry(index, value, 0);
|
||||
}
|
||||
|
||||
public void CacheAll()
|
||||
{
|
||||
for (int i = 0; i < Files.Length; i++)
|
||||
{
|
||||
if (Files[i] == null)
|
||||
{
|
||||
Files[i] = GetCachedValue(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reader = null;
|
||||
Stream.Close();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (Reader == null)
|
||||
throw new ArgumentNullException(nameof(Reader));
|
||||
|
||||
for (int i = 0; i < Files.Length; i++)
|
||||
Files[i] = null;
|
||||
|
||||
Modified = false;
|
||||
}
|
||||
|
||||
public abstract void Dump(string path, ContainerHandler handler);
|
||||
|
||||
public async Task SaveAs(string path, ContainerHandler handler, CancellationToken token = new CancellationToken())
|
||||
{
|
||||
bool sameLocation = path == FilePath && Reader != null;
|
||||
var writePath = sameLocation ? Path.GetTempFileName() : path;
|
||||
|
||||
var stream = new FileStream(path, FileMode.CreateNew);
|
||||
using (var bw = new BinaryWriter(stream))
|
||||
await Pack(bw, handler, token).ConfigureAwait(false);
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
stream.Close();
|
||||
File.Delete(path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sameLocation && path != writePath)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
File.Move(writePath, path);
|
||||
}
|
||||
|
||||
Stream = stream;
|
||||
Reader = new BinaryReader(Stream);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stream.Dispose();
|
||||
Reader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
74
pkNX.Containers/LargeContainerEntry.cs
Normal file
74
pkNX.Containers/LargeContainerEntry.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class LargeContainerEntry
|
||||
{
|
||||
public int Start { get; set; }
|
||||
public int End { get; set; }
|
||||
public virtual int Length { get; set; }
|
||||
public object File { get; set; }
|
||||
public int ParentDataPosition { get; set; }
|
||||
|
||||
public byte[] GetFileData(Stream parent)
|
||||
{
|
||||
parent.Seek(Start + ParentDataPosition, SeekOrigin.Begin);
|
||||
byte[] data = new byte[Length];
|
||||
parent.Read(data, 0, Length);
|
||||
return data;
|
||||
}
|
||||
|
||||
public void Write(Stream parent, Stream dest, int DataOffset)
|
||||
{
|
||||
switch (File)
|
||||
{
|
||||
case string f:
|
||||
WriteFrom(dest, DataOffset, f);
|
||||
break;
|
||||
case byte[] data:
|
||||
WriteFrom(dest, DataOffset, data);
|
||||
break;
|
||||
default:
|
||||
WriteFrom(dest, DataOffset, parent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteFrom(Stream dest, int DataOffset, string f)
|
||||
{
|
||||
Start = (int)dest.Position - DataOffset;
|
||||
using (var s = new FileStream(f, FileMode.Open, FileAccess.Read, FileShare.None))
|
||||
s.CopyTo(dest);
|
||||
End = (int)dest.Position - DataOffset;
|
||||
Length = End - Start;
|
||||
}
|
||||
|
||||
private void WriteFrom(Stream dest, int DataOffset, byte[] data)
|
||||
{
|
||||
Start = (int)dest.Position - DataOffset;
|
||||
Length = data.Length;
|
||||
End = Start + Length;
|
||||
|
||||
File = data;
|
||||
dest.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
private void WriteFrom(Stream dest, int DataOffset, Stream source)
|
||||
{
|
||||
Start = (int)dest.Position - DataOffset;
|
||||
End = Start + Length;
|
||||
|
||||
source.Seek(Start + ParentDataPosition, SeekOrigin.Begin);
|
||||
source.CopyTo(dest, Length);
|
||||
}
|
||||
|
||||
public void Dump(Stream parent, string path, int DataOffset)
|
||||
{
|
||||
if (File is string)
|
||||
return;
|
||||
|
||||
using (var file = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
Write(parent, file, DataOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
pkNX.Containers/Mini/Mini.cs
Normal file
57
pkNX.Containers/Mini/Mini.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class Mini : IFileContainer
|
||||
{
|
||||
public string Identifier { get; }
|
||||
public byte[][] Files { get; }
|
||||
|
||||
public Mini(byte[][] data, string ident) { Identifier = ident; Files = data; }
|
||||
|
||||
public byte[] this[int index]
|
||||
{
|
||||
get => Files[index];
|
||||
set
|
||||
{
|
||||
Files[index] = value;
|
||||
Modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public bool Modified { get; set; }
|
||||
public int Count => Files.Length;
|
||||
|
||||
public Task<byte[]> GetFile(int file, int subFile = 0) => new Task<byte[]>(()=>Files[file]);
|
||||
public Task SetFile(int file, byte[] value, int subFile = 0) => new Task(() => Files[file] = value);
|
||||
public Task<byte[][]> GetFiles() => new Task<byte[][]>(() => Files);
|
||||
|
||||
public Task SaveAs(string path, ContainerHandler handler, CancellationToken token)
|
||||
{
|
||||
return new Task(() =>
|
||||
{
|
||||
byte[] data = MiniUtil.PackMini(Files, Identifier);
|
||||
File.WriteAllBytes(path, data);
|
||||
});
|
||||
}
|
||||
|
||||
public void Dump(string path, ContainerHandler handler)
|
||||
{
|
||||
string format = this.GetFileFormatString();
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
handler.Initialize(Count);
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
var fn = Path.Combine(path, i.ToString(format) + ".bin");
|
||||
File.WriteAllBytes(fn, Files[i]);
|
||||
handler.StepFile(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
pkNX.Containers/Mini/MiniUtil.cs
Normal file
158
pkNX.Containers/Mini/MiniUtil.cs
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// // Mini Packing Util
|
||||
/// </summary>
|
||||
public static class MiniUtil
|
||||
{
|
||||
public static byte[] PackMini(string folder, string identifier) => PackMini(Directory.GetFiles(folder), identifier);
|
||||
|
||||
public static byte[] PackMini(string[] files, string identifier)
|
||||
{
|
||||
byte[][] fileData = new byte[files.Length][];
|
||||
for (int i = 0; i < fileData.Length; i++)
|
||||
fileData[i] = File.ReadAllBytes(files[i]);
|
||||
return PackMini(fileData, identifier);
|
||||
}
|
||||
|
||||
public static byte[] PackMini(byte[][] fileData, string identifier)
|
||||
{
|
||||
// Create new Binary with the relevant header bytes
|
||||
byte[] data = new byte[4];
|
||||
data[0] = (byte)identifier[0];
|
||||
data[1] = (byte)identifier[1];
|
||||
Array.Copy(BitConverter.GetBytes((ushort)fileData.Length), 0, data, 2, 2);
|
||||
|
||||
int count = fileData.Length;
|
||||
int dataOffset = 4 + 4 + (count * 4);
|
||||
|
||||
// Start the data filling.
|
||||
using (MemoryStream dataout = new MemoryStream())
|
||||
using (MemoryStream offsetMap = new MemoryStream())
|
||||
using (BinaryWriter bd = new BinaryWriter(dataout))
|
||||
using (BinaryWriter bo = new BinaryWriter(offsetMap))
|
||||
{
|
||||
// For each file...
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Write File Offset
|
||||
uint fileOffset = (uint)(dataout.Position + dataOffset);
|
||||
bo.Write(fileOffset);
|
||||
|
||||
// Write File to Stream
|
||||
bd.Write(fileData[i]);
|
||||
|
||||
// Pad the Data MemoryStream with Zeroes until len%4=0;
|
||||
while (dataout.Length % 4 != 0)
|
||||
bd.Write((byte)0);
|
||||
// File Offset will be updated as the offset is based off of the Data length.
|
||||
}
|
||||
// Cap the File
|
||||
bo.Write((uint)(dataout.Position + dataOffset));
|
||||
|
||||
using (var newPack = new MemoryStream())
|
||||
using (var header = new MemoryStream(data))
|
||||
{
|
||||
header.WriteTo(newPack);
|
||||
offsetMap.WriteTo(newPack);
|
||||
dataout.WriteTo(newPack);
|
||||
return newPack.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[][] UnpackMini(string file, string identifier = null)
|
||||
{
|
||||
byte[] fileData = File.ReadAllBytes(file);
|
||||
return UnpackMini(fileData, identifier);
|
||||
}
|
||||
|
||||
public static byte[][] UnpackMini(byte[] fileData, string identifier = null)
|
||||
{
|
||||
if (fileData == null || fileData.Length < 4)
|
||||
return null;
|
||||
|
||||
if (identifier?.Length == 2)
|
||||
{
|
||||
if (identifier[0] != fileData[0] || identifier[1] != fileData[1])
|
||||
return null;
|
||||
}
|
||||
|
||||
int count = BitConverter.ToUInt16(fileData, 2); int ctr = 4;
|
||||
int start = BitConverter.ToInt32(fileData, ctr); ctr += 4;
|
||||
byte[][] returnData = new byte[count][];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int end = BitConverter.ToInt32(fileData, ctr); ctr += 4;
|
||||
int len = end - start;
|
||||
byte[] data = new byte[len];
|
||||
Buffer.BlockCopy(fileData, start, data, 0, len);
|
||||
returnData[i] = data;
|
||||
start = end;
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
|
||||
public static Mini GetMini(string path)
|
||||
{
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
|
||||
using (var br = new BinaryReader(fs))
|
||||
return GetMini(br);
|
||||
}
|
||||
|
||||
public static Mini GetMini(BinaryReader br)
|
||||
{
|
||||
var ident = GetIsMini(br);
|
||||
if (ident == null)
|
||||
return null;
|
||||
|
||||
br.BaseStream.Position = 0;
|
||||
var data = br.ReadBytes((int)br.BaseStream.Length);
|
||||
var unpack = UnpackMini(data, ident);
|
||||
return new Mini(unpack, ident);
|
||||
}
|
||||
|
||||
public static string GetIsMini(BinaryReader br)
|
||||
{
|
||||
if (br.BaseStream.Length < 12)
|
||||
return null;
|
||||
br.BaseStream.Position = 0;
|
||||
var ident = br.ReadBytes(2);
|
||||
var count = br.ReadUInt16();
|
||||
|
||||
int finalLengthOfs = 4 + (count * 4);
|
||||
if (br.BaseStream.Length < finalLengthOfs + 4)
|
||||
return null;
|
||||
br.BaseStream.Position = 4 + (count * 4);
|
||||
var len = br.ReadUInt32();
|
||||
if (len != br.BaseStream.Length)
|
||||
return null;
|
||||
return $"{(char)ident[0]}{(char)ident[1]}";
|
||||
}
|
||||
|
||||
public static string GetIsMini(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var fs = new FileStream(path, FileMode.Open))
|
||||
using (var br = new BinaryReader(fs))
|
||||
return GetIsMini(br);
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
public static string GetIsMini(byte[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var ms = new MemoryStream(data))
|
||||
using (var br = new BinaryReader(ms))
|
||||
return GetIsMini(br);
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
}
|
||||
}
|
||||
114
pkNX.Containers/Misc/FnvHash.cs
Normal file
114
pkNX.Containers/Misc/FnvHash.cs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Fowler–Noll–Vo non-cryptographic hash
|
||||
/// </summary>
|
||||
public static class FnvHash
|
||||
{
|
||||
// 64 bit implementation
|
||||
private const ulong kFnvPrime_64 = 0x00000100000001b3;
|
||||
private const ulong kOffsetBasis_64 = 0xCBF29CE484222645;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code of the input sequence via the default Fnv1 method.
|
||||
/// </summary>
|
||||
/// <param name="input">Input sequence</param>
|
||||
/// <returns>Computed hash code</returns>
|
||||
public static ulong HashFnv1_64(IEnumerable<char> input)
|
||||
{
|
||||
ulong hash = kOffsetBasis_64;
|
||||
foreach (var c in input)
|
||||
{
|
||||
hash *= kFnvPrime_64;
|
||||
hash ^= c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code of the input sequence via the default Fnv1 method.
|
||||
/// </summary>
|
||||
/// <param name="input">Input sequence</param>
|
||||
/// <returns>Computed hash code</returns>
|
||||
public static ulong HashFnv1_64(IEnumerable<byte> input)
|
||||
{
|
||||
ulong hash = kOffsetBasis_64;
|
||||
foreach (var c in input)
|
||||
{
|
||||
hash *= kFnvPrime_64;
|
||||
hash ^= c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code of the input sequence via the alternative Fnv1 method.
|
||||
/// </summary>
|
||||
/// <param name="input">Input sequence</param>
|
||||
/// <returns>Computed hash code</returns>
|
||||
public static ulong HashFnv1a_64(IEnumerable<char> input)
|
||||
{
|
||||
ulong hash = kOffsetBasis_64;
|
||||
foreach (var c in input)
|
||||
{
|
||||
hash ^= c;
|
||||
hash *= kFnvPrime_64;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code of the input sequence via the alternative Fnv1 method.
|
||||
/// </summary>
|
||||
/// <param name="input">Input sequence</param>
|
||||
/// <returns>Computed hash code</returns>
|
||||
public static ulong HashFnv1a_64(IEnumerable<byte> input)
|
||||
{
|
||||
ulong hash = kOffsetBasis_64;
|
||||
foreach (var c in input)
|
||||
{
|
||||
hash ^= c;
|
||||
hash *= kFnvPrime_64;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// 32 bit implementation
|
||||
private const uint kFnvPrime_32 = 0x01000193;
|
||||
private const uint kOffsetBasis_32 = 0x811C9DC5;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code of the input sequence via the default Fnv1 method.
|
||||
/// </summary>
|
||||
/// <param name="input">Input sequence</param>
|
||||
/// <returns>Computed hash code</returns>
|
||||
public static uint HashFnv1_32(IEnumerable<char> input)
|
||||
{
|
||||
uint hash = kOffsetBasis_32;
|
||||
foreach (var c in input)
|
||||
{
|
||||
hash *= kFnvPrime_32;
|
||||
hash ^= c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code of the input sequence via the alternative Fnv1 method.
|
||||
/// </summary>
|
||||
/// <param name="input">Input sequence</param>
|
||||
/// <returns>Computed hash code</returns>
|
||||
public static uint HashFnv1a_32(IEnumerable<char> input)
|
||||
{
|
||||
uint hash = kOffsetBasis_32;
|
||||
foreach (var c in input)
|
||||
{
|
||||
hash ^= c;
|
||||
hash *= kFnvPrime_32;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
285
pkNX.Containers/Misc/GFPack.cs
Normal file
285
pkNX.Containers/Misc/GFPack.cs
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class GFPack : IEnumerable<byte[]>, IFileContainer
|
||||
{
|
||||
private const ulong Magic = 0x4B434150_584C4647; // GFLXPACK
|
||||
|
||||
// Overall structure: Header, metadata, and the raw compressed files
|
||||
public GFPackHeader Header { get; set; }
|
||||
public FileHashPath[] HashPaths { get; set; }
|
||||
public FileHashIndex[] HashIndexes { get; set; }
|
||||
public FileData[] FileTable { get; set; }
|
||||
public byte[][] CompressedFiles { get; set; }
|
||||
public byte[][] DecompressedFiles { get; set; }
|
||||
|
||||
public GFPack(string directory, string parent = @"\bin") => LoadFiles(Directory.GetFiles(directory), parent);
|
||||
public GFPack(string[] directories, string parent = @"\bin") => LoadFiles(directories, parent);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a packed <see cref="GFPack"/> object, and unpacks the data to accessible properties.
|
||||
/// </summary>
|
||||
/// <param name="data">Packed file</param>
|
||||
public GFPack(byte[] data)
|
||||
{
|
||||
using (var ms = new MemoryStream(data))
|
||||
using (var br = new BinaryReader(ms))
|
||||
ReadPack(br);
|
||||
}
|
||||
|
||||
public GFPack(BinaryReader br) => ReadPack(br);
|
||||
|
||||
private void ReadPack(BinaryReader br)
|
||||
{
|
||||
Header = br.ReadBytes(GFPackHeader.SIZE).ToClass<GFPackHeader>();
|
||||
Debug.Assert(Header.MAGIC == Magic);
|
||||
|
||||
HashPaths = new FileHashPath[Header.CountFiles];
|
||||
for (int i = 0; i < HashPaths.Length; i++)
|
||||
HashPaths[i] = br.ReadBytes(FileHashPath.SIZE).ToClass<FileHashPath>();
|
||||
|
||||
HashIndexes = new FileHashIndex[Header.CountFiles];
|
||||
for (int i = 0; i < HashIndexes.Length; i++)
|
||||
HashIndexes[i] = br.ReadBytes(FileHashIndex.SIZE).ToClass<FileHashIndex>();
|
||||
|
||||
FileTable = new FileData[Header.CountFiles];
|
||||
for (int i = 0; i < FileTable.Length; i++)
|
||||
FileTable[i] = br.ReadBytes(FileData.SIZE).ToClass<FileData>();
|
||||
|
||||
CompressedFiles = new byte[Header.CountFiles][];
|
||||
for (int i = 0; i < CompressedFiles.Length; i++)
|
||||
{
|
||||
br.BaseStream.Position = FileTable[i].OffsetPacked;
|
||||
CompressedFiles[i] = br.ReadBytes(FileTable[i].SizeCompressed);
|
||||
}
|
||||
|
||||
DecompressedFiles = new byte[Header.CountFiles][];
|
||||
for (int i = 0; i < DecompressedFiles.Length; i++)
|
||||
DecompressedFiles[i] = Decompress(CompressedFiles[i], FileTable[i].SizeDecompressed, FileTable[i].Type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intro bytes to the packed binary.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class GFPackHeader
|
||||
{
|
||||
public const int SIZE = 0x40;
|
||||
public ulong MAGIC = Magic;
|
||||
public uint Version = 0x1000;
|
||||
public uint IsRelocated; // bit0
|
||||
|
||||
/// <summary>
|
||||
/// Count of Files packed into the binary.
|
||||
/// </summary>
|
||||
public int CountFiles;
|
||||
|
||||
/// <summary>
|
||||
/// Count of Folders packed into the binary.
|
||||
/// </summary>
|
||||
public int CountFolders;
|
||||
|
||||
/// <summary>
|
||||
/// Data offset for the <see cref="FileHashPath"/> table.
|
||||
/// </summary>
|
||||
public long PtrHashPaths;
|
||||
|
||||
/// <summary>
|
||||
/// Data offset for the <see cref="FileHashIndex"/> table.
|
||||
/// </summary>
|
||||
public long PtrHashIndexes;
|
||||
|
||||
/// <summary>
|
||||
/// Data offset for the <see cref="FileData"/> table.
|
||||
/// </summary>
|
||||
public long PtrFileTable;
|
||||
|
||||
public ulong Reserved1;
|
||||
public ulong Reserved2;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class FileHashPath
|
||||
{
|
||||
public const int SIZE = 0x08;
|
||||
|
||||
/// <summary>
|
||||
/// Filename (with directory details) hash.
|
||||
/// </summary>
|
||||
public ulong HashFnv1aPathFull;
|
||||
|
||||
public bool IsMatch(string fileName) => FnvHash.HashFnv1a_64(fileName) == HashFnv1aPathFull;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class FileHashIndex
|
||||
{
|
||||
public const int SIZE = 0x10;
|
||||
|
||||
/// <summary>
|
||||
/// Filename (without directory details) hash.
|
||||
/// </summary>
|
||||
public ulong HashFnv1aPathFileName;
|
||||
public int Index;
|
||||
public uint Padding = 0xCC;
|
||||
|
||||
public bool IsMatch(string fileName) => FnvHash.HashFnv1a_64(fileName) == HashFnv1aPathFileName;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class FileData
|
||||
{
|
||||
public const int SIZE = 0x18;
|
||||
|
||||
public ushort Level = 9; // quality?
|
||||
public CompressionType Type;
|
||||
public int SizeDecompressed;
|
||||
public int SizeCompressed;
|
||||
public int Padding = 0xCC;
|
||||
public int OffsetPacked;
|
||||
public uint unused;
|
||||
}
|
||||
|
||||
public enum CompressionType : ushort
|
||||
{
|
||||
None = 0,
|
||||
Zlib = 1,
|
||||
Lz4 = 2,
|
||||
}
|
||||
|
||||
public IEnumerator<byte[]> GetEnumerator() => (IEnumerator<byte[]>)DecompressedFiles.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => DecompressedFiles.GetEnumerator();
|
||||
public int Count => Header.CountFiles;
|
||||
|
||||
public byte[] this[int index]
|
||||
{
|
||||
get => DecompressedFiles[index];
|
||||
set
|
||||
{
|
||||
Modified = true;
|
||||
DecompressedFiles[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetIndexFull(ulong hash) => Array.FindIndex(HashPaths, z => z.HashFnv1aPathFull == hash);
|
||||
public int GetIndexFileName(ulong hash) => Array.FindIndex(HashIndexes, z => z.HashFnv1aPathFileName == hash);
|
||||
public int GetIndexFileName(string name) => Array.FindIndex(HashIndexes, z => z.IsMatch(name));
|
||||
|
||||
public void LoadFiles(string[] directories, string parent, CompressionType type = CompressionType.Lz4)
|
||||
{
|
||||
var files = directories.SelectMany(Directory.GetFiles).ToArray();
|
||||
Header = new GFPackHeader { CountFolders = directories.Length, CountFiles = files.Length };
|
||||
|
||||
HashPaths = new FileHashPath[files.Length];
|
||||
HashIndexes = new FileHashIndex[files.Length];
|
||||
FileTable = new FileData[files.Length];
|
||||
DecompressedFiles = new byte[files.Length][];
|
||||
CompressedFiles = new byte[files.Length][];
|
||||
for (var i = 0; i < files.Length; i++)
|
||||
{
|
||||
var file = files[i];
|
||||
var nameshort = Path.GetFileName(file);
|
||||
var namelong = file.Substring(file.IndexOf(parent, StringComparison.Ordinal));
|
||||
ulong hashShort = FnvHash.HashFnv1a_64(nameshort);
|
||||
ulong hashFull = FnvHash.HashFnv1a_64(namelong);
|
||||
|
||||
HashPaths[i] = new FileHashPath {HashFnv1aPathFull = hashFull};
|
||||
HashIndexes[i] = new FileHashIndex {HashFnv1aPathFileName = hashShort, Index = i};
|
||||
FileTable[i] = new FileData {Type = type};
|
||||
DecompressedFiles[i] = File.ReadAllBytes(file);
|
||||
}
|
||||
Modified = true;
|
||||
}
|
||||
|
||||
public byte[] Write()
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
WriteHeaderTableList(bw);
|
||||
for (var i = 0; i < DecompressedFiles.Length; i++)
|
||||
{
|
||||
var entry = FileTable[i];
|
||||
var f = DecompressedFiles[i];
|
||||
var c = Compress(f, entry.Type);
|
||||
CompressedFiles[i] = c;
|
||||
|
||||
// update entry details
|
||||
entry.SizeDecompressed = f.Length;
|
||||
entry.SizeCompressed = c.Length;
|
||||
entry.OffsetPacked = (int)bw.BaseStream.Position;
|
||||
|
||||
bw.Write(c);
|
||||
// no padding
|
||||
}
|
||||
bw.BaseStream.Position = 0;
|
||||
WriteHeaderTableList(bw);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] Decompress(byte[] encryptedData, int decryptedLength, CompressionType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CompressionType.None:
|
||||
return encryptedData;
|
||||
case CompressionType.Zlib:
|
||||
return null; // not implemented
|
||||
default:
|
||||
return LZ4.Decode(encryptedData, decryptedLength);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] Compress(byte[] decryptedData, CompressionType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CompressionType.None:
|
||||
return decryptedData;
|
||||
case CompressionType.Zlib:
|
||||
return null; // not implemented
|
||||
default:
|
||||
return LZ4.Encode(decryptedData);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteHeaderTableList(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Header.ToBytesClass());
|
||||
Header.PtrHashPaths = bw.BaseStream.Position;
|
||||
bw.Write(HashPaths.ToBytesClass());
|
||||
Header.PtrHashIndexes = bw.BaseStream.Position;
|
||||
bw.Write(HashIndexes.ToBytesClass());
|
||||
Header.PtrFileTable = bw.BaseStream.Position;
|
||||
bw.Write(FileTable.ToBytesClass());
|
||||
}
|
||||
|
||||
public string FilePath { get; set; }
|
||||
public bool Modified { get; set; }
|
||||
public Task<byte[][]> GetFiles() => new Task<byte[][]>(() => DecompressedFiles);
|
||||
public Task<byte[]> GetFile(int file, int subFile = 0) => new Task<byte[]>(Write);
|
||||
public Task SetFile(int file, byte[] value, int subFile = 0) => new Task(() => this[file] = value);
|
||||
public Task SaveAs(string path, ContainerHandler handler, CancellationToken token) => new Task(() => Dump(path, handler));
|
||||
|
||||
public void Dump(string path, ContainerHandler handler)
|
||||
{
|
||||
for (var i = 0; i < FileTable.Length; i++)
|
||||
{
|
||||
var hashFull = HashPaths[i].HashFnv1aPathFull;
|
||||
var loc = Path.Combine(path ?? FilePath, $"{i:000} {hashFull:X16}");
|
||||
var data = DecompressedFiles[i];
|
||||
File.WriteAllBytes(loc, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
pkNX.Containers/Misc/LZ4.cs
Normal file
10
pkNX.Containers/Misc/LZ4.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
using LZ4;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public static class LZ4
|
||||
{
|
||||
public static byte[] Decode(byte[] data, int decLength) => LZ4Codec.Decode(data, 0, data.Length, decLength);
|
||||
public static byte[] Encode(byte[] data) => LZ4Codec.Encode(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
46
pkNX.Containers/Misc/StructConverter.cs
Normal file
46
pkNX.Containers/Misc/StructConverter.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public static class StructConverter
|
||||
{
|
||||
public static T ToStructure<T>(this byte[] bytes) where T : struct
|
||||
{
|
||||
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
try { return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); }
|
||||
finally { handle.Free(); }
|
||||
}
|
||||
|
||||
public static T ToClass<T>(this byte[] bytes) where T : class
|
||||
{
|
||||
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
try { return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); }
|
||||
finally { handle.Free(); }
|
||||
}
|
||||
|
||||
public static byte[] ToBytesClass<T>(this T obj) where T : class
|
||||
{
|
||||
int size = Marshal.SizeOf(obj);
|
||||
byte[] arr = new byte[size];
|
||||
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
Marshal.StructureToPtr(obj, ptr, true);
|
||||
Marshal.Copy(ptr, arr, 0, size);
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static byte[] ToBytes<T>(this T obj) where T : struct
|
||||
{
|
||||
int size = Marshal.SizeOf(obj);
|
||||
byte[] arr = new byte[size];
|
||||
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
Marshal.StructureToPtr(obj, ptr, true);
|
||||
Marshal.Copy(ptr, arr, 0, size);
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
}
|
||||
168
pkNX.Containers/SARC/SARC.cs
Normal file
168
pkNX.Containers/SARC/SARC.cs
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple (?) ARChive
|
||||
/// </summary>
|
||||
public sealed class SARC : LargeContainer
|
||||
{
|
||||
private const string Identifier = nameof(SARC);
|
||||
|
||||
public SARCHeader Header { get; set; }
|
||||
public SFAT SFAT;
|
||||
public SFNT SFNT;
|
||||
|
||||
public override int Count => SFAT.Entries.Count;
|
||||
|
||||
/// <summary>
|
||||
/// The required <see cref="SARCHeader.Magic"/> matches the first 4 bytes of the file data.
|
||||
/// </summary>
|
||||
public bool SigMatches => Header.Magic == Identifier;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an empty <see cref="SARC"/>.
|
||||
/// </summary>
|
||||
/// <param name="files">Files inside the <see cref="SARC"/>.</param>
|
||||
/// <param name="baseFolder">Root location of the archive</param>
|
||||
public SARC(IReadOnlyList<string> files, string baseFolder)
|
||||
{
|
||||
Header = new SARCHeader();
|
||||
SFAT = new SFAT(baseFolder, files);
|
||||
SFNT = new SFNT();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="SARC"/> from a file location.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public SARC(string path) => OpenBinary(path);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="SARC"/> from a provided stream.
|
||||
/// </summary>
|
||||
/// <param name="fs"></param>
|
||||
public SARC(Stream fs) => OpenRead(new BinaryReader(fs));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="SARC"/> from a provided reader.
|
||||
/// </summary>
|
||||
/// <param name="br"></param>
|
||||
public SARC(BinaryReader br) => OpenRead(br);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the contents of the <see cref="SARC"/> header and file info tables.
|
||||
/// </summary>
|
||||
protected override void Initialize()
|
||||
{
|
||||
Header = new SARCHeader(Reader);
|
||||
if (!SigMatches)
|
||||
return;
|
||||
SFAT = new SFAT(Reader, Header.DataOffset);
|
||||
SFNT = new SFNT(Reader);
|
||||
}
|
||||
|
||||
protected override Task Pack(BinaryWriter bw, ContainerHandler handler, CancellationToken token)
|
||||
{
|
||||
return Task.Run(() => PackSARC(bw, handler, token), token);
|
||||
}
|
||||
|
||||
private void PackSARC(BinaryWriter bw, ContainerHandler handler, CancellationToken token)
|
||||
{
|
||||
StartPack();
|
||||
var count = SFAT.Entries.Count;
|
||||
handler.Initialize(count);
|
||||
var currentStringOffset = SFNT.StringOffset;
|
||||
|
||||
WriteIntro(bw);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var entr = SFAT.Entries[i];
|
||||
if (entr.FileName == null)
|
||||
entr.GetFileName(Reader.BaseStream, (int)currentStringOffset);
|
||||
entr.WriteFileName(Reader.BaseStream, (int)SFNT.StringOffset);
|
||||
while (Reader.BaseStream.Position % 4 != 0)
|
||||
bw.Write((byte)0);
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
WriteEntry(bw, i);
|
||||
}
|
||||
WriteIntro(bw, true);
|
||||
bw.Flush();
|
||||
}
|
||||
|
||||
private void StartPack() { }
|
||||
|
||||
private void WriteIntro(BinaryWriter bw, bool finalPass = false)
|
||||
{
|
||||
Header.Write(bw);
|
||||
SFAT.Write(bw);
|
||||
SFNT.Write(bw);
|
||||
if (finalPass)
|
||||
Header.DataOffset = (int)bw.BaseStream.Position;
|
||||
}
|
||||
|
||||
private void WriteEntry(BinaryWriter bw, int i)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override int GetFileOffset(int file, int subFile = 0)
|
||||
{
|
||||
var f = SFAT[file];
|
||||
return Header.DataOffset + f.Start;
|
||||
}
|
||||
|
||||
public override byte[] GetEntry(int index, int subFile)
|
||||
{
|
||||
var f = SFAT[index];
|
||||
if (f.File is byte[] data)
|
||||
return data;
|
||||
|
||||
data = f.GetFileData(Reader.BaseStream);
|
||||
f.File = data; // cache for future fetches
|
||||
return data;
|
||||
}
|
||||
|
||||
public override void Dump(string path, ContainerHandler handler)
|
||||
{
|
||||
path = path ?? FilePath;
|
||||
if (path == null)
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
if (File.Exists(path))
|
||||
path = Path.GetDirectoryName(path);
|
||||
if (path == null)
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
|
||||
var folder = FileName ?? "sarc";
|
||||
string dir = Path.Combine(path, folder);
|
||||
|
||||
Directory.CreateDirectory(dir);
|
||||
var count = SFAT.Entries.Count;
|
||||
handler.Initialize(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
SFAT.Entries[i].Dump(Reader.BaseStream, path, Header.DataOffset);
|
||||
handler.StepFile(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static SARC GetSARC(BinaryReader br)
|
||||
{
|
||||
if (br.BaseStream.Length < 20)
|
||||
return null;
|
||||
br.BaseStream.Position = 0;
|
||||
var ident = new string(br.ReadChars(4));
|
||||
if (ident != SARCHeader.Identifier)
|
||||
return null;
|
||||
return new SARC(br);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
pkNX.Containers/SARC/SARCHeader.cs
Normal file
47
pkNX.Containers/SARC/SARCHeader.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class SARCHeader
|
||||
{
|
||||
public const string Identifier = nameof(SARC);
|
||||
|
||||
/// <summary>
|
||||
/// The required <see cref="Magic"/> matches the first 4 bytes of the file data.
|
||||
/// </summary>
|
||||
public bool SigMatches => Magic == Identifier;
|
||||
|
||||
public string Magic = Identifier;
|
||||
public ushort HeaderSize = 0x14;
|
||||
public ushort Endianness = 0xFFFE;
|
||||
public uint FileSize;
|
||||
public int DataOffset;
|
||||
public ushort Version = 0x0100;
|
||||
public ushort Reserved;
|
||||
|
||||
public SARCHeader() { }
|
||||
|
||||
public SARCHeader(BinaryReader Reader)
|
||||
{
|
||||
Magic = new string(Reader.ReadChars(4));
|
||||
HeaderSize = Reader.ReadUInt16();
|
||||
Endianness = Reader.ReadUInt16();
|
||||
FileSize = Reader.ReadUInt32();
|
||||
DataOffset = Reader.ReadInt32();
|
||||
Version = Reader.ReadUInt16();
|
||||
Reserved = Reader.ReadUInt16();
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
foreach (var c in Magic)
|
||||
bw.Write((byte)c);
|
||||
bw.Write(HeaderSize);
|
||||
bw.Write(FileSize);
|
||||
bw.Write(FileSize);
|
||||
bw.Write(DataOffset);
|
||||
bw.Write(Version);
|
||||
bw.Write(Reserved);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
pkNX.Containers/SARC/SFAT.cs
Normal file
78
pkNX.Containers/SARC/SFAT.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="SARC"/> File Access Table
|
||||
/// </summary>
|
||||
public class SFAT
|
||||
{
|
||||
public const string Identifier = nameof(SFAT);
|
||||
|
||||
/// <summary>
|
||||
/// The required <see cref="Magic"/> matches the first 4 bytes of the file data.
|
||||
/// </summary>
|
||||
public bool SigMatches => Magic == Identifier;
|
||||
|
||||
private readonly string Magic = Identifier;
|
||||
private readonly ushort HeaderSize = 0xC;
|
||||
private readonly ushort EntryCount;
|
||||
private readonly uint HashMult = 0x65;
|
||||
public readonly List<SFATEntry> Entries;
|
||||
|
||||
public SFATEntry this[int index]
|
||||
{
|
||||
get => Entries[index];
|
||||
set => Entries[index] = value;
|
||||
}
|
||||
|
||||
public SFAT(string baseFolder, IReadOnlyList<string> files)
|
||||
{
|
||||
EntryCount = (ushort)files.Count;
|
||||
Entries = new List<SFATEntry>(EntryCount);
|
||||
for (int i = 0; i < EntryCount; i++)
|
||||
{
|
||||
Entries[i] = new SFATEntry {File = files[i]};
|
||||
|
||||
var fn = files[i].Remove(baseFolder.Length);
|
||||
Entries[i].SetFileName(fn, HashMult);
|
||||
}
|
||||
}
|
||||
|
||||
public SFAT(BinaryReader br, int DataOffset)
|
||||
{
|
||||
Magic = new string(br.ReadChars(4));
|
||||
if (!SigMatches)
|
||||
throw new FormatException(nameof(SFAT));
|
||||
|
||||
HeaderSize = br.ReadUInt16();
|
||||
EntryCount = br.ReadUInt16();
|
||||
HashMult = br.ReadUInt32();
|
||||
Entries = new List<SFATEntry>(EntryCount);
|
||||
|
||||
for (int i = 0; i < EntryCount; i++)
|
||||
Entries.Add(new SFATEntry(br, DataOffset));
|
||||
}
|
||||
|
||||
public string GetFileName(int index, Stream parent, int StringOffset) => this[index].GetFileName(parent, StringOffset);
|
||||
public void SetFileName(int index, string value) => this[index].SetFileName(value, HashMult);
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
foreach (var c in Magic)
|
||||
bw.Write((byte)c);
|
||||
bw.Write(HeaderSize);
|
||||
bw.Write(EntryCount);
|
||||
bw.Write(HashMult);
|
||||
WriteEntries(bw);
|
||||
}
|
||||
|
||||
public void WriteEntries(BinaryWriter bw)
|
||||
{
|
||||
foreach (var entry in Entries)
|
||||
entry.Write(bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
pkNX.Containers/SARC/SFATEntry.cs
Normal file
81
pkNX.Containers/SARC/SFATEntry.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="SARC"/> File Access Table (<see cref="SFAT"/>) Entry
|
||||
/// </summary>
|
||||
public class SFATEntry : LargeContainerEntry
|
||||
{
|
||||
public uint FileNameHash;
|
||||
public int FileNameOffset;
|
||||
|
||||
public string FileName { get; private set; }
|
||||
|
||||
private static uint GetHash(string name, int length, uint multiplier)
|
||||
{
|
||||
uint result = 0;
|
||||
for (int i = 0; i < length; i++)
|
||||
result = name[i] + (result * multiplier);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override int Length
|
||||
{
|
||||
get => End - Start;
|
||||
set { }
|
||||
}
|
||||
|
||||
public SFATEntry() { }
|
||||
|
||||
public SFATEntry(BinaryReader br, int DataOffset)
|
||||
{
|
||||
FileNameHash = br.ReadUInt32();
|
||||
FileNameOffset = br.ReadInt32();
|
||||
Start = br.ReadInt32();
|
||||
End = br.ReadInt32();
|
||||
ParentDataPosition = DataOffset;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(FileNameHash);
|
||||
bw.Write(FileNameOffset);
|
||||
bw.Write(Start);
|
||||
bw.Write(End);
|
||||
}
|
||||
|
||||
public string GetFileName(Stream parent, int StringOffset)
|
||||
{
|
||||
if (FileName != null)
|
||||
return FileName;
|
||||
|
||||
var ofs = ((FileNameOffset & 0x00FFFFFF) * 4) + StringOffset;
|
||||
parent.Seek(ofs, SeekOrigin.Begin);
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (char c = (char)parent.ReadByte(); c != 0; c = (char)parent.ReadByte())
|
||||
sb.Append(c);
|
||||
|
||||
FileName = sb.Replace('/', Path.DirectorySeparatorChar).ToString();
|
||||
return FileName;
|
||||
}
|
||||
|
||||
public void WriteFileName(Stream parent, int StringOffset)
|
||||
{
|
||||
FileNameOffset = (int)(parent.Position - StringOffset) / 4;
|
||||
|
||||
var str = FileName.Replace(Path.DirectorySeparatorChar, '/');
|
||||
foreach (var b in str)
|
||||
parent.WriteByte((byte)b);
|
||||
parent.WriteByte(0); // \0
|
||||
}
|
||||
|
||||
public void SetFileName(string value, uint hashMult)
|
||||
{
|
||||
FileName = value;
|
||||
FileNameHash = GetHash(value, value.Length, hashMult);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
pkNX.Containers/SARC/SFNT.cs
Normal file
45
pkNX.Containers/SARC/SFNT.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="SARC"/> File Name Table
|
||||
/// </summary>
|
||||
public class SFNT
|
||||
{
|
||||
public const string Identifier = nameof(SFNT);
|
||||
|
||||
/// <summary>
|
||||
/// The required <see cref="Magic"/> matches the first 4 bytes of the file data.
|
||||
/// </summary>
|
||||
public bool SigMatches => Magic == Identifier;
|
||||
|
||||
public string Magic = Identifier;
|
||||
public ushort HeaderSize;
|
||||
public ushort Reserved;
|
||||
public uint StringOffset;
|
||||
|
||||
public SFNT() { }
|
||||
|
||||
public SFNT(BinaryReader br)
|
||||
{
|
||||
Magic = new string(br.ReadChars(4));
|
||||
if (!SigMatches)
|
||||
throw new FormatException(nameof(SFNT));
|
||||
|
||||
HeaderSize = br.ReadUInt16();
|
||||
Reserved = br.ReadUInt16();
|
||||
StringOffset = (uint)br.BaseStream.Position;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
foreach (var c in Magic)
|
||||
bw.Write((byte)c);
|
||||
bw.Write(HeaderSize);
|
||||
bw.Write(Reserved);
|
||||
StringOffset = (uint)bw.BaseStream.Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
pkNX.Containers/SingleFileContainer.cs
Normal file
31
pkNX.Containers/SingleFileContainer.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace pkNX.Containers
|
||||
{
|
||||
public class SingleFileContainer : IFileContainer
|
||||
{
|
||||
public string FilePath { get; set; }
|
||||
public bool Modified { get; set; }
|
||||
public int Count => 1;
|
||||
|
||||
public byte[] Data;
|
||||
public SingleFileContainer(byte[] data) => Data = data;
|
||||
public SingleFileContainer(BinaryReader br) => Data = br.ReadBytes((int) br.BaseStream.Length);
|
||||
|
||||
public SingleFileContainer(string path)
|
||||
{
|
||||
FilePath = path;
|
||||
Data = File.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
public byte[] this[int index] { get => Data; set => Data = value; }
|
||||
public Task<byte[][]> GetFiles() => new Task<byte[][]>(() => new[] {Data});
|
||||
public Task<byte[]> GetFile(int file, int subFile = 0) => new Task<byte[]>(() => Data);
|
||||
public Task SetFile(int file, byte[] value, int subFile = 0) => new Task(() => Data = value);
|
||||
public Task SaveAs(string path, ContainerHandler handler, CancellationToken token) => new Task(() => Dump(path, handler));
|
||||
public void Dump(string path, ContainerHandler handler) => File.WriteAllBytes(path ?? FilePath, Data);
|
||||
}
|
||||
}
|
||||
12
pkNX.Containers/pkNX.Containers.csproj
Normal file
12
pkNX.Containers/pkNX.Containers.csproj
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
|
||||
<Description>Packing & Unpacking</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="lz4net" Version="1.0.15.93" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
7
pkNX.Game/Editors/EditorBase.cs
Normal file
7
pkNX.Game/Editors/EditorBase.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace pkNX.Game
|
||||
{
|
||||
public class EditorBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
6
pkNX.Game/Editors/EditorFactory.cs
Normal file
6
pkNX.Game/Editors/EditorFactory.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace pkNX.Game
|
||||
{
|
||||
class EditorFactory
|
||||
{
|
||||
}
|
||||
}
|
||||
146
pkNX.Game/File/GameFile.cs
Normal file
146
pkNX.Game/File/GameFile.cs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
namespace pkNX.Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple descriptor related to what purpose the game data serves.
|
||||
/// </summary>
|
||||
public enum GameFile
|
||||
{
|
||||
/// <summary> Contains the game text that is commonly re-used, not related to the storyline or general overworld content. </summary>
|
||||
GameText,
|
||||
|
||||
/// <summary> Contains the story text that is used to tell the story via overworld events and interactions. </summary>
|
||||
StoryText,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.カタカナ"/>. </summary>
|
||||
GameText0,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.汉字简化方案"/>. </summary>
|
||||
GameText1,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.English"/>. </summary>
|
||||
GameText2,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.Français"/>. </summary>
|
||||
GameText3,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.Italiano"/>. </summary>
|
||||
GameText4,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.Deutsch"/>. </summary>
|
||||
GameText5,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.Español"/>. </summary>
|
||||
GameText6,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.한국"/>. </summary>
|
||||
GameText7,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.汉字简化方案"/>. </summary>
|
||||
GameText8,
|
||||
|
||||
/// <summary> Localized Game Text for <see cref="GameLanguage.漢字簡化方案"/>. </summary>
|
||||
GameText9,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.カタカナ"/>. </summary>
|
||||
StoryText0,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.汉字简化方案"/>. </summary>
|
||||
StoryText1,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.English"/>. </summary>
|
||||
StoryText2,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.Français"/>. </summary>
|
||||
StoryText3,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.Italiano"/>. </summary>
|
||||
StoryText4,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.Deutsch"/>. </summary>
|
||||
StoryText5,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.Español"/>. </summary>
|
||||
StoryText6,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.한국"/>. </summary>
|
||||
StoryText7,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.汉字简化方案"/>. </summary>
|
||||
StoryText8,
|
||||
|
||||
/// <summary> Localized Story Text for <see cref="GameLanguage.漢字簡化方案"/>. </summary>
|
||||
StoryText9,
|
||||
|
||||
/// <summary> Overworld grass/etc encounterable species data. </summary>
|
||||
Encounters,
|
||||
|
||||
/// <summary> Trainer Data related to Trainers of a shared type. </summary>
|
||||
TrainerClass,
|
||||
|
||||
/// <summary> Trainer Data for individual Trainers that can be battled. </summary>
|
||||
TrainerData,
|
||||
|
||||
/// <summary> Trainer PKM template data for regular battles. </summary>
|
||||
TrainerPoke,
|
||||
|
||||
/// <summary> Move data that defines the properties of in-game moves. </summary>
|
||||
MoveStats,
|
||||
|
||||
/// <summary> Egg Moves a species can learn when bred. </summary>
|
||||
EggMoves,
|
||||
|
||||
/// <summary> Moves a species can learn via level up. </summary>
|
||||
Learnsets,
|
||||
|
||||
/// <summary> Evolutions a species can have under specified conditions. </summary>
|
||||
Evolutions,
|
||||
|
||||
/// <summary> Mega Evolutions a species can have under specified conditions. </summary>
|
||||
MegaEvolutions,
|
||||
|
||||
/// <summary> In-game stats a species can have. </summary>
|
||||
PersonalStats,
|
||||
|
||||
/// <summary> Properties of in-game posessible items. </summary>
|
||||
ItemStats,
|
||||
|
||||
/// <summary> Static (fixed position/condition) encounter table. </summary>
|
||||
EncounterStatic,
|
||||
|
||||
/// <summary> Post-game roulette trainer data with normal difficulty. </summary>
|
||||
FacilityTrainerNormal,
|
||||
|
||||
/// <summary> Post-game roulette trainer data with heightened difficulty. </summary>
|
||||
FacilityTrainerSuper,
|
||||
|
||||
/// <summary> Post-game roulette PKM template data for normal difficulty trainers. </summary>
|
||||
FacilityPokeNormal,
|
||||
|
||||
/// <summary> Post-game roulette PKM template data for heightened difficulty trainers. </summary>
|
||||
FacilityPokeSuper,
|
||||
|
||||
/// <summary> Title Screen staging data. </summary>
|
||||
TitleScreen,
|
||||
|
||||
/// <summary> Box Interface wallpapers. </summary>
|
||||
Wallpaper,
|
||||
|
||||
/// <summary> Walk/Collision data for individual Maps. </summary>
|
||||
MapMatrix,
|
||||
|
||||
/// <summary> Zone assembling information to build large maps from individual small zones. </summary>
|
||||
MapGameRegion,
|
||||
|
||||
/// <summary> Area settings and permissives related to in-game areas the player travels to. </summary>
|
||||
ZoneData,
|
||||
|
||||
/// <summary> Post-battle items that can be picked up. </summary>
|
||||
Pickup,
|
||||
|
||||
/// <summary> Map data for individual zones. </summary>
|
||||
WorldData,
|
||||
|
||||
/// <summary> UI Sprites for pretty in-game move descriptors. </summary>
|
||||
MoveSprites,
|
||||
}
|
||||
}
|
||||
309
pkNX.Game/File/GameFileMapping.cs
Normal file
309
pkNX.Game/File/GameFileMapping.cs
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using pkNX.Containers;
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles file retrieval and lifetime management for a <see cref="GameLocation"/>'s <see cref="IFileContainer"/> data.
|
||||
/// </summary>
|
||||
public class GameFileMapping
|
||||
{
|
||||
private readonly Dictionary<GameFile, IFileContainer> Cache = new Dictionary<GameFile, IFileContainer>();
|
||||
private readonly IReadOnlyCollection<GameFileReference> FileMap;
|
||||
|
||||
public readonly ContainerHandler ProgressTracker = new ContainerHandler();
|
||||
public readonly CancellationTokenSource TokenSource = new CancellationTokenSource();
|
||||
|
||||
private readonly GameLocation ROM;
|
||||
public GameFileMapping(GameLocation rom) => FileMap = GetMapping((ROM = rom).Game);
|
||||
|
||||
internal IFileContainer GetFile(GameFile file, int language)
|
||||
{
|
||||
if (file == GameFile.GameText || file == GameFile.StoryText)
|
||||
file += language; // shift to localized language
|
||||
|
||||
if (Cache.TryGetValue(file, out var container))
|
||||
return container;
|
||||
|
||||
var info = FileMap.FirstOrDefault(f => f.File == file);
|
||||
if (info == null)
|
||||
throw new ArgumentException($"Unknown {nameof(GameFile)} provided.", file.ToString());
|
||||
|
||||
var basePath = info.Parent == ContainerParent.ExeFS ? ROM.ExeFS : ROM.RomFS;
|
||||
container = info.Get(basePath);
|
||||
Cache.Add(file, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
internal void SaveAll()
|
||||
{
|
||||
foreach (var container in Cache)
|
||||
{
|
||||
var c = container.Value;
|
||||
c.SaveAs(c.FilePath, ProgressTracker, TokenSource.Token);
|
||||
Cache.Remove(container.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public static IReadOnlyCollection<GameFileReference> GetMapping(GameVersion game)
|
||||
{
|
||||
switch (game)
|
||||
{
|
||||
case GameVersion.SN: return SN;
|
||||
case GameVersion.MN: return MN;
|
||||
case GameVersion.US: return US;
|
||||
case GameVersion.UM: return UM;
|
||||
case GameVersion.XY: return XY;
|
||||
case GameVersion.GG: return GG;
|
||||
case GameVersion.ORASDEMO:
|
||||
case GameVersion.ORAS: return AO;
|
||||
case GameVersion.SMDEMO: return SMDEMO;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#region Games
|
||||
private static readonly GameFileReference[] XY =
|
||||
{
|
||||
new GameFileReference(005, GameFile.MoveSprites),
|
||||
new GameFileReference(012, GameFile.Encounters),
|
||||
new GameFileReference(038, GameFile.TrainerData),
|
||||
new GameFileReference(039, GameFile.TrainerClass),
|
||||
new GameFileReference(040, GameFile.TrainerPoke),
|
||||
new GameFileReference(041, GameFile.MapGameRegion),
|
||||
new GameFileReference(042, GameFile.MapMatrix),
|
||||
|
||||
new GameFileReference(072, GameFile.GameText0),
|
||||
new GameFileReference(073, GameFile.GameText1),
|
||||
new GameFileReference(074, GameFile.GameText2),
|
||||
new GameFileReference(075, GameFile.GameText3),
|
||||
new GameFileReference(076, GameFile.GameText4),
|
||||
new GameFileReference(077, GameFile.GameText5),
|
||||
new GameFileReference(078, GameFile.GameText6),
|
||||
new GameFileReference(079, GameFile.GameText7),
|
||||
new GameFileReference(080, GameFile.StoryText0),
|
||||
new GameFileReference(081, GameFile.StoryText1),
|
||||
new GameFileReference(082, GameFile.StoryText2),
|
||||
new GameFileReference(083, GameFile.StoryText3),
|
||||
new GameFileReference(084, GameFile.StoryText4),
|
||||
new GameFileReference(085, GameFile.StoryText5),
|
||||
new GameFileReference(086, GameFile.StoryText6),
|
||||
new GameFileReference(087, GameFile.StoryText7),
|
||||
|
||||
new GameFileReference(104, GameFile.Wallpaper),
|
||||
new GameFileReference(165, GameFile.TitleScreen),
|
||||
new GameFileReference(203, GameFile.FacilityPokeNormal),
|
||||
new GameFileReference(204, GameFile.FacilityTrainerNormal),
|
||||
new GameFileReference(205, GameFile.FacilityPokeSuper),
|
||||
new GameFileReference(206, GameFile.FacilityTrainerSuper),
|
||||
new GameFileReference(212, GameFile.MoveStats),
|
||||
new GameFileReference(213, GameFile.EggMoves),
|
||||
new GameFileReference(214, GameFile.Learnsets),
|
||||
new GameFileReference(215, GameFile.Evolutions),
|
||||
new GameFileReference(216, GameFile.MegaEvolutions),
|
||||
new GameFileReference(218, GameFile.PersonalStats),
|
||||
new GameFileReference(220, GameFile.ItemStats),
|
||||
};
|
||||
|
||||
private static readonly GameFileReference[] AO =
|
||||
{
|
||||
new GameFileReference(013, GameFile.Encounters),
|
||||
new GameFileReference(036, GameFile.TrainerData),
|
||||
new GameFileReference(037, GameFile.TrainerClass),
|
||||
new GameFileReference(038, GameFile.TrainerPoke),
|
||||
new GameFileReference(039, GameFile.MapGameRegion),
|
||||
new GameFileReference(040, GameFile.MapMatrix),
|
||||
|
||||
new GameFileReference(071, GameFile.GameText0),
|
||||
new GameFileReference(072, GameFile.GameText1),
|
||||
new GameFileReference(073, GameFile.GameText2),
|
||||
new GameFileReference(074, GameFile.GameText3),
|
||||
new GameFileReference(075, GameFile.GameText4),
|
||||
new GameFileReference(076, GameFile.GameText5),
|
||||
new GameFileReference(077, GameFile.GameText6),
|
||||
new GameFileReference(078, GameFile.GameText7),
|
||||
new GameFileReference(079, GameFile.StoryText0),
|
||||
new GameFileReference(080, GameFile.StoryText1),
|
||||
new GameFileReference(081, GameFile.StoryText2),
|
||||
new GameFileReference(082, GameFile.StoryText3),
|
||||
new GameFileReference(083, GameFile.StoryText4),
|
||||
new GameFileReference(084, GameFile.StoryText5),
|
||||
new GameFileReference(085, GameFile.StoryText6),
|
||||
new GameFileReference(086, GameFile.StoryText7),
|
||||
|
||||
new GameFileReference(103, GameFile.Wallpaper),
|
||||
new GameFileReference(152, GameFile.TitleScreen),
|
||||
new GameFileReference(182, GameFile.FacilityPokeNormal),
|
||||
new GameFileReference(183, GameFile.FacilityTrainerNormal),
|
||||
new GameFileReference(184, GameFile.FacilityPokeSuper),
|
||||
new GameFileReference(185, GameFile.FacilityTrainerSuper),
|
||||
new GameFileReference(189, GameFile.MoveStats),
|
||||
new GameFileReference(190, GameFile.EggMoves),
|
||||
new GameFileReference(191, GameFile.Learnsets),
|
||||
new GameFileReference(192, GameFile.Evolutions),
|
||||
new GameFileReference(193, GameFile.MegaEvolutions),
|
||||
new GameFileReference(195, GameFile.PersonalStats),
|
||||
new GameFileReference(197, GameFile.ItemStats),
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region Gen7
|
||||
private static readonly GameFileReference[] SMDEMO =
|
||||
{
|
||||
new GameFileReference(011, GameFile.MoveStats),
|
||||
new GameFileReference(012, GameFile.EggMoves),
|
||||
new GameFileReference(013, GameFile.Learnsets),
|
||||
new GameFileReference(014, GameFile.Evolutions),
|
||||
new GameFileReference(015, GameFile.MegaEvolutions),
|
||||
new GameFileReference(017, GameFile.PersonalStats),
|
||||
new GameFileReference(019, GameFile.ItemStats),
|
||||
|
||||
new GameFileReference(030, GameFile.GameText0),
|
||||
new GameFileReference(031, GameFile.GameText1),
|
||||
new GameFileReference(032, GameFile.GameText2),
|
||||
new GameFileReference(033, GameFile.GameText3),
|
||||
new GameFileReference(034, GameFile.GameText4),
|
||||
new GameFileReference(035, GameFile.GameText5),
|
||||
new GameFileReference(036, GameFile.GameText6),
|
||||
new GameFileReference(037, GameFile.GameText7),
|
||||
new GameFileReference(038, GameFile.GameText8),
|
||||
new GameFileReference(039, GameFile.GameText9),
|
||||
new GameFileReference(040, GameFile.StoryText0),
|
||||
new GameFileReference(041, GameFile.StoryText1),
|
||||
new GameFileReference(042, GameFile.StoryText2),
|
||||
new GameFileReference(043, GameFile.StoryText3),
|
||||
new GameFileReference(044, GameFile.StoryText4),
|
||||
new GameFileReference(045, GameFile.StoryText5),
|
||||
new GameFileReference(046, GameFile.StoryText6),
|
||||
new GameFileReference(047, GameFile.StoryText7),
|
||||
new GameFileReference(048, GameFile.StoryText8),
|
||||
new GameFileReference(049, GameFile.StoryText9),
|
||||
|
||||
new GameFileReference(076, GameFile.ZoneData),
|
||||
new GameFileReference(091, GameFile.WorldData),
|
||||
|
||||
new GameFileReference(101, GameFile.TrainerClass),
|
||||
new GameFileReference(102, GameFile.TrainerData),
|
||||
new GameFileReference(103, GameFile.TrainerPoke),
|
||||
};
|
||||
|
||||
private static readonly GameFileReference[] SM =
|
||||
{
|
||||
new GameFileReference(011, GameFile.MoveStats),
|
||||
new GameFileReference(012, GameFile.EggMoves),
|
||||
new GameFileReference(013, GameFile.Learnsets),
|
||||
new GameFileReference(014, GameFile.Evolutions),
|
||||
new GameFileReference(015, GameFile.MegaEvolutions),
|
||||
new GameFileReference(017, GameFile.PersonalStats),
|
||||
new GameFileReference(019, GameFile.ItemStats),
|
||||
|
||||
new GameFileReference(030, GameFile.GameText0),
|
||||
new GameFileReference(031, GameFile.GameText1),
|
||||
new GameFileReference(032, GameFile.GameText2),
|
||||
new GameFileReference(033, GameFile.GameText3),
|
||||
new GameFileReference(034, GameFile.GameText4),
|
||||
new GameFileReference(035, GameFile.GameText5),
|
||||
new GameFileReference(036, GameFile.GameText6),
|
||||
new GameFileReference(037, GameFile.GameText7),
|
||||
new GameFileReference(038, GameFile.GameText8),
|
||||
new GameFileReference(039, GameFile.GameText9),
|
||||
new GameFileReference(040, GameFile.StoryText0),
|
||||
new GameFileReference(041, GameFile.StoryText1),
|
||||
new GameFileReference(042, GameFile.StoryText2),
|
||||
new GameFileReference(043, GameFile.StoryText3),
|
||||
new GameFileReference(044, GameFile.StoryText4),
|
||||
new GameFileReference(045, GameFile.StoryText5),
|
||||
new GameFileReference(046, GameFile.StoryText6),
|
||||
new GameFileReference(047, GameFile.StoryText7),
|
||||
new GameFileReference(048, GameFile.StoryText8),
|
||||
new GameFileReference(049, GameFile.StoryText9),
|
||||
|
||||
new GameFileReference(077, GameFile.ZoneData),
|
||||
new GameFileReference(091, GameFile.WorldData),
|
||||
|
||||
new GameFileReference(104, GameFile.TrainerClass),
|
||||
new GameFileReference(105, GameFile.TrainerData),
|
||||
new GameFileReference(106, GameFile.TrainerPoke),
|
||||
|
||||
new GameFileReference(155, GameFile.EncounterStatic),
|
||||
|
||||
new GameFileReference(267, GameFile.Pickup),
|
||||
|
||||
new GameFileReference(277, GameFile.FacilityPokeNormal),
|
||||
new GameFileReference(278, GameFile.FacilityTrainerNormal),
|
||||
new GameFileReference(279, GameFile.FacilityPokeSuper),
|
||||
new GameFileReference(280, GameFile.FacilityTrainerSuper),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Ultra Sun & Ultra Moon
|
||||
/// </summary>
|
||||
private static readonly GameFileReference[] UU =
|
||||
{
|
||||
new GameFileReference(011, GameFile.MoveStats),
|
||||
new GameFileReference(012, GameFile.EggMoves),
|
||||
new GameFileReference(013, GameFile.Learnsets),
|
||||
new GameFileReference(014, GameFile.Evolutions),
|
||||
new GameFileReference(015, GameFile.MegaEvolutions),
|
||||
new GameFileReference(017, GameFile.PersonalStats),
|
||||
new GameFileReference(019, GameFile.ItemStats),
|
||||
|
||||
new GameFileReference(030, GameFile.GameText0),
|
||||
new GameFileReference(031, GameFile.GameText1),
|
||||
new GameFileReference(032, GameFile.GameText2),
|
||||
new GameFileReference(033, GameFile.GameText3),
|
||||
new GameFileReference(034, GameFile.GameText4),
|
||||
new GameFileReference(035, GameFile.GameText5),
|
||||
new GameFileReference(036, GameFile.GameText6),
|
||||
new GameFileReference(037, GameFile.GameText7),
|
||||
new GameFileReference(038, GameFile.GameText8),
|
||||
new GameFileReference(039, GameFile.GameText9),
|
||||
new GameFileReference(040, GameFile.StoryText0),
|
||||
new GameFileReference(041, GameFile.StoryText1),
|
||||
new GameFileReference(042, GameFile.StoryText2),
|
||||
new GameFileReference(043, GameFile.StoryText3),
|
||||
new GameFileReference(044, GameFile.StoryText4),
|
||||
new GameFileReference(045, GameFile.StoryText5),
|
||||
new GameFileReference(046, GameFile.StoryText6),
|
||||
new GameFileReference(047, GameFile.StoryText7),
|
||||
new GameFileReference(048, GameFile.StoryText8),
|
||||
new GameFileReference(049, GameFile.StoryText9),
|
||||
|
||||
new GameFileReference(077, GameFile.ZoneData),
|
||||
new GameFileReference(091, GameFile.WorldData),
|
||||
|
||||
new GameFileReference(105, GameFile.TrainerClass),
|
||||
new GameFileReference(106, GameFile.TrainerData),
|
||||
new GameFileReference(107, GameFile.TrainerPoke),
|
||||
|
||||
new GameFileReference(159, GameFile.EncounterStatic),
|
||||
|
||||
new GameFileReference(271, GameFile.Pickup),
|
||||
|
||||
new GameFileReference(281, GameFile.FacilityPokeNormal),
|
||||
new GameFileReference(282, GameFile.FacilityTrainerNormal),
|
||||
new GameFileReference(283, GameFile.FacilityPokeSuper),
|
||||
new GameFileReference(284, GameFile.FacilityTrainerSuper),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Let's Go Pikachu & Let's Go Eevee
|
||||
/// </summary>
|
||||
private static readonly GameFileReference[] GG =
|
||||
{
|
||||
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region Split Versions
|
||||
private static readonly GameFileReference[] SN = SM.Concat(new[] {new GameFileReference(082, GameFile.Encounters)}).ToArray();
|
||||
private static readonly GameFileReference[] MN = SM.Concat(new[] {new GameFileReference(083, GameFile.Encounters)}).ToArray();
|
||||
private static readonly GameFileReference[] US = UU.Concat(new[] {new GameFileReference(082, GameFile.Encounters)}).ToArray();
|
||||
private static readonly GameFileReference[] UM = UU.Concat(new[] {new GameFileReference(083, GameFile.Encounters)}).ToArray();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
64
pkNX.Game/File/GameFileReference.cs
Normal file
64
pkNX.Game/File/GameFileReference.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
using System.IO;
|
||||
using pkNX.Containers;
|
||||
|
||||
namespace pkNX.Game
|
||||
{
|
||||
/// <summary>
|
||||
/// File reference pointing to the location of the <see cref="GameFile"/> data.
|
||||
/// </summary>
|
||||
public class GameFileReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of data the file contains.
|
||||
/// </summary>
|
||||
public GameFile File { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Location of the file in the user's environment.
|
||||
/// </summary>
|
||||
public string RelativePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of container the data is within.
|
||||
/// </summary>
|
||||
public ContainerType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Toggle to indicate that the <see cref="File"/> data is localized and should be shifted.
|
||||
/// </summary>
|
||||
public bool LanguageVariant { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the parent of the data.
|
||||
/// </summary>
|
||||
public ContainerParent Parent { get; set; }
|
||||
|
||||
internal GameFileReference(int FileNumber, GameFile ident, ContainerType t = ContainerType.GARC)
|
||||
{
|
||||
File = ident;
|
||||
Type = t;
|
||||
|
||||
int A = FileNumber / 100 % 10;
|
||||
int B = FileNumber / 10 % 10;
|
||||
int C = FileNumber / 1 % 10;
|
||||
RelativePath = Path.Combine("a", A.ToString(), B.ToString(), C.ToString());
|
||||
}
|
||||
|
||||
internal GameFileReference(string relPath, ContainerType t, GameFile ident, bool variant = false)
|
||||
{
|
||||
File = ident;
|
||||
Type = t;
|
||||
LanguageVariant = variant;
|
||||
|
||||
RelativePath = relPath;
|
||||
}
|
||||
|
||||
public IFileContainer Get(string basePath)
|
||||
{
|
||||
var path = Path.Combine(basePath, RelativePath);
|
||||
var container = Container.GetContainer(path, Type);
|
||||
container.FilePath = path;
|
||||
return container;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
pkNX.Game/GameData.cs
Normal file
18
pkNX.Game/GameData.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Cached data reusable by multiple editors.
|
||||
/// </summary>
|
||||
public class GameData
|
||||
{
|
||||
public PersonalTable PersonalData { get; internal set; }
|
||||
public MegaEvolutionTable MegaEvolutionData { get; internal set; }
|
||||
|
||||
public string[][] GameText { get; internal set; }
|
||||
public Move[] MoveData { get; internal set; }
|
||||
public EvolutionSet[] EvolutionData { get; internal set; }
|
||||
public Learnset[] LevelUpData { get; internal set; }
|
||||
}
|
||||
}
|
||||
58
pkNX.Game/GameLanguage.cs
Normal file
58
pkNX.Game/GameLanguage.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
namespace pkNX.Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Game Language IDs to index consecutive localized files
|
||||
/// </summary>
|
||||
public enum GameLanguage
|
||||
{
|
||||
/// <summary>
|
||||
/// Japanese (katakana)
|
||||
/// </summary>
|
||||
カタカナ = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Japanese (hiragana)
|
||||
/// </summary>
|
||||
漢字 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// English
|
||||
/// </summary>
|
||||
English = 2,
|
||||
|
||||
/// <summary>
|
||||
/// French
|
||||
/// </summary>
|
||||
Français = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Italian
|
||||
/// </summary>
|
||||
Italiano = 4,
|
||||
|
||||
/// <summary>
|
||||
/// German
|
||||
/// </summary>
|
||||
Deutsch = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Spanish
|
||||
/// </summary>
|
||||
Español = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Korean
|
||||
/// </summary>
|
||||
한국 = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Chinese (Simplified)
|
||||
/// </summary>
|
||||
汉字简化方案 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Chinese (Traditional)
|
||||
/// </summary>
|
||||
漢字簡化方案 = 9,
|
||||
}
|
||||
}
|
||||
96
pkNX.Game/GameLocation.cs
Normal file
96
pkNX.Game/GameLocation.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Environment Data where the Game's Data is located on the user's machine.
|
||||
/// </summary>
|
||||
public sealed class GameLocation
|
||||
{
|
||||
/// <summary>
|
||||
/// Location of the RomFS
|
||||
/// </summary>
|
||||
public string RomFS { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Location of the ExeFS.
|
||||
/// </summary>
|
||||
public string ExeFS { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Game version the files belong to.
|
||||
/// </summary>
|
||||
public GameVersion Game { get; }
|
||||
|
||||
private GameLocation(string romfs, string exefs, GameVersion game)
|
||||
{
|
||||
Game = game;
|
||||
RomFS = romfs;
|
||||
ExeFS = exefs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the <see cref="GameVersion"/> of the input directory and detects the location of files for editing.
|
||||
/// </summary>
|
||||
/// <param name="dir">Directory the game data is in</param>
|
||||
/// <returns>New <see cref="GameLocation"/> object with references to file paths.</returns>
|
||||
public static GameLocation GetGame(string dir)
|
||||
{
|
||||
if (dir == null || !Directory.Exists(dir))
|
||||
return null;
|
||||
|
||||
var dirs = Directory.GetDirectories(dir);
|
||||
var romfs = Array.Find(dirs, z => Path.GetFileName(z).StartsWith("rom"));
|
||||
var exefs = Array.Find(dirs, z => Path.GetFileName(z).StartsWith("exe"));
|
||||
|
||||
if (romfs == null || exefs == null)
|
||||
return null;
|
||||
|
||||
var files = Directory.GetFiles(romfs, "*", SearchOption.AllDirectories);
|
||||
var game = GetGameFromCount(files.Length, romfs);
|
||||
if (game == GameVersion.Invalid)
|
||||
return null;
|
||||
return new GameLocation(romfs, exefs, game);
|
||||
}
|
||||
|
||||
private const int FILECOUNT_XY = 271;
|
||||
private const int FILECOUNT_ORASDEMO = 301;
|
||||
private const int FILECOUNT_ORAS = 299;
|
||||
private const int FILECOUNT_SMDEMO = 239;
|
||||
private const int FILECOUNT_SM = 311;
|
||||
private const int FILECOUNT_USUM = 333;
|
||||
private const int FILECOUNT_GG = 27818;
|
||||
|
||||
private static GameVersion GetGameFromCount(int fileCount, string romfs)
|
||||
{
|
||||
switch (fileCount)
|
||||
{
|
||||
case FILECOUNT_XY: return GameVersion.XY;
|
||||
case FILECOUNT_ORASDEMO: return GameVersion.ORASDEMO;
|
||||
case FILECOUNT_ORAS: return GameVersion.ORAS;
|
||||
case FILECOUNT_SMDEMO: return GameVersion.SMDEMO;
|
||||
case FILECOUNT_SM:
|
||||
{
|
||||
var encdata = Path.Combine(romfs, "a", "0", "8", "2");
|
||||
if (File.Exists(encdata) && new FileInfo(encdata).Length != 0)
|
||||
return GameVersion.SN;
|
||||
return GameVersion.MN;
|
||||
}
|
||||
case FILECOUNT_USUM:
|
||||
{
|
||||
var encdata = Path.Combine(romfs, "a", "0", "8", "2");
|
||||
if (File.Exists(encdata) && new FileInfo(encdata).Length != 0)
|
||||
return GameVersion.US;
|
||||
return GameVersion.UM;
|
||||
}
|
||||
case FILECOUNT_GG:
|
||||
return GameVersion.GG;
|
||||
|
||||
default:
|
||||
return GameVersion.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
pkNX.Game/GameManager.cs
Normal file
91
pkNX.Game/GameManager.cs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
using pkNX.Containers;
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages fetching of game data.
|
||||
/// </summary>
|
||||
public sealed class GameManager
|
||||
{
|
||||
private readonly GameLocation ROM;
|
||||
private readonly TextManager Text; // GameText
|
||||
private readonly GameFileMapping FileMap;
|
||||
|
||||
/// <summary>
|
||||
/// Language to use when fetching string & graphic assets.
|
||||
/// </summary>
|
||||
public int Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current <see cref="GameVersion"/> the data represents.
|
||||
/// </summary>
|
||||
public GameVersion Game => ROM.Game;
|
||||
|
||||
/// <summary>
|
||||
/// Generally useful game data that can be used by multiple editors.
|
||||
/// </summary>
|
||||
public GameData Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="GameManager"/> for the input <see cref="GameLocation"/> with initial <see cref="Language"/>.
|
||||
/// </summary>
|
||||
/// <param name="rom"></param>
|
||||
/// <param name="language"></param>
|
||||
public GameManager(GameLocation rom, int language)
|
||||
{
|
||||
ROM = rom;
|
||||
Language = language;
|
||||
FileMap = new GameFileMapping(rom);
|
||||
Text = new TextManager(Game);
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches a <see cref="GameFile"/> from the Game data.
|
||||
/// </summary>
|
||||
/// <param name="file">File type to fetch</param>
|
||||
/// <returns>Container that contains the game data requested.</returns>
|
||||
/// <remarks>Sugar for the other <see cref="GetFile"/> method.</remarks>
|
||||
public IFileContainer this[GameFile file] => GetFile(file);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches a <see cref="GameFile"/> from the Game data.
|
||||
/// </summary>
|
||||
/// <param name="file">File type to fetch</param>
|
||||
/// <returns>Container that contains the game data requested.</returns>
|
||||
public IFileContainer GetFile(GameFile file) => FileMap.GetFile(file, Language);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches strings for the input <see cref="TextName"/>.
|
||||
/// </summary>
|
||||
/// <param name="text">Text file to fetch</param>
|
||||
/// <returns>Array of strings from the requested text file.</returns>
|
||||
public string[] GetStrings(TextName text)
|
||||
{
|
||||
var arc = this[GameFile.GameText];
|
||||
var lines = Text.GetStrings(text, arc);
|
||||
return lines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves all open files and finalizes the ROM data.
|
||||
/// </summary>
|
||||
/// <param name="closing">Skip re-initialization of game data.</param>
|
||||
public void SaveAll(bool closing)
|
||||
{
|
||||
FileMap.SaveAll();
|
||||
if (!closing)
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
Data = new GameData
|
||||
{
|
||||
MoveData = this[GameFile.MoveStats].GetFiles().GetArray(z => (Move)new Move6(z)),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
43
pkNX.Game/Text/TextManager.cs
Normal file
43
pkNX.Game/Text/TextManager.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using pkNX.Containers;
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Game
|
||||
{
|
||||
public class TextManager
|
||||
{
|
||||
private readonly TextConfig Config;
|
||||
private readonly IReadOnlyCollection<TextReference> References;
|
||||
|
||||
private readonly Dictionary<TextName, string[]> Cache = new Dictionary<TextName, string[]>();
|
||||
|
||||
public TextManager(GameVersion game, TextConfig config = null)
|
||||
{
|
||||
References = TextMapping.GetMapping(game);
|
||||
Config = config ?? new TextConfig(game);
|
||||
}
|
||||
|
||||
internal string[] GetStrings(byte[] data, bool remap = false)
|
||||
{
|
||||
var txt = new TextFile(data, Config, remap);
|
||||
return txt.Lines;
|
||||
}
|
||||
|
||||
internal string[] GetStrings(TextName file, IFileContainer textFile, bool remap = false)
|
||||
{
|
||||
if (Cache.TryGetValue(file, out var container))
|
||||
return container;
|
||||
|
||||
var info = References.FirstOrDefault(f => f.Name == file);
|
||||
if (info == null)
|
||||
throw new ArgumentException($"Unknown {nameof(TextName)} provided.", file.ToString());
|
||||
|
||||
var data = textFile[info.Index];
|
||||
var lines = GetStrings(data, remap);
|
||||
Cache.Add(file, lines);
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
pkNX.Game/Text/TextMapping.cs
Normal file
131
pkNX.Game/Text/TextMapping.cs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
using System.Collections.Generic;
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Game
|
||||
{
|
||||
public static class TextMapping
|
||||
{
|
||||
public static IReadOnlyCollection<TextReference> GetMapping(GameVersion game)
|
||||
{
|
||||
switch (game)
|
||||
{
|
||||
case GameVersion.XY: return XY;
|
||||
case GameVersion.ORASDEMO:
|
||||
case GameVersion.ORAS: return AO;
|
||||
case GameVersion.SMDEMO: return SMDEMO;
|
||||
case GameVersion.SN:
|
||||
case GameVersion.MN: return SM;
|
||||
case GameVersion.US:
|
||||
case GameVersion.UM: return USUM;
|
||||
case GameVersion.GG: return GG;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static readonly TextReference[] XY =
|
||||
{
|
||||
new TextReference(005, TextName.Forms),
|
||||
new TextReference(013, TextName.MoveNames),
|
||||
new TextReference(015, TextName.MoveFlavor),
|
||||
new TextReference(017, TextName.Types),
|
||||
new TextReference(020, TextName.TrainerClasses),
|
||||
new TextReference(021, TextName.TrainerNames),
|
||||
new TextReference(022, TextName.TrainerText),
|
||||
new TextReference(034, TextName.AbilityNames),
|
||||
new TextReference(047, TextName.Natures),
|
||||
new TextReference(072, TextName.metlist_000000),
|
||||
new TextReference(080, TextName.SpeciesNames),
|
||||
new TextReference(096, TextName.ItemNames),
|
||||
new TextReference(099, TextName.ItemFlavor),
|
||||
new TextReference(130, TextName.MaisonTrainerNames),
|
||||
new TextReference(131, TextName.SuperTrainerNames),
|
||||
new TextReference(141, TextName.OPowerFlavor),
|
||||
};
|
||||
|
||||
private static readonly TextReference[] AO =
|
||||
{
|
||||
new TextReference(005, TextName.Forms),
|
||||
new TextReference(014, TextName.MoveNames),
|
||||
new TextReference(016, TextName.MoveFlavor),
|
||||
new TextReference(018, TextName.Types),
|
||||
new TextReference(021, TextName.TrainerClasses),
|
||||
new TextReference(022, TextName.TrainerNames),
|
||||
new TextReference(023, TextName.TrainerText),
|
||||
new TextReference(037, TextName.AbilityNames),
|
||||
new TextReference(051, TextName.Natures),
|
||||
new TextReference(090, TextName.metlist_000000),
|
||||
new TextReference(098, TextName.SpeciesNames),
|
||||
new TextReference(114, TextName.ItemNames),
|
||||
new TextReference(117, TextName.ItemFlavor),
|
||||
new TextReference(153, TextName.MaisonTrainerNames),
|
||||
new TextReference(154, TextName.SuperTrainerNames),
|
||||
new TextReference(165, TextName.OPowerFlavor),
|
||||
};
|
||||
|
||||
private static readonly TextReference[] SMDEMO =
|
||||
{
|
||||
new TextReference(020, TextName.ItemFlavor),
|
||||
new TextReference(021, TextName.ItemNames),
|
||||
new TextReference(026, TextName.SpeciesNames),
|
||||
new TextReference(030, TextName.metlist_000000),
|
||||
new TextReference(044, TextName.Forms),
|
||||
new TextReference(044, TextName.Natures),
|
||||
new TextReference(046, TextName.AbilityNames),
|
||||
new TextReference(049, TextName.TrainerText),
|
||||
new TextReference(050, TextName.TrainerNames),
|
||||
new TextReference(051, TextName.TrainerClasses),
|
||||
new TextReference(052, TextName.Types),
|
||||
new TextReference(054, TextName.MoveFlavor),
|
||||
new TextReference(055, TextName.MoveNames),
|
||||
};
|
||||
|
||||
private static readonly TextReference[] SM =
|
||||
{
|
||||
new TextReference(035, TextName.ItemFlavor),
|
||||
new TextReference(036, TextName.ItemNames),
|
||||
new TextReference(055, TextName.SpeciesNames),
|
||||
new TextReference(067, TextName.metlist_000000),
|
||||
new TextReference(086, TextName.BattleRoyalNames),
|
||||
new TextReference(087, TextName.Natures),
|
||||
new TextReference(096, TextName.AbilityNames),
|
||||
new TextReference(099, TextName.BattleTreeNames),
|
||||
new TextReference(104, TextName.TrainerText),
|
||||
new TextReference(105, TextName.TrainerNames),
|
||||
new TextReference(106, TextName.TrainerClasses),
|
||||
new TextReference(107, TextName.Types),
|
||||
new TextReference(112, TextName.MoveFlavor),
|
||||
new TextReference(113, TextName.MoveNames),
|
||||
new TextReference(114, TextName.Forms),
|
||||
new TextReference(116, TextName.SpeciesClassifications),
|
||||
new TextReference(119, TextName.PokedexEntry1),
|
||||
new TextReference(120, TextName.PokedexEntry2)
|
||||
};
|
||||
|
||||
private static readonly TextReference[] USUM =
|
||||
{
|
||||
new TextReference(039, TextName.ItemFlavor),
|
||||
new TextReference(040, TextName.ItemNames),
|
||||
new TextReference(060, TextName.SpeciesNames),
|
||||
new TextReference(072, TextName.metlist_000000),
|
||||
new TextReference(091, TextName.BattleRoyalNames),
|
||||
new TextReference(092, TextName.Natures),
|
||||
new TextReference(101, TextName.AbilityNames),
|
||||
new TextReference(104, TextName.BattleTreeNames),
|
||||
new TextReference(109, TextName.TrainerText),
|
||||
new TextReference(110, TextName.TrainerNames),
|
||||
new TextReference(111, TextName.TrainerClasses),
|
||||
new TextReference(112, TextName.Types),
|
||||
new TextReference(117, TextName.MoveFlavor),
|
||||
new TextReference(118, TextName.MoveNames),
|
||||
new TextReference(119, TextName.Forms),
|
||||
new TextReference(121, TextName.SpeciesClassifications),
|
||||
new TextReference(124, TextName.PokedexEntry1),
|
||||
new TextReference(125, TextName.PokedexEntry2)
|
||||
};
|
||||
|
||||
private static readonly TextReference[] GG =
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
31
pkNX.Game/Text/TextName.cs
Normal file
31
pkNX.Game/Text/TextName.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
namespace pkNX.Game
|
||||
{
|
||||
public enum TextName
|
||||
{
|
||||
AbilityNames,
|
||||
MoveNames,
|
||||
MoveFlavor,
|
||||
|
||||
ItemNames,
|
||||
ItemFlavor,
|
||||
|
||||
SpeciesNames,
|
||||
Types,
|
||||
Natures,
|
||||
Forms,
|
||||
|
||||
TrainerNames,
|
||||
TrainerClasses,
|
||||
TrainerText,
|
||||
metlist_000000,
|
||||
OPowerFlavor,
|
||||
MaisonTrainerNames,
|
||||
SuperTrainerNames,
|
||||
BattleRoyalNames,
|
||||
BattleTreeNames,
|
||||
|
||||
SpeciesClassifications,
|
||||
PokedexEntry1,
|
||||
PokedexEntry2
|
||||
}
|
||||
}
|
||||
15
pkNX.Game/Text/TextReference.cs
Normal file
15
pkNX.Game/Text/TextReference.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
namespace pkNX.Game
|
||||
{
|
||||
public class TextReference
|
||||
{
|
||||
public readonly int Index;
|
||||
public readonly TextName Name;
|
||||
|
||||
internal TextReference(int index, TextName name)
|
||||
{
|
||||
Index = index;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
pkNX.Game/pkNX.Game.csproj
Normal file
14
pkNX.Game/pkNX.Game.csproj
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
|
||||
<Description>Game Data Manager</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\pkNX.Containers\pkNX.Containers.csproj" />
|
||||
<ProjectReference Include="..\pkNX.Randomization\pkNX.Randomization.csproj" />
|
||||
<ProjectReference Include="..\pkNX.Structures\pkNX.Structures.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
97
pkNX.Randomization/Randomizers/EvolutionRandomizer.cs
Normal file
97
pkNX.Randomization/Randomizers/EvolutionRandomizer.cs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EvolutionSet"/> randomizer.
|
||||
/// </summary>
|
||||
public class EvolutionRandomizer : Randomizer
|
||||
{
|
||||
private readonly EvolutionSet[] Evolutions;
|
||||
private readonly GameInfo Game;
|
||||
public readonly SpeciesRandomizer Randomizer;
|
||||
|
||||
public EvolutionRandomizer(GameInfo game, EvolutionSet[] evolutions, PersonalTable t)
|
||||
{
|
||||
Game = game;
|
||||
Evolutions = evolutions;
|
||||
Randomizer = new SpeciesRandomizer(Game, t);
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
for (var i = 0; i < Evolutions.Length; i++)
|
||||
{
|
||||
var evo = Evolutions[i];
|
||||
Randomize(evo, i);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteTrade()
|
||||
{
|
||||
for (var i = 0; i < Evolutions.Length; i++)
|
||||
{
|
||||
var evo = Evolutions[i];
|
||||
ReplaceTradeMethods(evo, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void Randomize(EvolutionSet evos, int species)
|
||||
{
|
||||
foreach (var evo in evos.PossibleEvolutions)
|
||||
{
|
||||
if (evo.Method != 0)
|
||||
{
|
||||
evo.Species = Randomizer.GetRandomSpecies(evo.Species, species);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceTradeMethods(EvolutionSet evos, int species)
|
||||
{
|
||||
for (var i = 0; i < evos.PossibleEvolutions.Length; i++)
|
||||
{
|
||||
var evo = evos.PossibleEvolutions[i];
|
||||
ReplaceTradeMethod(evo, species, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceTradeMethod(EvolutionMethod evo, int species, int evoIndex)
|
||||
{
|
||||
switch (evo.Method)
|
||||
{
|
||||
case EvolutionType.Trade when Game.Generation == 6:
|
||||
evo.Method = EvolutionType.LevelUp; // trade -> level up
|
||||
evo.Argument = 30;
|
||||
return;
|
||||
case EvolutionType.Trade when Game.Generation == 7:
|
||||
evo.Method = EvolutionType.LevelUp; // trade -> level up
|
||||
evo.Level = 30;
|
||||
return;
|
||||
case EvolutionType.TradeHeldItem:
|
||||
evo.Method = EvolutionType.LevelUpHeldItemDay;
|
||||
return;
|
||||
case EvolutionType.TradeSpecies:
|
||||
evo.Method = EvolutionType.LevelUpWithTeammate;
|
||||
if (species == 588)
|
||||
evo.Argument = 616; // Karrablast with Shelmet
|
||||
if (species == 616)
|
||||
evo.Argument = 588; // Shelmet with Karrablast
|
||||
return;
|
||||
|
||||
case EvolutionType.LevelUpVersion:
|
||||
evo.Method = evoIndex == 0 ? EvolutionType.LevelUpECl5 : EvolutionType.LevelUpECgeq5;
|
||||
evo.Argument = 0; // clear ver
|
||||
return;
|
||||
case EvolutionType.LevelUpVersionDay:
|
||||
evo.Method = EvolutionType.LevelUpFriendshipMorning;
|
||||
evo.Argument = 0; // clear ver
|
||||
return;
|
||||
case EvolutionType.LevelUpVersionNight:
|
||||
evo.Method = EvolutionType.LevelUpFriendshipNight;
|
||||
evo.Argument = 0; // clear ver
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
pkNX.Randomization/Randomizers/FormRandomizer.cs
Normal file
47
pkNX.Randomization/Randomizers/FormRandomizer.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
using pkNX.Structures;
|
||||
using System.Linq;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
public class FormRandomizer
|
||||
{
|
||||
private readonly PersonalTable Table;
|
||||
|
||||
public FormRandomizer(PersonalTable t)
|
||||
{
|
||||
Table = t;
|
||||
}
|
||||
|
||||
public bool AllowMega { get; set; } = false;
|
||||
public bool AllowAlolanForm { get; set; } = true;
|
||||
|
||||
public int GetRandomForme(int species, PersonalInfo[] stats = null)
|
||||
{
|
||||
if (stats == null)
|
||||
stats = Table.Table;
|
||||
if (stats[species].FormeCount <= 1)
|
||||
return 0;
|
||||
|
||||
switch (species)
|
||||
{
|
||||
case 201: // Unown
|
||||
case 586: // Sawsbuck
|
||||
return 31; // Random
|
||||
case 658 when !AllowMega:
|
||||
return 0;
|
||||
case 664:
|
||||
case 665:
|
||||
case 666: // Vivillon evo chain
|
||||
return 30; // save file specific
|
||||
case 774: // Minior
|
||||
return Util.Random.Next(7);
|
||||
}
|
||||
|
||||
if (AllowAlolanForm && Legal.EvolveToAlolanForms.Contains(species))
|
||||
return Util.Random.Next(2);
|
||||
if (!Legal.BattleExclusiveForms.Contains(species) || AllowMega)
|
||||
return Util.Random.Next(stats[species].FormeCount); // Slot-Random
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
pkNX.Randomization/Randomizers/GenericRandomizer.cs
Normal file
34
pkNX.Randomization/Randomizers/GenericRandomizer.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
namespace pkNX.Randomization
|
||||
{
|
||||
/// <summary> Cyclical Shuffled Randomizer </summary>
|
||||
/// <remarks>
|
||||
/// The shuffled list is iterated over, and reshuffled when exhausted.
|
||||
/// The list does not repeat values until the list is exhausted.
|
||||
/// </remarks>
|
||||
public class GenericRandomizer
|
||||
{
|
||||
public GenericRandomizer(int[] randomValues)
|
||||
{
|
||||
RandomValues = randomValues;
|
||||
}
|
||||
|
||||
private readonly int[] RandomValues;
|
||||
private int ctr;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ctr = 0;
|
||||
Util.Shuffle(RandomValues);
|
||||
}
|
||||
|
||||
public int Next()
|
||||
{
|
||||
if (ctr == 0)
|
||||
Util.Shuffle(RandomValues);
|
||||
|
||||
int value = RandomValues[ctr++];
|
||||
ctr %= RandomValues.Length;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
pkNX.Randomization/Randomizers/Personal/Consistency.cs
Normal file
29
pkNX.Randomization/Randomizers/Personal/Consistency.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
namespace pkNX.Randomization
|
||||
{
|
||||
public enum RandSetting
|
||||
{
|
||||
Unchanged,
|
||||
Randomized,
|
||||
}
|
||||
|
||||
public enum Permissive
|
||||
{
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
public enum ModifyState
|
||||
{
|
||||
Shared,
|
||||
One,
|
||||
Two,
|
||||
All,
|
||||
}
|
||||
|
||||
public enum CatchRate
|
||||
{
|
||||
Unchanged,
|
||||
BSTScaled,
|
||||
Random,
|
||||
}
|
||||
}
|
||||
206
pkNX.Randomization/Randomizers/Personal/PersonalRandSettings.cs
Normal file
206
pkNX.Randomization/Randomizers/Personal/PersonalRandSettings.cs
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
using System.ComponentModel;
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
/// <summary>
|
||||
/// Randomization settings when randomizing a <see cref="PersonalInfo"/>.
|
||||
/// </summary>
|
||||
public class PersonalRandSettings : RandSettings
|
||||
{
|
||||
private const string Moves = nameof(Moves);
|
||||
private const string Types = nameof(Types);
|
||||
private const string Stats = nameof(Stats);
|
||||
private const string Abilities = nameof(Abilities);
|
||||
private const string Misc = nameof(Misc);
|
||||
private const string Evolutions = nameof(Evolutions);
|
||||
|
||||
/// <summary>Toggle to use Evolution data for inherited <see cref="PersonalInfo"/> properties.</summary>
|
||||
[Category("A1"), Description("Evolution Chain Species Randomization instead of Pure Random. Refer to the Evolutions settings when using this mode.")]
|
||||
public bool ModifyByEvolutions { get; set; } = true;
|
||||
|
||||
#region Moves
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.TMHM"/> values.</summary>
|
||||
[Category(Moves), Description("Enables randomizing the TM (Technical Machine) compatibility flags.")]
|
||||
public bool ModifyLearnsetTM { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.TMHM"/> values.</summary>
|
||||
[Category(Moves), Description("Enables randomizing the HM (Hidden Machine) compatibility flags.")]
|
||||
public bool ModifyLearnsetHM { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.TMHM"/> values.</summary>
|
||||
[Category(Moves), Description("Enables randomizing the move tutor compatibility flags.")]
|
||||
public bool ModifyLearnsetMoveTutors { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.TMHM"/> values.</summary>
|
||||
[Category(Moves), Description("Enables randomizing the type tutor compatibility flags.")]
|
||||
public bool ModifyLearnsetTypeTutors { get; set; } = false;
|
||||
|
||||
/// <summary>Percent chance to learn a TMHM move (0-100).</summary>
|
||||
/// <remarks>Average Learnable TMs is 35.260.</remarks>
|
||||
[Category(Moves), Description("Percentage chance to learn a given TM move.")]
|
||||
public decimal LearnTMPercent { get; set; } = 35;
|
||||
|
||||
/// <summary>Percent chance to learn a type tutor move (0-100).</summary>
|
||||
/// <remarks>136 special tutor moves learnable by species in Untouched ORAS.</remarks>
|
||||
[Category(Moves), Description("Percentage chance to learn a given special Type Tutor move.")]
|
||||
public decimal LearnTypeTutorPercent { get; set; } = 2;
|
||||
|
||||
/// <summary>Percent chance to learn a tutor move (0-100).</summary>
|
||||
/// <remarks>10001 tutor moves learnable by 826 species in Untouched ORAS.</remarks>
|
||||
[Category(Moves), Description("Percentage chance to learn a given Move Tutor move.")]
|
||||
public decimal LearnMoveTutorPercent { get; set; } = 30;
|
||||
#endregion
|
||||
|
||||
#region Types
|
||||
/// <summary>Permits modification of <see cref="PersonalInfo.Types"/>.</summary>
|
||||
[Category(Types), Description("Enables a PKM's Type to be modified.")]
|
||||
public bool ModifyTypes { get; set; } = true;
|
||||
|
||||
/// <summary>Option to modify the <see cref="PersonalInfo.Types"/>.</summary>
|
||||
[Category(Types), Description("Option to modify the Types depending on the specified setting.")]
|
||||
public ModifyState Type { get; set; } = ModifyState.All;
|
||||
|
||||
/// <summary>Chance that both types are the same.</summary>
|
||||
[Category(Types), Description("Chance that both types are the same.")]
|
||||
public decimal SameTypeChance { get; set; } = 50;
|
||||
#endregion
|
||||
|
||||
#region Ability
|
||||
/// <summary>Toggle to permit modification of <see cref="PersonalInfo.Abilities"/>.</summary>
|
||||
[Category(Abilities), Description("Enables a PKM's Abilities to be modified.")]
|
||||
public bool ModifyAbility { get; set; } = true;
|
||||
|
||||
/// <summary>Permits Wonder Guard as a random ability.</summary>
|
||||
[Category(Abilities), Description("Permits Wonder Guard as a random ability.")]
|
||||
public Permissive WonderGuard { get; set; } = Permissive.No;
|
||||
|
||||
/// <summary>Option to modify the <see cref="PersonalInfo.Abilities"/>.</summary>
|
||||
[Category(Abilities), Description("Option to modify the Abilities depending on the specified setting.")]
|
||||
public ModifyState Ability { get; set; } = ModifyState.All;
|
||||
|
||||
/// <summary>Chance that both abilities are the same.</summary>
|
||||
[Category(Abilities), Description("Chance that both abilities are the same.")]
|
||||
public decimal SameAbilityChance { get; set; } = 100;
|
||||
#endregion
|
||||
|
||||
#region Stats
|
||||
/// <summary>Permits modification of <see cref="PersonalInfo.Stats"/>.</summary>
|
||||
[Category(Stats), Description("Enables a PKM's base stats to be modified.")]
|
||||
public bool ModifyStats { get; set; } = true;
|
||||
|
||||
/// <summary>Amount a Base Stat is amplified as a low bound.</summary>
|
||||
[Category(Stats), Description("Minimum Percentage bound a Base Stat is after randomizing. 100 corresponds to an unchanged minimum.")]
|
||||
public int StatDeviationMin { get; set; } = 75;
|
||||
|
||||
/// <summary>Amount a Base Stat is amplified as a high bound.</summary>
|
||||
[Category(Stats), Description("Maximum Percentage bound a Base Stat is after randomizing. 100 corresponds to an unchanged maximum.")]
|
||||
public int StatDeviationMax { get; set; } = 125;
|
||||
|
||||
/// <summary>Toggle to permit shuffling of <see cref="PersonalInfo.Stats"/>.</summary>
|
||||
[Category(Stats), Description("Shuffles the PKM's base stats after any modifications have been made.")]
|
||||
public bool ShuffleStats { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.HP"/> stat.</summary>
|
||||
[Category(Stats), Description("Permits randomizing the HP base stat.")]
|
||||
public bool HP { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.ATK"/> stat.</summary>
|
||||
[Category(Stats), Description("Permits randomizing the Attack base stat.")]
|
||||
public bool ATK { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.DEF"/> stat.</summary>
|
||||
[Category(Stats), Description("Permits randomizing the Defense base stat.")]
|
||||
public bool DEF { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.SPA"/> stat.</summary>
|
||||
[Category(Stats), Description("Permits randomizing the Special Attack base stat.")]
|
||||
public bool SPA { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.SPD"/> stat.</summary>
|
||||
[Category(Stats), Description("Permits randomizing the Special Defense base stat.")]
|
||||
public bool SPD { get; set; } = true;
|
||||
|
||||
/// <summary>Permits randomizing the <see cref="PersonalInfo.SPE"/> stat.</summary>
|
||||
[Category(Stats), Description("Permits randomizing the Speed base stat.")]
|
||||
public bool SPE { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Flags to edit the stats when randomizing.
|
||||
/// </summary>
|
||||
public bool[] StatsToRandomize => new[] {HP, ATK, DEF, SPE, SPA, SPD};
|
||||
#endregion
|
||||
|
||||
#region Misc
|
||||
/// <summary>Option permitting modification of <see cref="PersonalInfo.CatchRate"/>.</summary>
|
||||
[Category(Misc), Description("Enables a PKM's catch rate to be modified. Can inversely scale off BST.")]
|
||||
public CatchRate CatchRate { get; set; } = CatchRate.Unchanged;
|
||||
|
||||
/// <summary>Permits modification of <see cref="PersonalInfo.HeldItems"/>.</summary>
|
||||
[Category(Misc), Description("Enables a PKM's base stats to be modified.")]
|
||||
public bool ModifyHeldItems { get; set; } = true;
|
||||
|
||||
/// <summary>Chance all held items are the same.</summary>
|
||||
[Category(Misc), Description("Percentage chance that all Held Items are the same, resulting in a 100% chance of having the held item.")]
|
||||
public decimal AlwaysHeldItemChance { get; set; } = 20;
|
||||
|
||||
/// <summary>Permits modification of <see cref="PersonalInfo.EggGroups"/>.</summary>
|
||||
[Category(Misc), Description("Enables a PKM's egg groups to be modified.")]
|
||||
public bool ModifyEgg { get; set; } = false;
|
||||
|
||||
/// <summary>Chance both egg groups are the same.</summary>
|
||||
[Category(Misc), Description("Percentage chance that both egg groups will be the same.")]
|
||||
public decimal SameEggGroupChance { get; set; } = 50;
|
||||
#endregion
|
||||
|
||||
#region Evolutions
|
||||
/// <summary>Toggles inheriting types from the pre-evolution that evolves into this species/form.</summary>
|
||||
[Category(Evolutions), Description("Toggles inheriting types from the pre-evolution that evolves into this species/form.")]
|
||||
public bool InheritType { get; set; } = true;
|
||||
|
||||
/// <summary>Maximum amount of Types that can be different from the pre-evolution.</summary>
|
||||
[Category(Evolutions), Description("Maximum amount of Types that can be different from the pre-evolutionn.")]
|
||||
public ModifyState InheritTypeSetting { get; set; } = ModifyState.One;
|
||||
|
||||
/// <summary>Percentage chance that only one type will be inherited, and a new random one will replace the other.</summary>
|
||||
[Category(Evolutions), Description("Percentage chance that only one type will be inherited, and a new random one will replace the other.")]
|
||||
public decimal InheritTypeOnlyOneChance { get; set; } = 65;
|
||||
|
||||
/// <summary>Percentage chance that neither one type will be inherited, and new random ones will replace the others.</summary>
|
||||
[Category(Evolutions), Description("Percentage chance that neither type will be inherited, and new random ones will replace the others.")]
|
||||
public decimal InheritTypeNeitherChance { get; set; } = 30;
|
||||
|
||||
/// <summary>Toggles chance that neither one type will be inherited, and new random ones will replace the others.</summary>
|
||||
[Category(Evolutions), Description("Percentage chance that neither type will be inherited, and new random ones will replace the others.")]
|
||||
public ModifyState InheritAbilitySetting { get; set; } = ModifyState.One;
|
||||
|
||||
/// <summary>Toggles inheriting abilities from the pre-evolution that evolves into this species/form.</summary>
|
||||
[Category(Evolutions), Description("Toggles inheriting abilities from the pre-evolution that evolves into this species/form.")]
|
||||
public bool InheritAbility { get; set; } = true;
|
||||
|
||||
/// <summary>Percentage chance that only one ability will be inherited, and a new random one will replace the other.</summary>
|
||||
[Category(Evolutions), Description("Percentage chance that only one ability will be inherited, and a new random one will replace the other.")]
|
||||
public decimal InheritAbilityOnlyOneChance { get; set; } = 45;
|
||||
|
||||
/// <summary>Percentage chance that neither one ability will be inherited, and new random ones will replace the others.</summary>
|
||||
[Category(Evolutions), Description("Percentage chance that neither ability will be inherited, and new random ones will replace the others.")]
|
||||
public decimal InheritAbilityNeitherChance { get; set; } = 20;
|
||||
|
||||
/// <summary>Inherit the held item values from the pre-evolution.</summary>
|
||||
[Category(Evolutions), Description("Inherit the held item values from the pre-evolution.")]
|
||||
public bool InheritHeldItem { get; set; } = true;
|
||||
|
||||
/// <summary>Inherit the TM/HM compatibility from the pre-evolution.</summary>
|
||||
[Category(Evolutions), Description("Inherit the TM/HM compatibility values from the pre-evolution.")]
|
||||
public bool InheritChildTM { get; set; } = true;
|
||||
|
||||
/// <summary>Inherit the Tutor compatibility from the pre-evolution.</summary>
|
||||
[Category(Evolutions), Description("Inherit the Tutor compatibility values from the pre-evolution.")]
|
||||
public bool InheritChildTutor { get; set; } = true;
|
||||
|
||||
/// <summary>Inherit the Type Tutor values from the pre-evolution.</summary>
|
||||
[Category(Evolutions), Description("Inherit the Type Tutor compatibility values from the pre-evolution.")]
|
||||
public bool InheritChildSpecial { get; set; } = true;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
428
pkNX.Randomization/Randomizers/Personal/PersonalRandomizer.cs
Normal file
428
pkNX.Randomization/Randomizers/Personal/PersonalRandomizer.cs
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
public class PersonalRandomizer : Randomizer
|
||||
{
|
||||
private const int tmcount = 100;
|
||||
private const int eggGroupCount = 16;
|
||||
private const int TypeCount = 18;
|
||||
|
||||
private readonly GameInfo Game;
|
||||
private readonly PersonalTable Table;
|
||||
private readonly EvolutionSet[] Evolutions;
|
||||
|
||||
public PersonalRandSettings Settings { get; set; }
|
||||
|
||||
public PersonalRandomizer(PersonalTable table, GameInfo game, EvolutionSet[] evolutions)
|
||||
{
|
||||
Game = game;
|
||||
Table = table;
|
||||
Evolutions = evolutions;
|
||||
if (File.Exists("bannedabilites.txt"))
|
||||
{
|
||||
var data = File.ReadAllLines("bannedabilities.txt");
|
||||
var list = new List<int>(BannedAbilities);
|
||||
list.AddRange(data.Select(z => Convert.ToInt32(z)));
|
||||
BannedAbilities = list;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
if (Settings.ModifyByEvolutions)
|
||||
RandomizeChains();
|
||||
else
|
||||
RandomizeAllSpecies();
|
||||
}
|
||||
|
||||
private void RandomizeAllSpecies()
|
||||
{
|
||||
for (var species = 0; species <= Game.MaxSpeciesID; species++)
|
||||
RandomizeSpecies(species);
|
||||
}
|
||||
|
||||
private bool[] processed;
|
||||
|
||||
private void RandomizeChains()
|
||||
{
|
||||
processed = new bool[Table.Table.Length];
|
||||
for (var species = 0; species <= Game.MaxSpeciesID; species++)
|
||||
{
|
||||
for (int f = 0; f <= Table[species].FormeCount; f++)
|
||||
RandomizeChain(species, f);
|
||||
}
|
||||
}
|
||||
|
||||
private bool AlreadyProcessed(int index)
|
||||
{
|
||||
if (processed[index])
|
||||
return true;
|
||||
processed[index] = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void RandomizeChain(int species, int forme)
|
||||
{
|
||||
var index = Table.GetFormeIndex(species, forme);
|
||||
if (AlreadyProcessed(index))
|
||||
return;
|
||||
processed[index] = true;
|
||||
|
||||
var entry = Table[index];
|
||||
Randomize(entry, species);
|
||||
ProcessEvolutions(species, forme, index);
|
||||
}
|
||||
|
||||
private void ProcessEvolutions(int species, int forme, int devolvedIndex)
|
||||
{
|
||||
var evoindex = GetEvolutionEntry(species, forme);
|
||||
var evos = Evolutions[evoindex];
|
||||
|
||||
if (evos.PossibleEvolutions.Length == 1)
|
||||
{
|
||||
var evo = evos.PossibleEvolutions[0];
|
||||
var ei = Table.GetFormeIndex(evo.Species, evo.Form);
|
||||
|
||||
if (AlreadyProcessed(evoindex))
|
||||
return;
|
||||
RandomizeSingleChain(evo, devolvedIndex);
|
||||
ProcessEvolutions(evo.Species, evo.Form, ei);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var evo in evos.PossibleEvolutions)
|
||||
{
|
||||
var ei = Table.GetFormeIndex(evo.Species, evo.Form);
|
||||
if (AlreadyProcessed(evoindex))
|
||||
return;
|
||||
RandomizeSplitChain(evo, devolvedIndex);
|
||||
ProcessEvolutions(evo.Species, evo.Form, ei);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RandomizeSingleChain(EvolutionMethod evo, int devolvedIndex)
|
||||
{
|
||||
var child = Table[devolvedIndex];
|
||||
var z = Table.GetFormeEntry(evo.Species, evo.Form);
|
||||
RandomizeFrom(z, child, evo.Species);
|
||||
}
|
||||
|
||||
private void RandomizeSplitChain(EvolutionMethod evo, int devolvedIndex)
|
||||
{
|
||||
var child = Table[devolvedIndex];
|
||||
var z = Table.GetFormeEntry(evo.Species, evo.Form);
|
||||
RandomizeFrom(z, child, evo.Species);
|
||||
}
|
||||
|
||||
private int GetEvolutionEntry(int species, int form)
|
||||
{
|
||||
if (Game.Generation < 7)
|
||||
return species;
|
||||
return Table.GetFormeIndex(species, form);
|
||||
}
|
||||
|
||||
private void RandomizeSpecies(int species)
|
||||
{
|
||||
var entry = Table[species];
|
||||
Randomize(entry, species);
|
||||
var formeCount = entry.FormeCount;
|
||||
for (int forme = 1; forme <= formeCount; forme++)
|
||||
{
|
||||
entry = Table.GetFormeEntry(species, forme);
|
||||
Randomize(entry, species);
|
||||
}
|
||||
}
|
||||
|
||||
public void RandomizeFrom(PersonalInfo z, PersonalInfo child, int species)
|
||||
{
|
||||
if (Settings.ModifyStats)
|
||||
RandomizeStats(z);
|
||||
if (Settings.ShuffleStats)
|
||||
RandomShuffledStats(z);
|
||||
|
||||
if (Settings.ModifyTypes)
|
||||
{
|
||||
if (Settings.InheritType && Settings.InheritTypeSetting != ModifyState.All)
|
||||
{
|
||||
var types = child.Types;
|
||||
switch (Settings.InheritTypeSetting)
|
||||
{
|
||||
case ModifyState.Two when Rand.Next(100) < Settings.InheritTypeNeitherChance:
|
||||
types = new[] { GetRandomType(), GetRandomType() };
|
||||
break;
|
||||
case ModifyState.Two when Rand.Next(100) < Settings.InheritTypeOnlyOneChance:
|
||||
case ModifyState.One when Rand.Next(100) < Settings.InheritTypeOnlyOneChance:
|
||||
types[Rand.Next(2)] = GetRandomType();
|
||||
break;
|
||||
}
|
||||
if (Rand.Next(0, 100) < Settings.SameTypeChance)
|
||||
{
|
||||
int index = Rand.Next(2);
|
||||
types[index ^ 1] = types[index];
|
||||
}
|
||||
z.Types = types;
|
||||
}
|
||||
else
|
||||
{
|
||||
RandomizeTypes(z);
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.ModifyAbility)
|
||||
{
|
||||
if (Settings.InheritAbility)
|
||||
{
|
||||
var abils = child.Abilities;
|
||||
GetRandomAbilities(abils, Settings.InheritAbilitySetting);
|
||||
z.Abilities = abils;
|
||||
}
|
||||
else
|
||||
{
|
||||
RandomizeAbilities(z);
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.ModifyLearnsetTM || Settings.ModifyLearnsetHM)
|
||||
{
|
||||
if (Settings.InheritChildTM)
|
||||
z.TMHM = child.TMHM;
|
||||
else
|
||||
RandomizeTMHM(z);
|
||||
}
|
||||
|
||||
if (Settings.ModifyLearnsetTypeTutors)
|
||||
{
|
||||
if (Settings.InheritChildSpecial)
|
||||
z.TypeTutors = child.TypeTutors;
|
||||
else
|
||||
RandomizeTypeTutors(z, species);
|
||||
}
|
||||
|
||||
if (Settings.ModifyLearnsetMoveTutors)
|
||||
{
|
||||
if (Settings.InheritChildTutor)
|
||||
z.SpecialTutors = child.SpecialTutors;
|
||||
else
|
||||
RandomizeSpecialTutors(z);
|
||||
}
|
||||
|
||||
if (Settings.ModifyEgg)
|
||||
z.EggGroups = child.EggGroups;
|
||||
|
||||
if (Settings.ModifyHeldItems)
|
||||
{
|
||||
if (Settings.InheritHeldItem)
|
||||
z.Items = child.Items;
|
||||
else
|
||||
RandomizeHeldItems(z);
|
||||
}
|
||||
|
||||
ExecuteCatchRate(z);
|
||||
}
|
||||
|
||||
private void GetRandomAbilities(int[] abils, ModifyState setting)
|
||||
{
|
||||
switch (setting)
|
||||
{
|
||||
case ModifyState.All:
|
||||
GetRandomAbilities(abils);
|
||||
break;
|
||||
case ModifyState.Two when Rand.Next(100) < Settings.InheritAbilityNeitherChance:
|
||||
GetRandomAbilities(abils, 1);
|
||||
break;
|
||||
case ModifyState.Two when Rand.Next(100) < Settings.InheritAbilityOnlyOneChance:
|
||||
case ModifyState.One when Rand.Next(100) < Settings.InheritAbilityOnlyOneChance:
|
||||
GetRandomAbilities(abils, 2);
|
||||
break;
|
||||
}
|
||||
if (Rand.Next(100) < Settings.SameAbilityChance)
|
||||
{
|
||||
int index = Rand.Next(2);
|
||||
abils[index^1] = abils[index];
|
||||
}
|
||||
}
|
||||
|
||||
public void Randomize(PersonalInfo z, int species)
|
||||
{
|
||||
if (Settings.ModifyStats)
|
||||
RandomizeStats(z);
|
||||
if (Settings.ShuffleStats)
|
||||
RandomShuffledStats(z);
|
||||
|
||||
if (Settings.ModifyTypes)
|
||||
RandomizeTypes(z);
|
||||
|
||||
if (Settings.ModifyAbility)
|
||||
RandomizeAbilities(z);
|
||||
|
||||
if (Settings.ModifyLearnsetTM || Settings.ModifyLearnsetHM)
|
||||
RandomizeTMHM(z);
|
||||
|
||||
if (Settings.ModifyLearnsetTypeTutors)
|
||||
RandomizeTypeTutors(z, species);
|
||||
|
||||
if (Settings.ModifyLearnsetMoveTutors)
|
||||
RandomizeSpecialTutors(z);
|
||||
|
||||
if (Settings.ModifyEgg)
|
||||
RandomizeEggGroups(z);
|
||||
|
||||
if (Settings.ModifyHeldItems)
|
||||
RandomizeHeldItems(z);
|
||||
|
||||
ExecuteCatchRate(z);
|
||||
}
|
||||
|
||||
private void ExecuteCatchRate(PersonalInfo z)
|
||||
{
|
||||
if (Settings.CatchRate == CatchRate.Random)
|
||||
z.CatchRate = Rand.Next(3, 251); // Random Catch Rate between 3 and 250.
|
||||
else if (Settings.CatchRate == CatchRate.BSTScaled)
|
||||
z.CatchRate = GetBSTCatchRate(z.BST);
|
||||
}
|
||||
|
||||
private static int GetBSTCatchRate(int BST)
|
||||
{
|
||||
var c = 11 * (Math.Sqrt(Math.Max(0, 600 - BST)));
|
||||
|
||||
const int min = 3;
|
||||
return (int)Math.Min(255, min + c);
|
||||
}
|
||||
|
||||
private void RandomizeTMHM(PersonalInfo z)
|
||||
{
|
||||
var tms = z.TMHM;
|
||||
|
||||
if (Settings.ModifyLearnsetTM)
|
||||
{
|
||||
for (int j = 0; j < tmcount; j++)
|
||||
tms[j] = Rand.Next(100) < Settings.LearnTMPercent;
|
||||
}
|
||||
|
||||
if (Settings.ModifyLearnsetHM)
|
||||
{
|
||||
for (int j = tmcount; j < tms.Length; j++)
|
||||
tms[j] = Rand.Next(100) < Settings.LearnTMPercent;
|
||||
}
|
||||
|
||||
z.TMHM = tms;
|
||||
}
|
||||
|
||||
private void RandomizeTypeTutors(PersonalInfo z, int species)
|
||||
{
|
||||
var t = z.TypeTutors;
|
||||
for (int i = 0; i < t.Length; i++)
|
||||
t[i] = Rand.Next(100) < Settings.LearnTypeTutorPercent;
|
||||
|
||||
// Make sure Rayquaza can learn Dragon Ascent.
|
||||
if (!Game.XY && species == 384)
|
||||
t[7] = true;
|
||||
|
||||
z.TypeTutors = t;
|
||||
}
|
||||
|
||||
private void RandomizeSpecialTutors(PersonalInfo z)
|
||||
{
|
||||
var tutors = z.SpecialTutors;
|
||||
foreach (bool[] tutor in tutors)
|
||||
{
|
||||
for (int i = 0; i < tutor.Length; i++)
|
||||
tutor[i] = Rand.Next(100) < Settings.LearnMoveTutorPercent;
|
||||
}
|
||||
|
||||
z.SpecialTutors = tutors;
|
||||
}
|
||||
|
||||
private void RandomizeAbilities(PersonalInfo z)
|
||||
{
|
||||
var abils = z.Abilities;
|
||||
GetRandomAbilities(abils, Settings.Ability);
|
||||
z.Abilities = abils;
|
||||
}
|
||||
|
||||
private void GetRandomAbilities(int[] abils, int skip = 0)
|
||||
{
|
||||
for (int i = 0; i < abils.Length - skip; i++)
|
||||
abils[i] = GetRandomAbility();
|
||||
}
|
||||
|
||||
private void RandomizeEggGroups(PersonalInfo z)
|
||||
{
|
||||
var egg = z.EggGroups;
|
||||
egg[0] = GetRandomEggGroup();
|
||||
egg[1] = Rand.Next(100) < Settings.SameEggGroupChance ? egg[0] : GetRandomEggGroup();
|
||||
z.EggGroups = egg;
|
||||
}
|
||||
|
||||
private void RandomizeHeldItems(PersonalInfo z)
|
||||
{
|
||||
var item = z.Items;
|
||||
for (int j = 0; j < item.Length; j++)
|
||||
item[j] = GetRandomHeldItem();
|
||||
z.Items = item;
|
||||
}
|
||||
|
||||
private void RandomizeTypes(PersonalInfo z)
|
||||
{
|
||||
var t = z.Types;
|
||||
t[0] = GetRandomType();
|
||||
t[1] = Rand.Next(0, 100) < Settings.SameTypeChance ? t[0] : GetRandomType();
|
||||
z.Types = t;
|
||||
}
|
||||
|
||||
private void RandomizeStats(PersonalInfo z)
|
||||
{
|
||||
// Fiddle with Base Stats, don't muck with Shedinja.
|
||||
var stats = z.Stats;
|
||||
if (stats[0] == 1)
|
||||
return;
|
||||
int RandDeviation() => Rand.Next(Settings.StatDeviationMin, Settings.StatDeviationMax);
|
||||
for (int i = 0; i < stats.Length; i++)
|
||||
{
|
||||
if (!Settings.StatsToRandomize[i])
|
||||
continue;
|
||||
|
||||
var val = stats[i] * RandDeviation() / 100;
|
||||
var stat = Math.Max(1, Math.Min(255, val));
|
||||
}
|
||||
z.Stats = stats;
|
||||
}
|
||||
|
||||
private void RandomShuffledStats(PersonalInfo z)
|
||||
{
|
||||
// Fiddle with Base Stats, don't muck with Shedinja.
|
||||
var stats = z.Stats;
|
||||
if (stats[0] == 1)
|
||||
return;
|
||||
for (int i = 0; i < stats.Length; i++)
|
||||
Util.Shuffle(stats);
|
||||
z.Stats = stats;
|
||||
}
|
||||
|
||||
private int GetRandomType() => Rand.Next(0, TypeCount);
|
||||
private int GetRandomEggGroup() => Rand.Next(1, eggGroupCount);
|
||||
private int GetRandomHeldItem() => Game.HeldItems[Rand.Next(1, Game.HeldItems.Length)];
|
||||
private readonly IList<int> BannedAbilities = Array.Empty<int>();
|
||||
|
||||
private int GetRandomAbility()
|
||||
{
|
||||
const int WonderGuard = 25;
|
||||
while (true)
|
||||
{
|
||||
int newabil = Rand.Next(1, Game.MaxAbilityID + 1);
|
||||
if (newabil == WonderGuard && Settings.WonderGuard == Permissive.No)
|
||||
continue;
|
||||
if (BannedAbilities.Contains(newabil))
|
||||
continue;
|
||||
return newabil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
pkNX.Randomization/Randomizers/RandSettings.cs
Normal file
7
pkNX.Randomization/Randomizers/RandSettings.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace pkNX.Randomization
|
||||
{
|
||||
public class RandSettings
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
11
pkNX.Randomization/Randomizers/Randomizer.cs
Normal file
11
pkNX.Randomization/Randomizers/Randomizer.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
public abstract class Randomizer
|
||||
{
|
||||
public abstract void Execute();
|
||||
|
||||
protected readonly Random Rand = Util.Random;
|
||||
}
|
||||
}
|
||||
137
pkNX.Randomization/Randomizers/SpeciesRandomizer.cs
Normal file
137
pkNX.Randomization/Randomizers/SpeciesRandomizer.cs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
using System.Linq;
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
public class SpeciesRandomizer
|
||||
{
|
||||
private readonly PersonalTable SpeciesStat;
|
||||
private readonly int MaxSpeciesID;
|
||||
private readonly GameInfo Game;
|
||||
|
||||
private SpeciesSettings s;
|
||||
|
||||
public SpeciesRandomizer(GameInfo game, PersonalTable t)
|
||||
{
|
||||
Game = game;
|
||||
MaxSpeciesID = Game.MaxSpeciesID;
|
||||
SpeciesStat = t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="RandSpec"/> according to the provided settings.
|
||||
/// </summary>
|
||||
/// <param name="settings"></param>
|
||||
public void Initialize(SpeciesSettings settings)
|
||||
{
|
||||
s = settings;
|
||||
var list = s.GetSpecies(Game.MaxSpeciesID, Game.Generation);
|
||||
RandSpec = new GenericRandomizer(list);
|
||||
}
|
||||
|
||||
#region Random Species Filtering Parameters
|
||||
private GenericRandomizer RandSpec;
|
||||
private int loopctr;
|
||||
private const int l = 10; // tweakable scalars
|
||||
private const int h = 11;
|
||||
#endregion
|
||||
|
||||
internal int GetRandomSpecies(int oldSpecies, params int[] bannedSpecies)
|
||||
{
|
||||
// Get a new random species
|
||||
var oldpkm = SpeciesStat[oldSpecies];
|
||||
|
||||
loopctr = 0; // altering calculations to prevent infinite loops
|
||||
int newSpecies;
|
||||
while (!GetNewSpecies(oldSpecies, oldpkm, out newSpecies) || bannedSpecies.Contains(newSpecies))
|
||||
loopctr++;
|
||||
return newSpecies;
|
||||
}
|
||||
|
||||
public int GetRandomSpeciesType(int oldSpecies, int type)
|
||||
{
|
||||
// Get a new random species
|
||||
PersonalInfo oldpkm = SpeciesStat[oldSpecies];
|
||||
|
||||
loopctr = 0; // altering calculations to prevent infinite loops
|
||||
int newSpecies;
|
||||
while (!GetNewSpecies(oldSpecies, oldpkm, out newSpecies) || !GetIsTypeMatch(newSpecies, type))
|
||||
loopctr++;
|
||||
return newSpecies;
|
||||
}
|
||||
|
||||
private bool GetIsTypeMatch(int newSpecies, int type) => type == -1 || SpeciesStat[newSpecies].Types.Any(z => z == type) || loopctr > 9000;
|
||||
|
||||
public int GetRandomSpecies(int oldSpecies)
|
||||
{
|
||||
// Get a new random species
|
||||
var oldpkm = SpeciesStat[oldSpecies];
|
||||
|
||||
loopctr = 0; // altering calculations to prevent infinite loops
|
||||
int newSpecies;
|
||||
while (!GetNewSpecies(oldSpecies, oldpkm, out newSpecies))
|
||||
{
|
||||
if (loopctr > 0x0001_0000)
|
||||
{
|
||||
var pkm = SpeciesStat[newSpecies];
|
||||
if (IsSpeciesBSTBad(oldpkm, pkm) && loopctr > 0x0001_1000) // keep trying for at minimum BST
|
||||
continue;
|
||||
return newSpecies; // failed to find any match based on criteria, return random species that may or may not match criteria
|
||||
}
|
||||
loopctr++;
|
||||
}
|
||||
return newSpecies;
|
||||
}
|
||||
|
||||
public int[] RandomSpeciesList => Enumerable.Range(1, MaxSpeciesID).ToArray();
|
||||
|
||||
private bool GetNewSpecies(int currentSpecies, PersonalInfo oldpkm, out int newSpecies)
|
||||
{
|
||||
newSpecies = RandSpec.Next();
|
||||
var pkm = SpeciesStat[newSpecies];
|
||||
|
||||
if (IsSpeciesReplacementBad(newSpecies, currentSpecies)) // no A->A randomization
|
||||
return false;
|
||||
return IsCriteriaMatch(oldpkm, pkm);
|
||||
}
|
||||
|
||||
private bool IsSpeciesReplacementBad(int newSpecies, int currentSpecies)
|
||||
{
|
||||
if (newSpecies != currentSpecies)
|
||||
return false;
|
||||
return loopctr < MaxSpeciesID * 10;
|
||||
}
|
||||
|
||||
private bool IsCriteriaMatch(PersonalInfo oldpkm, PersonalInfo pkm)
|
||||
{
|
||||
if (IsSpeciesEXPRateBad(oldpkm, pkm))
|
||||
return false;
|
||||
if (IsSpeciesTypeBad(oldpkm, pkm))
|
||||
return false;
|
||||
if (IsSpeciesBSTBad(oldpkm, pkm))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsSpeciesEXPRateBad(PersonalInfo oldpkm, PersonalInfo pkm)
|
||||
{
|
||||
return s.EXPGroup && oldpkm.EXPGrowth == pkm.EXPGrowth;
|
||||
}
|
||||
|
||||
private bool IsSpeciesTypeBad(PersonalInfo oldpkm, PersonalInfo pkm)
|
||||
{
|
||||
return s.Type && oldpkm.Types.Intersect(pkm.Types).Any();
|
||||
}
|
||||
|
||||
private bool IsSpeciesBSTBad(PersonalInfo oldpkm, PersonalInfo pkm)
|
||||
{
|
||||
if (!s.BST)
|
||||
return false;
|
||||
// Base stat total has to be close to original BST
|
||||
int expand = loopctr / MaxSpeciesID;
|
||||
int lo = oldpkm.BST * l / (h + expand);
|
||||
int hi = oldpkm.BST * (h + expand) / l;
|
||||
return lo > pkm.BST || pkm.BST > hi;
|
||||
}
|
||||
}
|
||||
}
|
||||
192
pkNX.Randomization/Randomizers/SpeciesSettings.cs
Normal file
192
pkNX.Randomization/Randomizers/SpeciesSettings.cs
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for what Species are permitted during randomization.
|
||||
/// </summary>
|
||||
public class SpeciesSettings : RandSettings
|
||||
{
|
||||
private const string General = nameof(General);
|
||||
private const string Misc = nameof(Misc);
|
||||
|
||||
/// <summary>Allows Generation 1 species when randomizing.</summary>
|
||||
[Category(General), Description("Allows Generation 1 species when randomizing.")]
|
||||
public bool Gen1 { get; set; } = true;
|
||||
|
||||
/// <summary>Allows Generation 2 species when randomizing.</summary>
|
||||
[Category(General), Description("Allows Generation 2 species when randomizing.")]
|
||||
public bool Gen2 { get; set; } = true;
|
||||
|
||||
/// <summary>Allows Generation 3 species when randomizing.</summary>
|
||||
[Category(General), Description("Allows Generation 3 species when randomizing.")]
|
||||
public bool Gen3 { get; set; } = true;
|
||||
|
||||
/// <summary>Allows Generation 4 species when randomizing.</summary>
|
||||
[Category(General), Description("Allows Generation 4 species when randomizing.")]
|
||||
public bool Gen4 { get; set; } = true;
|
||||
|
||||
/// <summary>Allows Generation 5 species when randomizing.</summary>
|
||||
[Category(General), Description("Allows Generation 5 species when randomizing.")]
|
||||
public bool Gen5 { get; set; } = true;
|
||||
|
||||
/// <summary>Allows Generation 6 species when randomizing.</summary>
|
||||
[Category(General), Description("Allows Generation 6 species when randomizing.")]
|
||||
public bool Gen6 { get; set; } = true;
|
||||
|
||||
/// <summary>Allows Generation 7 species when randomizing.</summary>
|
||||
[Category(General), Description("Allows Generation 7 species when randomizing.")]
|
||||
public bool Gen7 { get; set; } = true;
|
||||
|
||||
/// <summary>Allows Legendary species when randomizing.</summary>
|
||||
[Category(Misc), Description("Allows Legendary species when randomizing.")]
|
||||
public bool Legends { get; set; } = false;
|
||||
|
||||
/// <summary>Allows Event-only species when randomizing.</summary>
|
||||
[Category(Misc), Description("Allows Event-only species when randomizing.")]
|
||||
public bool Events { get; set; } = false;
|
||||
|
||||
/// <summary>Allows Shedinja as a random species when randomizing.</summary>
|
||||
[Category(Misc), Description("Allows Shedinja as a random species when randomizing.")]
|
||||
public bool Shedinja { get; set; } = false;
|
||||
|
||||
/// <summary>Requires the randomized species to be in the same EXP Group as the original species.</summary>
|
||||
[Category(Misc), Description("Requires the randomized species to be in the same EXP Group as the original species. Note: might not be used by all randomizers.")]
|
||||
public bool EXPGroup { get; set; } = false;
|
||||
|
||||
/// <summary>Requires the randomized species to have a similar Base Stat Total as the original species.</summary>
|
||||
[Category(Misc), Description("Requires the randomized species to have a similar Base Stat Total as the original species. Note: might not be used by all randomizers.")]
|
||||
public bool BST { get; set; } = true;
|
||||
|
||||
/// <summary>Requires the randomized species to have a similar typing as the original species.</summary>
|
||||
[Category(Misc), Description("Requires the randomized species to have a similar typing as the original species. Note: might not be used by all randomizers.")]
|
||||
public bool Type { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of Species according to the specified settings.
|
||||
/// </summary>
|
||||
/// <param name="maxSpecies">Max Species ID</param>
|
||||
/// <param name="generation"></param>
|
||||
/// <returns></returns>
|
||||
public int[] GetSpecies(int maxSpecies, int generation)
|
||||
{
|
||||
var list = new List<int>();
|
||||
if (Gen1) AddGen1Species(list, maxSpecies);
|
||||
if (Gen2) AddGen2Species(list, maxSpecies);
|
||||
if (Gen3) AddGen3Species(list, maxSpecies);
|
||||
if (Gen4) AddGen4Species(list, maxSpecies);
|
||||
if (Gen5) AddGen5Species(list, maxSpecies);
|
||||
if (Gen6) AddGen6Species(list, maxSpecies);
|
||||
if (Gen7) AddGen7Species(list, maxSpecies);
|
||||
|
||||
if (generation == 7 && Gen1 && maxSpecies <= Legal.MaxSpeciesID_7_GG)
|
||||
AddGGEvents(list);
|
||||
|
||||
return list.Count == 0 ? GetSpeciesAll(maxSpecies) : list.ToArray();
|
||||
}
|
||||
|
||||
private int[] GetSpeciesAll(int maxSpecies) => Enumerable.Range(1, maxSpecies).ToArray();
|
||||
|
||||
private void AddGen1Species(List<int> list, int maxSpecies)
|
||||
{
|
||||
if (maxSpecies <= 0)
|
||||
return;
|
||||
list.AddRange(Enumerable.Range(1, 143)); // Bulbasaur - Snorlax
|
||||
list.AddRange(Enumerable.Range(147, 3)); // Dratini - Dragonite
|
||||
|
||||
if (Legends)
|
||||
{
|
||||
list.AddRange(Enumerable.Range(144, 3)); // Birds
|
||||
list.Add(150); // Mewtwo
|
||||
}
|
||||
if (Events) list.Add(151); // Mew
|
||||
}
|
||||
|
||||
private void AddGen2Species(List<int> list, int maxSpecies)
|
||||
{
|
||||
if (maxSpecies <= 151)
|
||||
return;
|
||||
list.AddRange(Enumerable.Range(152, 91)); // Chikorita - Blissey
|
||||
list.AddRange(Enumerable.Range(246, 3)); // Larvitar - Tyranitar
|
||||
|
||||
if (Legends)
|
||||
{
|
||||
list.AddRange(Enumerable.Range(243, 3)); // Beasts
|
||||
list.AddRange(Enumerable.Range(249, 2)); // Lugia & Ho-Oh
|
||||
}
|
||||
if (Events) list.Add(251); // Celebi
|
||||
}
|
||||
|
||||
private void AddGen3Species(List<int> list, int maxSpecies)
|
||||
{
|
||||
if (maxSpecies <= 251)
|
||||
return;
|
||||
list.AddRange(Enumerable.Range(252, 40)); // Treecko - Ninjask
|
||||
list.AddRange(Enumerable.Range(293, 84)); // Whismur - Metagross
|
||||
if (Shedinja) list.Add(292); // Shedinja
|
||||
if (Legends) list.AddRange(Enumerable.Range(377, 8)); // Regi, Lati, Mascot
|
||||
if (Events) list.AddRange(Enumerable.Range(385, 2)); // Jirachi/Deoxys
|
||||
}
|
||||
|
||||
private void AddGen4Species(List<int> list, int maxSpecies)
|
||||
{
|
||||
if (maxSpecies <= 386)
|
||||
return;
|
||||
list.AddRange(Enumerable.Range(387, 93)); // Turtwig - Rotom
|
||||
if (Legends) list.AddRange(Enumerable.Range(480, 9)); // Sinnoh Legends
|
||||
if (Events) list.AddRange(Enumerable.Range(489, 5)); // Phione, Manaphy, Darkrai, Shaymin, Arceus
|
||||
}
|
||||
|
||||
private void AddGen5Species(List<int> list, int maxSpecies)
|
||||
{
|
||||
if (maxSpecies <= 493)
|
||||
return;
|
||||
list.AddRange(Enumerable.Range(495, 143)); // Snivy - Volcarona
|
||||
if (Legends) list.AddRange(Enumerable.Range(638, 9)); // Unova Legends
|
||||
if (Events) list.Add(494); list.AddRange(Enumerable.Range(647, 3)); // Victini, Keldeo, Meloetta, Genesect
|
||||
}
|
||||
|
||||
private void AddGen6Species(List<int> list, int maxSpecies)
|
||||
{
|
||||
if (maxSpecies <= 649)
|
||||
return;
|
||||
list.AddRange(Enumerable.Range(650, 66)); // Chespin - Noivern
|
||||
if (Legends) list.AddRange(Enumerable.Range(716, 3)); // Kalos Legends
|
||||
if (Events) list.AddRange(Enumerable.Range(719, 3)); // Diancie, Hoopa, Volcanion
|
||||
}
|
||||
|
||||
private void AddGen7Species(List<int> list, int maxSpecies)
|
||||
{
|
||||
if (maxSpecies <= 721)
|
||||
return;
|
||||
list.AddRange(Enumerable.Range(722, 50)); // Rowlet - Pyukumuku
|
||||
list.AddRange(Enumerable.Range(774, 11)); // Minior - Kommo-o
|
||||
|
||||
if (Legends)
|
||||
{
|
||||
list.AddRange(Enumerable.Range(772, 2)); // Type: Null, Silvally
|
||||
list.AddRange(Enumerable.Range(785, 16)); // Tapus, Legends, UBs
|
||||
}
|
||||
if (Events) list.AddRange(Enumerable.Range(801, 2)); // Magearna, Marshadow
|
||||
|
||||
if (maxSpecies >= Legal.MaxSpeciesID_7_USUM) // USUM
|
||||
{
|
||||
if (Legends) list.AddRange(Enumerable.Range(803, 4)); // Poipole, Naganadel, Stakataka, Blacephalon
|
||||
if (Events) list.Add(807); // Zeraora
|
||||
}
|
||||
if (maxSpecies >= Legal.MaxSpeciesID_7_GG) // USUM
|
||||
{
|
||||
if (Events)
|
||||
AddGGEvents(list);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddGGEvents(List<int> list)
|
||||
{
|
||||
list.AddRange(Enumerable.Range(808, Legal.MaxSpeciesID_7_GG - Legal.MaxAbilityID_7_USUM)); // *
|
||||
}
|
||||
}
|
||||
}
|
||||
38
pkNX.Randomization/SpeciesSetting.cs
Normal file
38
pkNX.Randomization/SpeciesSetting.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using pkNX.Structures;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
public class SpeciesSetting
|
||||
{
|
||||
public bool Gen1 { get; set; }
|
||||
public bool Gen2 { get; set; }
|
||||
public bool Gen3 { get; set; }
|
||||
public bool Gen4 { get; set; }
|
||||
public bool Gen5 { get; set; }
|
||||
public bool Gen6 { get; set; }
|
||||
public bool Gen7 { get; set; }
|
||||
|
||||
public bool L { get; set; } = false;
|
||||
public bool E { get; set; } = false;
|
||||
public bool Shedinja = false;
|
||||
public bool EXP { get; set; } = false;
|
||||
public bool BST { get; set; } = true;
|
||||
public bool Type { get; set; } = false;
|
||||
|
||||
private readonly GameVersion Game;
|
||||
private readonly int Generation;
|
||||
|
||||
public SpeciesSetting(GameVersion game)
|
||||
{
|
||||
Game = game;
|
||||
Generation = Game.GetGeneration();
|
||||
Gen1 = Generation <= 1;
|
||||
Gen2 = Generation <= 2;
|
||||
Gen3 = Generation <= 3;
|
||||
Gen4 = Generation <= 4;
|
||||
Gen5 = Generation <= 5;
|
||||
Gen6 = Generation <= 6;
|
||||
Gen7 = Generation <= 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
30
pkNX.Randomization/Util.cs
Normal file
30
pkNX.Randomization/Util.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace pkNX.Randomization
|
||||
{
|
||||
public static class Util
|
||||
{
|
||||
public static Random Random { get; private set; } = new Random();
|
||||
|
||||
public static void ReseedRand(int seed)
|
||||
{
|
||||
Random = new Random(seed);
|
||||
Structures.Util.Rand = Random;
|
||||
}
|
||||
|
||||
public static uint Rand32() => (uint)Random.Next(1 << 30) << 2 | (uint)Random.Next(1 << 2);
|
||||
|
||||
public static void Shuffle<T>(IList<T> array)
|
||||
{
|
||||
int n = array.Count;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int r = i + (int)(Random.NextDouble() * (n - i));
|
||||
var t = array[r];
|
||||
array[r] = array[i];
|
||||
array[i] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
pkNX.Randomization/pkNX.Randomization.csproj
Normal file
12
pkNX.Randomization/pkNX.Randomization.csproj
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
|
||||
<Description>Randomizer Utility</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\pkNX.Structures\pkNX.Structures.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
89
pkNX.Structures/CodePattern.cs
Normal file
89
pkNX.Structures/CodePattern.cs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public static class CodePattern
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds a provided <see cref="pattern"/> within the supplied <see cref="array"/>.
|
||||
/// </summary>
|
||||
/// <param name="array">Array to look in</param>
|
||||
/// <param name="pattern">Pattern to look for</param>
|
||||
/// <param name="startIndex">Starting offset to look from</param>
|
||||
/// <param name="length">Amount of entries to look through</param>
|
||||
/// <returns>Index the pattern occurrs at; if not found, returns -1.</returns>
|
||||
public static int IndexOfBytes(byte[] array, byte[] pattern, int startIndex = 0, int length = -1)
|
||||
{
|
||||
int len = pattern.Length;
|
||||
int endIndex = length > 0
|
||||
? startIndex + length
|
||||
: array.Length - len - startIndex;
|
||||
|
||||
endIndex = Math.Min(array.Length - pattern.Length, endIndex);
|
||||
|
||||
int i = startIndex;
|
||||
int j = 0;
|
||||
while (true)
|
||||
{
|
||||
if (pattern[j] != array[i + j])
|
||||
{
|
||||
if (++i == endIndex)
|
||||
return -1;
|
||||
j = 0;
|
||||
}
|
||||
else if (++j == len)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a provided <see cref="pattern"/> within the supplied <see cref="array"/>.
|
||||
/// </summary>
|
||||
/// <param name="array">Array to look in</param>
|
||||
/// <param name="pattern">Pattern to look for</param>
|
||||
/// <param name="wildCard">Wildcard byte to be ignored, incrementally as bitflags.</param>
|
||||
/// <param name="startIndex">Starting offset to look from</param>
|
||||
/// <param name="length">Amount of entries to look through</param>
|
||||
/// <returns>Index the pattern occurrs at; if not found, returns -1.</returns>
|
||||
public static int IndexOfPattern(byte[] array, byte[] pattern, ulong wildCard, int startIndex = 0, int length = -1)
|
||||
{
|
||||
Debug.Assert(pattern.Length <= 8*sizeof(ulong));
|
||||
|
||||
int len = pattern.Length;
|
||||
int endIndex = length > 0
|
||||
? startIndex + length
|
||||
: array.Length - len - startIndex;
|
||||
|
||||
endIndex = Math.Min(array.Length - pattern.Length, endIndex);
|
||||
|
||||
int i = startIndex;
|
||||
int j = 0;
|
||||
while (true)
|
||||
{
|
||||
if (pattern[j] != array[i + j] && ((wildCard >> j) & 1) == 0)
|
||||
{
|
||||
if (++i == endIndex)
|
||||
return -1;
|
||||
j = 0;
|
||||
}
|
||||
else if (++j == len)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="GameVersion.GG"/> byte pattern which precedes the TMHM list. This list is the tail end of item IDs for each TM(01->100).
|
||||
/// </summary>
|
||||
public static readonly byte[] TMHM_GG =
|
||||
{
|
||||
0xA0, 0x01, 0xA1, 0x01, 0xA2, 0x01, 0xA3, 0x01,
|
||||
0x6A, 0x02, 0x6B, 0x02, 0x6C, 0x02, 0xB2, 0x02,
|
||||
0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02,
|
||||
};
|
||||
}
|
||||
}
|
||||
11
pkNX.Structures/EggMove/EggMoves.cs
Normal file
11
pkNX.Structures/EggMove/EggMoves.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace pkNX.Structures
|
||||
{
|
||||
public abstract class EggMoves
|
||||
{
|
||||
public int Count;
|
||||
public int[] Moves;
|
||||
public int FormTableIndex;
|
||||
|
||||
public abstract byte[] Write();
|
||||
}
|
||||
}
|
||||
35
pkNX.Structures/EggMove/EggMoves6.cs
Normal file
35
pkNX.Structures/EggMove/EggMoves6.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class EggMoves6 : EggMoves
|
||||
{
|
||||
public EggMoves6(byte[] data)
|
||||
{
|
||||
if (data.Length < 2 || data.Length % 2 != 0)
|
||||
{ Count = 0; Moves = Array.Empty<int>(); return; }
|
||||
using (BinaryReader br = new BinaryReader(new MemoryStream(data)))
|
||||
{
|
||||
Moves = new int[Count = br.ReadUInt16()];
|
||||
for (int i = 0; i < Count; i++)
|
||||
Moves[i] = br.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] Write()
|
||||
{
|
||||
Count = Moves.Length;
|
||||
if (Count == 0) return Array.Empty<byte>();
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
using (BinaryWriter bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write((ushort)Count);
|
||||
for (int i = 0; i < Count; i++)
|
||||
bw.Write((ushort)Moves[i]);
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
pkNX.Structures/EggMove/EggMoves7.cs
Normal file
37
pkNX.Structures/EggMove/EggMoves7.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class EggMoves7 : EggMoves
|
||||
{
|
||||
public EggMoves7(byte[] data)
|
||||
{
|
||||
if (data.Length < 2 || data.Length % 2 != 0)
|
||||
{ Count = 0; Moves = Array.Empty<int>(); return; }
|
||||
using (BinaryReader br = new BinaryReader(new MemoryStream(data)))
|
||||
{
|
||||
FormTableIndex = br.ReadUInt16();
|
||||
Count = br.ReadUInt16();
|
||||
Moves = new int[Count];
|
||||
for (int i = 0; i < Count; i++)
|
||||
Moves[i] = br.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] Write()
|
||||
{
|
||||
Count = Moves.Length;
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
using (BinaryWriter bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write((ushort)FormTableIndex);
|
||||
bw.Write((ushort)Count);
|
||||
for (int i = 0; i < Count; i++)
|
||||
bw.Write((ushort)Moves[i]);
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
pkNX.Structures/Encounter/EncounterGift.cs
Normal file
42
pkNX.Structures/Encounter/EncounterGift.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public abstract class EncounterGift
|
||||
{
|
||||
protected byte[] Data { get; set; }
|
||||
public virtual byte[] Write() => (byte[])Data.Clone();
|
||||
|
||||
public abstract int Species { get; set; }
|
||||
public virtual int HeldItem { get; set; }
|
||||
public abstract int Level { get; set; }
|
||||
public abstract int Form { get; set; }
|
||||
public abstract FixedGender Gender { get; set; }
|
||||
|
||||
public virtual Nature Nature { get; set; } = Nature.Random;
|
||||
public virtual int Ability { get; set; }
|
||||
public virtual bool ShinyLock { get; set; }
|
||||
|
||||
public virtual bool IV3 { get; set; }
|
||||
public virtual int[] RelearnMoves { get; set; } = Array.Empty<int>();
|
||||
public abstract Shiny Shiny { get; set; }
|
||||
|
||||
public abstract int IV_HP { get; set; }
|
||||
public abstract int IV_ATK { get; set; }
|
||||
public abstract int IV_DEF { get; set; }
|
||||
public abstract int IV_SPE { get; set; }
|
||||
public abstract int IV_SPA { get; set; }
|
||||
public abstract int IV_SPD { get; set; }
|
||||
|
||||
public int[] IVs
|
||||
{
|
||||
get => new[] { IV_HP, IV_ATK, IV_DEF, IV_SPE, IV_SPA, IV_SPD };
|
||||
set
|
||||
{
|
||||
if (value?.Length != 6) return;
|
||||
IV_HP = value[0]; IV_ATK = value[1]; IV_DEF = value[2];
|
||||
IV_SPE = value[3]; IV_SPA = value[4]; IV_SPD = value[5];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
pkNX.Structures/Encounter/EncounterGift6AO.cs
Normal file
44
pkNX.Structures/Encounter/EncounterGift6AO.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class EncounterGift6AO : EncounterGift
|
||||
{
|
||||
public override int Species { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x00); }
|
||||
public int _02 { get => BitConverter.ToUInt16(Data, 0x02); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x02); }
|
||||
public override int Form { get => Data[0x04]; set => Data[0x04] = (byte)value; }
|
||||
public override int Level { get => Data[0x05]; set => Data[0x05] = (byte)value; }
|
||||
public override int Ability { get => (sbyte)Data[0x06]; set => Data[0x06] = (byte)value; }
|
||||
public override Nature Nature { get => (Nature)Data[0x07]; set => Data[0x07] = (byte)value; }
|
||||
public override Shiny Shiny { get => (Shiny)Data[0x08]; set => Data[0x08] = (byte)value; }
|
||||
|
||||
// padding?
|
||||
public int _09 { get => Data[0x09]; set => Data[0x09] = (byte)value; }
|
||||
public int _0A { get => Data[0x0A]; set => Data[0x0A] = (byte)value; }
|
||||
public int _0B { get => Data[0x0B]; set => Data[0x0B] = (byte)value; }
|
||||
|
||||
public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); }
|
||||
public override FixedGender Gender { get => (FixedGender)Data[0x10]; set => Data[0x10] = (byte)value; }
|
||||
|
||||
// padding?
|
||||
public int _11 { get => (sbyte)Data[0x11]; set => Data[0x11] = (byte)value; }
|
||||
public short MetLocation { get => BitConverter.ToInt16(Data, 0x12); set => BitConverter.GetBytes(value).CopyTo(Data, 0x12); }
|
||||
public int Move { get => BitConverter.ToUInt16(Data, 0x14); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14); }
|
||||
|
||||
public override int IV_HP { get => (sbyte)Data[0x16]; set => Data[0x16] = (byte)value; }
|
||||
public override int IV_ATK { get => (sbyte)Data[0x17]; set => Data[0x17] = (byte)value; }
|
||||
public override int IV_DEF { get => (sbyte)Data[0x18]; set => Data[0x18] = (byte)value; }
|
||||
public override int IV_SPA { get => (sbyte)Data[0x19]; set => Data[0x19] = (byte)value; }
|
||||
public override int IV_SPD { get => (sbyte)Data[0x1A]; set => Data[0x1A] = (byte)value; }
|
||||
public override int IV_SPE { get => (sbyte)Data[0x1B]; set => Data[0x1B] = (byte)value; }
|
||||
|
||||
public int CNT_Cool { get => (sbyte)Data[0x1C]; set => Data[0x1C] = (byte)value; }
|
||||
public int CNT_Beauty{ get => (sbyte)Data[0x1D]; set => Data[0x1D] = (byte)value; }
|
||||
public int CNT_Cute { get => (sbyte)Data[0x1E]; set => Data[0x1E] = (byte)value; }
|
||||
public int CNT_Smart { get => (sbyte)Data[0x1F]; set => Data[0x1F] = (byte)value; }
|
||||
public int CNT_Tough { get => (sbyte)Data[0x20]; set => Data[0x20] = (byte)value; }
|
||||
public int CNT_Sheen { get => (sbyte)Data[0x21]; set => Data[0x21] = (byte)value; }
|
||||
|
||||
public int _22 { get => (sbyte)Data[0x22]; set => Data[0x22] = (byte)value; }
|
||||
}
|
||||
}
|
||||
33
pkNX.Structures/Encounter/EncounterGift6XY.cs
Normal file
33
pkNX.Structures/Encounter/EncounterGift6XY.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class EncounterGift6XY : EncounterGift
|
||||
{
|
||||
public override int Species { get => BitConverter.ToUInt16(Data, 0x00); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x00); }
|
||||
public int _02 { get => BitConverter.ToUInt16(Data, 0x02); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x02); }
|
||||
public override int Form { get => Data[0x04]; set => Data[0x04] = (byte) value; }
|
||||
public override int Level { get => Data[0x05]; set => Data[0x05] = (byte)value; }
|
||||
public override int Ability { get => (sbyte)Data[0x06]; set => Data[0x06] = (byte)value; }
|
||||
public override Nature Nature { get => (Nature)Data[0x07]; set => Data[0x07] = (byte)value; }
|
||||
public override Shiny Shiny { get => (Shiny)Data[0x08]; set => Data[0x08] = (byte)value; }
|
||||
|
||||
// padding
|
||||
public int _09 { get => Data[0x09]; set => Data[0x09] = (byte)value; }
|
||||
public int _0A { get => Data[0x0A]; set => Data[0x0A] = (byte)value; }
|
||||
public int _0B { get => Data[0x0B]; set => Data[0x0B] = (byte)value; }
|
||||
|
||||
public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); }
|
||||
public override FixedGender Gender { get => (FixedGender)Data[0x10]; set => Data[0x10] = (byte)value; }
|
||||
|
||||
public override int IV_HP { get => (sbyte)Data[0x11]; set => Data[0x11] = (byte)value; }
|
||||
public override int IV_ATK { get => (sbyte)Data[0x12]; set => Data[0x12] = (byte)value; }
|
||||
public override int IV_DEF { get => (sbyte)Data[0x13]; set => Data[0x13] = (byte)value; }
|
||||
public override int IV_SPA { get => (sbyte)Data[0x14]; set => Data[0x14] = (byte)value; }
|
||||
public override int IV_SPD { get => (sbyte)Data[0x15]; set => Data[0x15] = (byte)value; }
|
||||
public override int IV_SPE { get => (sbyte)Data[0x16]; set => Data[0x16] = (byte)value; }
|
||||
|
||||
// padding
|
||||
public int _17 { get => (sbyte)Data[0x17]; set => Data[0x17] = (byte)value; }
|
||||
}
|
||||
}
|
||||
34
pkNX.Structures/Encounter/EncounterGift7.cs
Normal file
34
pkNX.Structures/Encounter/EncounterGift7.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class EncounterGift7 : EncounterGift
|
||||
{
|
||||
public const int SIZE = 0x14;
|
||||
|
||||
public EncounterGift7(byte[] data) => Data = data;
|
||||
|
||||
public override int Species { get => BitConverter.ToUInt16(Data, 0x0); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0); }
|
||||
public override int Form { get => Data[0x2]; set => Data[0x2] = (byte)value; }
|
||||
public override int Level { get => Data[0x3]; set => Data[0x3] = (byte)value; }
|
||||
|
||||
public override Shiny Shiny { get => (Shiny)Data[0x4]; set => Data[0x4] = (byte)value; }
|
||||
public override FixedGender Gender { get => (FixedGender)Data[0x5]; set => Data[0x5] = (byte)value; }
|
||||
public override int Ability { get => (sbyte)Data[0x6]; set => Data[0x6] = (byte)value; }
|
||||
public override Nature Nature { get => (Nature)Data[0x7]; set => Data[0x7] = (byte)value; }
|
||||
|
||||
public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x8); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x8); }
|
||||
|
||||
public bool IsEgg { get => Data[0xA] == 1; set => Data[0xA] = (byte)(value ? 1 : 0); }
|
||||
|
||||
public int SpecialMove { get => BitConverter.ToUInt16(Data, 0xC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xC); }
|
||||
|
||||
public override bool IV3 => (sbyte)Data[0xE] < 0 && (sbyte)Data[0xE] + 1 == -3;
|
||||
public override int IV_HP { get; set; } = -1;
|
||||
public override int IV_ATK { get; set; } = -1;
|
||||
public override int IV_DEF { get; set; } = -1;
|
||||
public override int IV_SPE { get; set; } = -1;
|
||||
public override int IV_SPA { get; set; } = -1;
|
||||
public override int IV_SPD { get; set; } = -1;
|
||||
}
|
||||
}
|
||||
74
pkNX.Structures/Encounter/EncounterGift7b.cs
Normal file
74
pkNX.Structures/Encounter/EncounterGift7b.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class EncounterGift7b : EncounterGift
|
||||
{
|
||||
private const int SIZE = 0x20;
|
||||
public EncounterGift7b(byte[] data) => Data = data ?? new byte[SIZE];
|
||||
|
||||
public ulong Hash => BitConverter.ToUInt64(Data, 0);
|
||||
|
||||
public override int Species
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, 0x08);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08);
|
||||
}
|
||||
|
||||
public override int Form { get => Data[0x0A]; set => Data[0x0A] = (byte)value; }
|
||||
public override int Level { get => Data[0x0B]; set => Data[0x0B] = (byte)value; }
|
||||
|
||||
public override Shiny Shiny // 0 = Random, 1 = Always, 2 = Never
|
||||
{
|
||||
get => (Shiny)(Data[0x0C] & 3);
|
||||
set => Data[0x0C] = (byte)((Data[0x0C] & ~3) | ((byte)value & 3));
|
||||
}
|
||||
|
||||
public override FixedGender Gender // 0 = Random, 1 = Male, 2 = Female, 3 = Panic
|
||||
{
|
||||
get => (FixedGender)(Data[0x0D] & 3);
|
||||
set => Data[0x0D] = (byte)((Data[0x0D] & ~3) | ((byte)value & 3));
|
||||
}
|
||||
|
||||
public override Nature Nature { get => (Nature)Data[0x0E]; set => Data[0x0E] = (byte)value; } // 25 = random (sets the nature rand to 1)
|
||||
public override int Ability { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } // 0 = rand
|
||||
|
||||
public int SpecialMove // sub_71002B7AD0 checks nonzero, pushes move
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, 0x10);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x10);
|
||||
}
|
||||
|
||||
public override int IV_HP { get => (sbyte)Data[0x12]; set => Data[0x12] = (byte)value; }
|
||||
public override int IV_ATK { get => (sbyte)Data[0x13]; set => Data[0x13] = (byte)value; }
|
||||
public override int IV_DEF { get => (sbyte)Data[0x14]; set => Data[0x14] = (byte)value; }
|
||||
public override int IV_SPA { get => (sbyte)Data[0x15]; set => Data[0x15] = (byte)value; }
|
||||
public override int IV_SPD { get => (sbyte)Data[0x16]; set => Data[0x16] = (byte)value; }
|
||||
public override int IV_SPE { get => (sbyte)Data[0x17]; set => Data[0x17] = (byte)value; }
|
||||
|
||||
public int AV_HP { get => (sbyte)Data[0x18]; set => Data[0x18] = (byte)value; }
|
||||
public int AV_ATK { get => (sbyte)Data[0x19]; set => Data[0x19] = (byte)value; }
|
||||
public int AV_DEF { get => (sbyte)Data[0x1A]; set => Data[0x1A] = (byte)value; }
|
||||
public int AV_SPA { get => (sbyte)Data[0x1B]; set => Data[0x1B] = (byte)value; }
|
||||
public int AV_SPD { get => (sbyte)Data[0x1C]; set => Data[0x1C] = (byte)value; }
|
||||
public int AV_SPE { get => (sbyte)Data[0x1D]; set => Data[0x1D] = (byte)value; }
|
||||
|
||||
public override int[] RelearnMoves
|
||||
{
|
||||
get => new int[]
|
||||
{
|
||||
BitConverter.ToUInt16(Data, 0x18),
|
||||
BitConverter.ToUInt16(Data, 0x1A),
|
||||
BitConverter.ToUInt16(Data, 0x1C),
|
||||
BitConverter.ToUInt16(Data, 0x1E),
|
||||
};
|
||||
set
|
||||
{
|
||||
if (value?.Length != 4)
|
||||
return;
|
||||
for (int i = 0; i < 4; i++)
|
||||
BitConverter.GetBytes((ushort)value[i]).CopyTo(Data, 0x18 + (i * 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
pkNX.Structures/Encounter/EncounterStatic.cs
Normal file
95
pkNX.Structures/Encounter/EncounterStatic.cs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public abstract class EncounterStatic
|
||||
{
|
||||
protected byte[] Data { get; set; }
|
||||
public virtual byte[] Write() => (byte[])Data.Clone();
|
||||
|
||||
public abstract int Species { get; set; }
|
||||
public virtual int HeldItem { get; set; }
|
||||
public abstract int Level { get; set; }
|
||||
public abstract int Form { get; set; }
|
||||
public abstract FixedGender Gender { get; set; }
|
||||
|
||||
public virtual Nature Nature { get; set; } = Nature.Random;
|
||||
public virtual int Ability { get; set; }
|
||||
public virtual bool ShinyLock { get; set; }
|
||||
|
||||
public virtual bool IV3 { get; set; }
|
||||
public virtual int[] RelearnMoves { get; set; } = Array.Empty<int>();
|
||||
public abstract Shiny Shiny { get; set; }
|
||||
|
||||
public virtual int IV_HP { get; set; } = -1;
|
||||
public virtual int IV_ATK { get; set; } = -1;
|
||||
public virtual int IV_DEF { get; set; } = -1;
|
||||
public virtual int IV_SPE { get; set; } = -1;
|
||||
public virtual int IV_SPA { get; set; } = -1;
|
||||
public virtual int IV_SPD { get; set; } = -1;
|
||||
public virtual int EV_HP { get; set; }
|
||||
public virtual int EV_ATK { get; set; }
|
||||
public virtual int EV_DEF { get; set; }
|
||||
public virtual int EV_SPE { get; set; }
|
||||
public virtual int EV_SPA { get; set; }
|
||||
public virtual int EV_SPD { get; set; }
|
||||
|
||||
public int[] IVs
|
||||
{
|
||||
get => new[] { IV_HP, IV_ATK, IV_DEF, IV_SPE, IV_SPA, IV_SPD };
|
||||
set
|
||||
{
|
||||
if (value?.Length != 6) return;
|
||||
IV_HP = value[0]; IV_ATK = value[1]; IV_DEF = value[2];
|
||||
IV_SPE = value[3]; IV_SPA = value[4]; IV_SPD = value[5];
|
||||
}
|
||||
}
|
||||
|
||||
public int[] EVs
|
||||
{
|
||||
get => new[] { EV_HP, EV_ATK, EV_DEF, EV_SPE, EV_SPA, EV_SPD };
|
||||
set
|
||||
{
|
||||
if (value?.Length != 6) return;
|
||||
EV_HP = value[0]; EV_ATK = value[1]; EV_DEF = value[2];
|
||||
EV_SPE = value[3]; EV_SPA = value[4]; EV_SPD = value[5];
|
||||
}
|
||||
}
|
||||
|
||||
public string GetSummary()
|
||||
{
|
||||
var str = $"new EncounterStatic {{ Species = {Species:000}, Level = {Level:00}, Location = -01, ";
|
||||
if (Ability != 0)
|
||||
str += $"Ability = {1 << (Ability - 1)}, ";
|
||||
if (ShinyLock)
|
||||
str += "Shiny = false, ";
|
||||
|
||||
if (IV3)
|
||||
{
|
||||
str += "IV3 = true, ";
|
||||
}
|
||||
else if (IVs.Any(z => z >= 0))
|
||||
{
|
||||
var iv = IVs.Select(z => z >= 0 ? $"{z:00}" : "-1");
|
||||
str += $"IVs = new[] {{{string.Join(",", iv)}}}, ";
|
||||
}
|
||||
if (RelearnMoves.Any(z => z != 0))
|
||||
{
|
||||
var mv = RelearnMoves.Select(z => $"{z:000}");
|
||||
str += $"Relearn = new[] {{{string.Join(",", mv)}}}, ";
|
||||
}
|
||||
if (Form != 0)
|
||||
str += $"Form = {Form}, ";
|
||||
if (Gender != 0)
|
||||
str += $"Gender = {Gender - 1}, ";
|
||||
if (HeldItem > 0)
|
||||
str += $"HeldItem = {HeldItem}, ";
|
||||
if (Nature >= 0 && Nature < Nature.Random25)
|
||||
str += $"Nature = {Nature - 1}, ";
|
||||
|
||||
str += " },";
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
pkNX.Structures/Encounter/EncounterStatic6.cs
Normal file
50
pkNX.Structures/Encounter/EncounterStatic6.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures.Encounter
|
||||
{
|
||||
public sealed class EncounterStatic6 : EncounterStatic
|
||||
{
|
||||
private const int SIZE = 0xC;
|
||||
public EncounterStatic6(byte[] data) => Data = data ?? new byte[SIZE];
|
||||
|
||||
public override int Species { get => BitConverter.ToUInt16(Data, 0x0); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0); }
|
||||
public override int Form { get => Data[0x2]; set => Data[0x2] = (byte)value; }
|
||||
public override int Level { get => Data[0x3]; set => Data[0x3] = (byte)value; }
|
||||
|
||||
public override int HeldItem
|
||||
{
|
||||
get => Math.Max(0, (int)BitConverter.ToInt16(Data, 0x4));
|
||||
set => BitConverter.GetBytes((short)(value <= 0 ? -1 : value)).CopyTo(Data, 0x4);
|
||||
}
|
||||
|
||||
public override Shiny Shiny
|
||||
{
|
||||
get => (Shiny)(Data[0x6] & 3);
|
||||
set => Data[0x6] = (byte)((Data[0x6] & ~3) | ((byte)value & 3));
|
||||
}
|
||||
|
||||
public override FixedGender Gender
|
||||
{
|
||||
get => (FixedGender)((Data[0x6] & 0x0C) >> 2);
|
||||
set => Data[0x6] = (byte)((Data[0x6] & ~0xC) | (((byte)value & 3) << 2));
|
||||
}
|
||||
|
||||
public override int Ability
|
||||
{
|
||||
get => (Data[0x6] & 0x70) >> 4;
|
||||
set => Data[0x6] = (byte)((Data[0x6] & ~0x70) | ((value & 7) << 4));
|
||||
}
|
||||
|
||||
public override bool IV3
|
||||
{
|
||||
get => (Data[0x7] & 1) >> 0 == 1;
|
||||
set => Data[0x7] = (byte)((Data[0x7] & ~1) | (value ? 1 : 0));
|
||||
}
|
||||
|
||||
public bool IV3_1
|
||||
{
|
||||
get => (Data[0x7] & 2) >> 1 == 1;
|
||||
set => Data[0x7] = (byte)((Data[0x7] & ~2) | (value ? 2 : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
134
pkNX.Structures/Encounter/EncounterStatic7.cs
Normal file
134
pkNX.Structures/Encounter/EncounterStatic7.cs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public sealed class EncounterStatic7 : EncounterStatic
|
||||
{
|
||||
public const int SIZE = 0x38;
|
||||
public EncounterStatic7(byte[] data) => Data = data ?? new byte[SIZE];
|
||||
|
||||
public override int Species
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, 0x0);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0);
|
||||
}
|
||||
|
||||
public override int Form
|
||||
{
|
||||
get => Data[0x2];
|
||||
set => Data[0x2] = (byte)value;
|
||||
}
|
||||
|
||||
public override int Level
|
||||
{
|
||||
get => Data[0x3];
|
||||
set => Data[0x3] = (byte)value;
|
||||
}
|
||||
|
||||
public override int HeldItem
|
||||
{
|
||||
get => Math.Max(0, (int)BitConverter.ToInt16(Data, 0x4));
|
||||
set => BitConverter.GetBytes((short)(value <= 0 ? -1 : value)).CopyTo(Data, 0x4);
|
||||
}
|
||||
|
||||
public override Shiny Shiny
|
||||
{
|
||||
get => (Shiny) (Data[0x6] & 3);
|
||||
set => Data[0x6] = (byte)((Data[0x6] & ~3) | ((byte)value & 3));
|
||||
}
|
||||
|
||||
public override FixedGender Gender
|
||||
{
|
||||
get => (FixedGender)((Data[0x6] & 0x0C) >> 2);
|
||||
set => Data[0x6] = (byte)((Data[0x6] & ~0xC) | (((byte)value & 3) << 2));
|
||||
}
|
||||
|
||||
public override int Ability
|
||||
{
|
||||
get => (Data[0x6] & 0x70) >> 4;
|
||||
set => Data[0x6] = (byte)((Data[0x6] & ~0x70) | ((value & 7) << 4));
|
||||
}
|
||||
|
||||
public bool _7
|
||||
{
|
||||
get => (Data[0x7] & 1) >> 0 == 1;
|
||||
set => Data[0x7] = (byte)((Data[0x7] & ~1) | (value ? 1 : 0));
|
||||
}
|
||||
|
||||
public bool _7_1
|
||||
{
|
||||
get => (Data[0x7] & 2) >> 1 == 1;
|
||||
set => Data[0x7] = (byte)((Data[0x7] & ~2) | (value ? 2 : 0));
|
||||
}
|
||||
|
||||
public int Map
|
||||
{
|
||||
get => BitConverter.ToInt16(Data, 0x8) - 1;
|
||||
set => BitConverter.GetBytes((short)(value + 1)).CopyTo(Data, 0x8);
|
||||
}
|
||||
|
||||
public override int[] RelearnMoves
|
||||
{
|
||||
get => new int[]
|
||||
{
|
||||
BitConverter.ToUInt16(Data, 0xC),
|
||||
BitConverter.ToUInt16(Data, 0xE),
|
||||
BitConverter.ToUInt16(Data, 0x10),
|
||||
BitConverter.ToUInt16(Data, 0x12),
|
||||
};
|
||||
set
|
||||
{
|
||||
if (value.Length != 4)
|
||||
return;
|
||||
for (int i = 0; i < 4; i++)
|
||||
BitConverter.GetBytes((ushort)value[i]).CopyTo(Data, 0xC + (i * 2));
|
||||
}
|
||||
}
|
||||
|
||||
public override Nature Nature
|
||||
{
|
||||
get => (Nature)Data[0x14];
|
||||
set => Data[0x14] = (byte)value;
|
||||
}
|
||||
|
||||
public override int IV_HP { get => (sbyte)Data[0x15]; set => Data[0x15] = (byte)value; }
|
||||
public override int IV_ATK { get => (sbyte)Data[0x16]; set => Data[0x16] = (byte)value; }
|
||||
public override int IV_DEF { get => (sbyte)Data[0x17]; set => Data[0x17] = (byte)value; }
|
||||
public override int IV_SPA { get => (sbyte)Data[0x18]; set => Data[0x18] = (byte)value; }
|
||||
public override int IV_SPD { get => (sbyte)Data[0x19]; set => Data[0x19] = (byte)value; }
|
||||
public override int IV_SPE { get => (sbyte)Data[0x1A]; set => Data[0x1A] = (byte)value; }
|
||||
|
||||
public override int EV_HP { get => (sbyte)Data[0x1B]; set => Data[0x1B] = (byte)value; }
|
||||
public override int EV_ATK { get => (sbyte)Data[0x1C]; set => Data[0x1C] = (byte)value; }
|
||||
public override int EV_DEF { get => (sbyte)Data[0x1D]; set => Data[0x1D] = (byte)value; }
|
||||
public override int EV_SPA { get => (sbyte)Data[0x1E]; set => Data[0x1E] = (byte)value; }
|
||||
public override int EV_SPD { get => (sbyte)Data[0x1F]; set => Data[0x1F] = (byte)value; }
|
||||
public override int EV_SPE { get => (sbyte)Data[0x20]; set => Data[0x20] = (byte)value; }
|
||||
|
||||
public int Aura
|
||||
{
|
||||
get => Data[0x25];
|
||||
set => Data[0x25] = (byte)value;
|
||||
}
|
||||
|
||||
public int Allies
|
||||
{
|
||||
get => Data[0x27];
|
||||
set => Data[0x27] = (byte)value;
|
||||
}
|
||||
|
||||
public int Ally1
|
||||
{
|
||||
get => Data[0x28];
|
||||
set => Data[0x28] = (byte)value;
|
||||
}
|
||||
|
||||
public int Ally2
|
||||
{
|
||||
get => Data[0x2C];
|
||||
set => Data[0x2C] = (byte)value;
|
||||
}
|
||||
|
||||
public override bool IV3 => (sbyte)Data[0x15] < 0 && (sbyte)Data[0x15] + 1 == -3;
|
||||
}
|
||||
}
|
||||
110
pkNX.Structures/Encounter/EncounterStatic7b.cs
Normal file
110
pkNX.Structures/Encounter/EncounterStatic7b.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace pkNX.Structures.Encounter
|
||||
{
|
||||
public sealed class EncounterStatic7b : EncounterStatic
|
||||
{
|
||||
private const int SIZE = 0x40;
|
||||
public EncounterStatic7b(byte[] data) => Data = data ?? new byte[SIZE];
|
||||
|
||||
public ulong Hash => BitConverter.ToUInt64(Data, 0);
|
||||
|
||||
public override int Species
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, 0x08);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08);
|
||||
}
|
||||
|
||||
public override int Form { get => Data[0x0A]; set => Data[0x0A] = (byte)value; }
|
||||
public override int Level { get => Data[0x0B]; set => Data[0x0B] = (byte)value; }
|
||||
|
||||
public override Shiny Shiny // 0 = 0x3..., 1 = 0x2, 2 = 0x1
|
||||
{
|
||||
get => (Shiny)(Data[0x0C] & 3);
|
||||
set => Data[0x0C] = (byte)((Data[0x0C] & ~3) | ((byte)value & 3));
|
||||
}
|
||||
|
||||
public override FixedGender Gender // 0 = Random, 1 = Male, 2 = Female, 3 = Panic
|
||||
{
|
||||
get => (FixedGender)(Data[0x0D] & 3);
|
||||
set => Data[0x0D] = (byte)((Data[0x0D] & ~3) | ((byte)value & 3));
|
||||
}
|
||||
|
||||
public override Nature Nature { get => (Nature)Data[0x0E]; set => Data[0x0E] = (byte)value; } // 25 = random (sets the nature rand to 1)
|
||||
public override int Ability { get => Data[0x0F]; set => Data[0x0F] = (byte)value; }
|
||||
|
||||
public uint[] Ptrs // 0x10-0x1F -- are these text line references?
|
||||
{
|
||||
get => new[]
|
||||
{
|
||||
BitConverter.ToUInt32(Data, 0x10),
|
||||
BitConverter.ToUInt32(Data, 0x14),
|
||||
BitConverter.ToUInt32(Data, 0x18),
|
||||
BitConverter.ToUInt32(Data, 0x1C),
|
||||
};
|
||||
set { }
|
||||
}
|
||||
|
||||
public override int[] RelearnMoves // 0x20-0x27 -- these are actually just moves
|
||||
{
|
||||
get => new int[]
|
||||
{
|
||||
BitConverter.ToUInt16(Data, 0x20),
|
||||
BitConverter.ToUInt16(Data, 0x22),
|
||||
BitConverter.ToUInt16(Data, 0x24),
|
||||
BitConverter.ToUInt16(Data, 0x26),
|
||||
};
|
||||
set
|
||||
{
|
||||
if (value?.Length != 4)
|
||||
return;
|
||||
for (int i = 0; i < 4; i++)
|
||||
BitConverter.GetBytes((ushort)value[i]).CopyTo(Data, 0x20 + (i * 2));
|
||||
}
|
||||
}
|
||||
|
||||
public int V1
|
||||
{
|
||||
get => BitConverter.ToInt16(Data, 0x28);
|
||||
set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x28);
|
||||
}
|
||||
|
||||
public int V2
|
||||
{
|
||||
get => BitConverter.ToInt16(Data, 0x2A);
|
||||
set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x2A);
|
||||
}
|
||||
|
||||
// 0x2C-0x31
|
||||
public override int IV_HP { get => (sbyte)Data[0x2C]; set => Data[0x2C] = (byte)value; }
|
||||
public override int IV_ATK { get => (sbyte)Data[0x2D]; set => Data[0x2D] = (byte)value; }
|
||||
public override int IV_DEF { get => (sbyte)Data[0x2E]; set => Data[0x2E] = (byte)value; }
|
||||
public override int IV_SPA { get => (sbyte)Data[0x2F]; set => Data[0x2F] = (byte)value; }
|
||||
public override int IV_SPD { get => (sbyte)Data[0x30]; set => Data[0x30] = (byte)value; }
|
||||
public override int IV_SPE { get => (sbyte)Data[0x31]; set => Data[0x31] = (byte)value; }
|
||||
|
||||
// 0x32-0x37
|
||||
public override int EV_HP { get => (sbyte)Data[0x32]; set => Data[0x32] = (byte)value; }
|
||||
public override int EV_ATK { get => (sbyte)Data[0x33]; set => Data[0x33] = (byte)value; }
|
||||
public override int EV_DEF { get => (sbyte)Data[0x34]; set => Data[0x34] = (byte)value; }
|
||||
public override int EV_SPA { get => (sbyte)Data[0x35]; set => Data[0x35] = (byte)value; }
|
||||
public override int EV_SPD { get => (sbyte)Data[0x36]; set => Data[0x36] = (byte)value; }
|
||||
public override int EV_SPE { get => (sbyte)Data[0x37]; set => Data[0x37] = (byte)value; }
|
||||
|
||||
// Stat Boost levels/flags
|
||||
public int Boost_ATK { get => (sbyte)Data[0x38]; set => Data[0x38] = (byte)value; }
|
||||
public int Boost_DEF { get => (sbyte)Data[0x39]; set => Data[0x39] = (byte)value; }
|
||||
public int Boost_SPA { get => (sbyte)Data[0x3A]; set => Data[0x3A] = (byte)value; }
|
||||
public int Boost_SPD { get => (sbyte)Data[0x3B]; set => Data[0x3B] = (byte)value; }
|
||||
public int Boost_SPE { get => (sbyte)Data[0x3C]; set => Data[0x3C] = (byte)value; }
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3)]
|
||||
public byte[] UnknownFlags; // 0x3D-0x3F
|
||||
|
||||
public string Dump()
|
||||
{
|
||||
return $"new EncounterStatic {{ Species = {Species:000}, Level = {Level:00}, Location = -1, Ability = {Ability:0}, Shiny = Shiny.{Shiny} }},";
|
||||
}
|
||||
}
|
||||
}
|
||||
42
pkNX.Structures/Encounter/EncounterTrade.cs
Normal file
42
pkNX.Structures/Encounter/EncounterTrade.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public abstract class EncounterTrade
|
||||
{
|
||||
protected byte[] Data { get; set; }
|
||||
public virtual byte[] Write() => (byte[])Data.Clone();
|
||||
|
||||
public abstract int Species { get; set; }
|
||||
public virtual int HeldItem { get; set; }
|
||||
public abstract int Level { get; set; }
|
||||
public abstract int Form { get; set; }
|
||||
public abstract FixedGender Gender { get; set; }
|
||||
|
||||
public virtual Nature Nature { get; set; } = Nature.Random;
|
||||
public virtual int Ability { get; set; }
|
||||
public virtual bool ShinyLock { get; set; }
|
||||
|
||||
public virtual bool IV3 { get; set; }
|
||||
public virtual int[] RelearnMoves { get; set; } = Array.Empty<int>();
|
||||
public abstract Shiny Shiny { get; set; }
|
||||
|
||||
public abstract int IV_HP { get; set; }
|
||||
public abstract int IV_ATK { get; set; }
|
||||
public abstract int IV_DEF { get; set; }
|
||||
public abstract int IV_SPE { get; set; }
|
||||
public abstract int IV_SPA { get; set; }
|
||||
public abstract int IV_SPD { get; set; }
|
||||
|
||||
public int[] IVs
|
||||
{
|
||||
get => new[] { IV_HP, IV_ATK, IV_DEF, IV_SPE, IV_SPA, IV_SPD };
|
||||
set
|
||||
{
|
||||
if (value?.Length != 6) return;
|
||||
IV_HP = value[0]; IV_ATK = value[1]; IV_DEF = value[2];
|
||||
IV_SPE = value[3]; IV_SPA = value[4]; IV_SPD = value[5];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
pkNX.Structures/Encounter/EncounterTrade7b.cs
Normal file
50
pkNX.Structures/Encounter/EncounterTrade7b.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class EncounterTrade7b : EncounterTrade
|
||||
{
|
||||
private const int SIZE = 0x58;
|
||||
public EncounterTrade7b(byte[] data) => Data = data ?? new byte[SIZE];
|
||||
|
||||
public ulong Hash1 => BitConverter.ToUInt64(Data, 0x00);
|
||||
public ulong Hash2 => BitConverter.ToUInt64(Data, 0x30);
|
||||
public ulong Hash3 => BitConverter.ToUInt64(Data, 0x50);
|
||||
|
||||
public override int Species
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, 0x08);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08);
|
||||
}
|
||||
|
||||
public override int Form { get => Data[0x0A]; set => Data[0x0A] = (byte)value; }
|
||||
public override int Level { get => Data[0x0C]; set => Data[0x0C] = (byte)value; } // minimum level?
|
||||
|
||||
public override Shiny Shiny // 0 = 0x3..., 1 = 0x2, 2 = 0x1
|
||||
{
|
||||
get => (Shiny)(Data[0x0D] & 3);
|
||||
set => Data[0x0D] = (byte)((Data[0x0D] & ~3) | ((byte)value & 3));
|
||||
}
|
||||
|
||||
public override FixedGender Gender // 0 = Random, 1 = Male, 2 = Female, 3 = Panic
|
||||
{
|
||||
get => (FixedGender)(Data[0x0D] & 3);
|
||||
set => Data[0x0D] = (byte)((Data[0x0D] & ~3) | ((byte)value & 3));
|
||||
}
|
||||
|
||||
public int RequiredSpecies
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, 0x42);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x42);
|
||||
}
|
||||
|
||||
public int RequiredForm { get => Data[0x44]; set => Data[0x44] = (byte)value; }
|
||||
|
||||
public override int IV_HP { get => (sbyte)Data[0x46]; set => Data[0x46] = (byte)value; }
|
||||
public override int IV_ATK { get => (sbyte)Data[0x47]; set => Data[0x47] = (byte)value; }
|
||||
public override int IV_DEF { get => (sbyte)Data[0x48]; set => Data[0x48] = (byte)value; }
|
||||
public override int IV_SPA { get => (sbyte)Data[0x49]; set => Data[0x49] = (byte)value; }
|
||||
public override int IV_SPD { get => (sbyte)Data[0x4A]; set => Data[0x4A] = (byte)value; }
|
||||
public override int IV_SPE { get => (sbyte)Data[0x4B]; set => Data[0x4B] = (byte)value; }
|
||||
}
|
||||
}
|
||||
11
pkNX.Structures/Encounter/FixedGender.cs
Normal file
11
pkNX.Structures/Encounter/FixedGender.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace pkNX.Structures
|
||||
{
|
||||
public enum FixedGender : byte
|
||||
{
|
||||
Random = 0,
|
||||
Male = 1,
|
||||
Female = 2,
|
||||
|
||||
Genderless = Random,
|
||||
}
|
||||
}
|
||||
39
pkNX.Structures/Encounter/Nature.cs
Normal file
39
pkNX.Structures/Encounter/Nature.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
namespace pkNX.Structures
|
||||
{
|
||||
public enum Nature : sbyte
|
||||
{
|
||||
Random = -1,
|
||||
|
||||
Hardy,
|
||||
Lonely,
|
||||
Brave,
|
||||
Adamant,
|
||||
Naughty,
|
||||
Bold,
|
||||
|
||||
Docile,
|
||||
Relaxed,
|
||||
Impish,
|
||||
Lax,
|
||||
Timid,
|
||||
Hasty,
|
||||
|
||||
Serious,
|
||||
Jolly,
|
||||
Naive,
|
||||
Modest,
|
||||
Mild,
|
||||
Quiet,
|
||||
|
||||
Bashful,
|
||||
Rash,
|
||||
Calm,
|
||||
Gentle,
|
||||
Sassy,
|
||||
Careful,
|
||||
|
||||
Quirky,
|
||||
|
||||
Random25 = 25,
|
||||
}
|
||||
}
|
||||
20
pkNX.Structures/Encounter/Shiny.cs
Normal file
20
pkNX.Structures/Encounter/Shiny.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
namespace pkNX.Structures
|
||||
{
|
||||
public enum Shiny : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// PID is purely random; can be shiny or not shiny.
|
||||
/// </summary>
|
||||
Random = 0,
|
||||
|
||||
/// <summary>
|
||||
/// PID is randomly created and forced to be shiny.
|
||||
/// </summary>
|
||||
Always = 1,
|
||||
|
||||
/// <summary>
|
||||
/// PID is randomly created and forced to be not shiny.
|
||||
/// </summary>
|
||||
Never = 2,
|
||||
}
|
||||
}
|
||||
28
pkNX.Structures/Evolution/EvolutionMethod.cs
Normal file
28
pkNX.Structures/Evolution/EvolutionMethod.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
namespace pkNX.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// Criteria for evolving to another species.
|
||||
/// </summary>
|
||||
public class EvolutionMethod
|
||||
{
|
||||
public EvolutionType Method;
|
||||
public int Species;
|
||||
public int Argument;
|
||||
public int Form = -1;
|
||||
public int Level;
|
||||
|
||||
public EvolutionMethod Copy(int species = -1)
|
||||
{
|
||||
if (species < 0)
|
||||
species = Species;
|
||||
return new EvolutionMethod
|
||||
{
|
||||
Method = Method,
|
||||
Species = species,
|
||||
Argument = Argument,
|
||||
Form = Form,
|
||||
Level = Level
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
11
pkNX.Structures/Evolution/EvolutionSet.cs
Normal file
11
pkNX.Structures/Evolution/EvolutionSet.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace pkNX.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// Table of Evolution Branch Entries
|
||||
/// </summary>
|
||||
public abstract class EvolutionSet
|
||||
{
|
||||
public EvolutionMethod[] PossibleEvolutions;
|
||||
public abstract byte[] Write();
|
||||
}
|
||||
}
|
||||
56
pkNX.Structures/Evolution/EvolutionSet6.cs
Normal file
56
pkNX.Structures/Evolution/EvolutionSet6.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// Generation 6 Evolution Branch Entries
|
||||
/// </summary>
|
||||
public class EvolutionSet6 : EvolutionSet
|
||||
{
|
||||
private const int ENTRY_SIZE = 6;
|
||||
private const int ENTRY_COUNT = 8;
|
||||
public const int SIZE = ENTRY_COUNT * ENTRY_SIZE;
|
||||
private static readonly HashSet<int> argEvos = new HashSet<int> { 6, 8, 16, 17, 18, 19, 20, 21, 22, 29, 30, 32, 33, 34 };
|
||||
|
||||
public EvolutionSet6(byte[] data)
|
||||
{
|
||||
if (data.Length != SIZE)
|
||||
return;
|
||||
PossibleEvolutions = data.GetArray(GetEvo, SIZE);
|
||||
}
|
||||
|
||||
private static EvolutionMethod GetEvo(byte[] data, int offset)
|
||||
{
|
||||
var evo = new EvolutionMethod
|
||||
{
|
||||
Method = (EvolutionType)BitConverter.ToUInt16(data, offset + 0),
|
||||
Argument = BitConverter.ToUInt16(data, offset + 2),
|
||||
Species = BitConverter.ToUInt16(data, offset + 4),
|
||||
// Copy
|
||||
Level = BitConverter.ToUInt16(data, offset + 2),
|
||||
};
|
||||
|
||||
// Argument is used by both Level argument and Item/Move/etc. Clear if appropriate.
|
||||
if (argEvos.Contains((int)evo.Method))
|
||||
evo.Level = 0;
|
||||
return evo;
|
||||
}
|
||||
|
||||
public override byte[] Write()
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
using (BinaryWriter bw = new BinaryWriter(ms))
|
||||
{
|
||||
foreach (EvolutionMethod evo in PossibleEvolutions)
|
||||
{
|
||||
bw.Write((ushort)evo.Method);
|
||||
bw.Write((ushort)evo.Argument);
|
||||
bw.Write((ushort)evo.Species);
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
pkNX.Structures/Evolution/EvolutionSet7.cs
Normal file
48
pkNX.Structures/Evolution/EvolutionSet7.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class EvolutionSet7 : EvolutionSet
|
||||
{
|
||||
private const int ENTRY_SIZE = 8;
|
||||
private const int ENTRY_COUNT = 8;
|
||||
public const int SIZE = ENTRY_COUNT * ENTRY_SIZE;
|
||||
|
||||
public EvolutionSet7(byte[] data)
|
||||
{
|
||||
if (data.Length != SIZE)
|
||||
return;
|
||||
PossibleEvolutions = data.GetArray(GetEvo, SIZE);
|
||||
}
|
||||
|
||||
private static EvolutionMethod GetEvo(byte[] data, int offset)
|
||||
{
|
||||
return new EvolutionMethod
|
||||
{
|
||||
Method = (EvolutionType)BitConverter.ToUInt16(data, offset + 0),
|
||||
Argument = BitConverter.ToUInt16(data, offset + 2),
|
||||
Species = BitConverter.ToUInt16(data, offset + 4),
|
||||
Form = (sbyte)data[offset + 6],
|
||||
Level = data[offset + 7],
|
||||
};
|
||||
}
|
||||
|
||||
public override byte[] Write()
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
using (BinaryWriter bw = new BinaryWriter(ms))
|
||||
{
|
||||
foreach (EvolutionMethod evo in PossibleEvolutions)
|
||||
{
|
||||
bw.Write((ushort)evo.Method);
|
||||
bw.Write((ushort)evo.Argument);
|
||||
bw.Write((ushort)evo.Species);
|
||||
bw.Write((sbyte)evo.Form);
|
||||
bw.Write((byte)evo.Level);
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
pkNX.Structures/Evolution/EvolutionType.cs
Normal file
46
pkNX.Structures/Evolution/EvolutionType.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
namespace pkNX.Structures
|
||||
{
|
||||
public enum EvolutionType : byte
|
||||
{
|
||||
None,
|
||||
LevelUpFriendship,
|
||||
LevelUpFriendshipMorning,
|
||||
LevelUpFriendshipNight,
|
||||
LevelUp,
|
||||
Trade,
|
||||
TradeHeldItem,
|
||||
TradeSpecies,
|
||||
UseItem,
|
||||
LevelUpATK,
|
||||
LevelUpDEF,
|
||||
LevelUpAeqD,
|
||||
LevelUpECl5,
|
||||
LevelUpECgeq5,
|
||||
LevelUpNinjask,
|
||||
LevelUpShedinja,
|
||||
LevelUpBauty,
|
||||
UseItemMale,
|
||||
UseItemFemale,
|
||||
LevelUpHeldItemDay,
|
||||
LevelUpHeldItemNight,
|
||||
LevelUpKnowMove,
|
||||
LevelUpWithTeammate,
|
||||
LevelUpMale,
|
||||
LevelUpFemale,
|
||||
LevelUpElectric,
|
||||
LevelUpForest,
|
||||
LevelUpCold,
|
||||
LevelUpInverted,
|
||||
LevelUpAffection50MoveType,
|
||||
LevelUpFairy,
|
||||
LevelUpWeather,
|
||||
LevelUpMorning,
|
||||
LevelUpNight,
|
||||
LevelUpFormFemale1,
|
||||
UNUSED,
|
||||
LevelUpVersion,
|
||||
LevelUpVersionDay,
|
||||
LevelUpVersionNight,
|
||||
LevelUpSummit,
|
||||
}
|
||||
}
|
||||
37
pkNX.Structures/Evolution/SeedPokeTable.cs
Normal file
37
pkNX.Structures/Evolution/SeedPokeTable.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public sealed class SeedPokeTable
|
||||
{
|
||||
private readonly ushort[] Table;
|
||||
|
||||
public SeedPokeTable(byte[] data)
|
||||
{
|
||||
Table = new ushort[data.Length/2];
|
||||
for (int i = 0; i < Table.Length; i++)
|
||||
Table[i] = BitConverter.ToUInt16(data, i * 2);
|
||||
}
|
||||
|
||||
public ushort this[int index] => Table[index];
|
||||
|
||||
public byte[] Write()
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
foreach (var seed in Table)
|
||||
bw.Write(seed);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> Dump(string[] specNames)
|
||||
{
|
||||
return Table.Select((t, i) => $"{i:000}\t{specNames[i]}\t{specNames[t]}");
|
||||
}
|
||||
}
|
||||
}
|
||||
41
pkNX.Structures/Evolution/ZukanEvolutionTable.cs
Normal file
41
pkNX.Structures/Evolution/ZukanEvolutionTable.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public sealed class ZukanEvolutionTable
|
||||
{
|
||||
private const int SIZE = 0x14;
|
||||
private readonly ushort[][] Table;
|
||||
|
||||
public ZukanEvolutionTable(byte[] data)
|
||||
{
|
||||
if (data.Length % SIZE != 0)
|
||||
throw new ArgumentException(nameof(data) + " length should be a multiple of " + SIZE);
|
||||
|
||||
Table = new ushort[data.Length / SIZE][];
|
||||
for (int i = 0; i < Table.Length; i++)
|
||||
{
|
||||
var evos = new ushort[(SIZE / 2) - 1];
|
||||
for (int j = 0; j < evos.Length; j++)
|
||||
evos[i] = BitConverter.ToUInt16(data, (i * SIZE) + (j * 2));
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Write()
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
foreach (var t in Table)
|
||||
{
|
||||
foreach (var e in t)
|
||||
bw.Write(e);
|
||||
bw.Write(t.Count(x => x != 0));
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
253
pkNX.Structures/GameUtil.cs
Normal file
253
pkNX.Structures/GameUtil.cs
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using static pkNX.Structures.GameVersion;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for <see cref="GameVersion"/> logic.
|
||||
/// </summary>
|
||||
public static class GameUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// List of possible <see cref="GameVersion"/> values that are stored in PKM data.
|
||||
/// </summary>
|
||||
/// <remarks>Ordered roughly by most recent games first.</remarks>
|
||||
public static readonly GameVersion[] GameVersions = ((GameVersion[])Enum.GetValues(typeof(GameVersion))).Where(z => z < RB && z > 0).Reverse().ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the <see cref="GameVersion"/> value is a value used by the games or is an aggregate indicator.
|
||||
/// </summary>
|
||||
/// <param name="game">Game to check</param>
|
||||
public static bool IsValidSavedVersion(this GameVersion game) => 0 < game && game <= RB;
|
||||
|
||||
/// <summary>Determines the Version Grouping of an input Version ID</summary>
|
||||
/// <param name="Version">Version of which to determine the group</param>
|
||||
/// <returns>Version Group Identifier or Invalid if type cannot be determined.</returns>
|
||||
public static GameVersion GetMetLocationVersionGroup(GameVersion Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
// Sidegame
|
||||
case CXD:
|
||||
return CXD;
|
||||
case GO:
|
||||
return GO;
|
||||
|
||||
// Gen1
|
||||
case RBY:
|
||||
case RD:
|
||||
case BU:
|
||||
case YW:
|
||||
case GN:
|
||||
return RBY;
|
||||
|
||||
// Gen2
|
||||
case GS:
|
||||
case GD:
|
||||
case SV:
|
||||
case C:
|
||||
return GSC;
|
||||
|
||||
// Gen3
|
||||
case R:
|
||||
case S:
|
||||
return RS;
|
||||
case E:
|
||||
return E;
|
||||
case FR:
|
||||
case LG:
|
||||
return FR;
|
||||
|
||||
// Gen4
|
||||
case D:
|
||||
case P:
|
||||
return DP;
|
||||
case Pt:
|
||||
return Pt;
|
||||
case HG:
|
||||
case SS:
|
||||
return HGSS;
|
||||
|
||||
// Gen5
|
||||
case B:
|
||||
case W:
|
||||
return BW;
|
||||
case B2:
|
||||
case W2:
|
||||
return B2W2;
|
||||
|
||||
// Gen6
|
||||
case X:
|
||||
case Y:
|
||||
return XY;
|
||||
case OR:
|
||||
case AS:
|
||||
return ORAS;
|
||||
|
||||
// Gen7
|
||||
case SN:
|
||||
case MN:
|
||||
return SM;
|
||||
case US:
|
||||
case UM:
|
||||
return USUM;
|
||||
case GP:
|
||||
case GE:
|
||||
return GG;
|
||||
|
||||
default:
|
||||
return Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Version ID from the end of that Generation
|
||||
/// </summary>
|
||||
/// <param name="generation">Generation ID</param>
|
||||
/// <returns>Version ID from requested generation. If none, return <see cref="Invalid"/>.</returns>
|
||||
public static GameVersion GetVersion(int generation)
|
||||
{
|
||||
switch (generation)
|
||||
{
|
||||
case 1: return RBY;
|
||||
case 2: return C;
|
||||
case 3: return E;
|
||||
case 4: return SS;
|
||||
case 5: return W2;
|
||||
case 6: return AS;
|
||||
case 7: return UM;
|
||||
default:
|
||||
return Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Generation the <see cref="GameVersion"/> belongs to.
|
||||
/// </summary>
|
||||
/// <param name="game">Game to retrieve the generation for</param>
|
||||
/// <returns>Generation ID</returns>
|
||||
public static int GetGeneration(this GameVersion game)
|
||||
{
|
||||
if (Gen1.Contains(game)) return 1;
|
||||
if (Gen2.Contains(game)) return 2;
|
||||
if (Gen3.Contains(game)) return 3;
|
||||
if (Gen4.Contains(game)) return 4;
|
||||
if (Gen5.Contains(game)) return 5;
|
||||
if (Gen6.Contains(game)) return 6;
|
||||
if (Gen7.Contains(game)) return 7;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Generation the <see cref="GameVersion"/> belongs to.
|
||||
/// </summary>
|
||||
/// <param name="game">Game to retrieve the generation for</param>
|
||||
/// <returns>Generation ID</returns>
|
||||
public static int GetMaxSpeciesID(this GameVersion game)
|
||||
{
|
||||
if (Gen1.Contains(game)) return 151;
|
||||
if (Gen2.Contains(game)) return 251;
|
||||
if (Gen3.Contains(game)) return 384;
|
||||
if (Gen4.Contains(game)) return 493;
|
||||
if (Gen5.Contains(game)) return 649;
|
||||
if (Gen6.Contains(game)) return Legal.MaxSpeciesID_6;
|
||||
if (Gen7.Contains(game))
|
||||
{
|
||||
if (SM.Contains(game))
|
||||
return 802;
|
||||
if (USUM.Contains(game))
|
||||
return 807;
|
||||
return Legal.MaxSpeciesID_7_GG;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="g1"/> version (or subset versions) is equivalent to <see cref="g2"/>.
|
||||
/// </summary>
|
||||
/// <param name="g1">Version (set)</param>
|
||||
/// <param name="g2">Individual version</param>
|
||||
public static bool Contains(this GameVersion g1, int g2) => g1.Contains((GameVersion)g2);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="g1"/> version (or subset versions) is equivalent to <see cref="g2"/>.
|
||||
/// </summary>
|
||||
/// <param name="g1">Version (set)</param>
|
||||
/// <param name="g2">Individual version</param>
|
||||
public static bool Contains(this GameVersion g1, GameVersion g2)
|
||||
{
|
||||
if (g1 == g2 || g1 == Any)
|
||||
return true;
|
||||
|
||||
switch (g1)
|
||||
{
|
||||
case RB:
|
||||
return g2 == RD || g2 == BU || g2 == GN;
|
||||
case Stadium:
|
||||
case EventsGBGen1:
|
||||
case VCEvents:
|
||||
case RBY:
|
||||
return RB.Contains(g2) || g2 == YW;
|
||||
case Gen1:
|
||||
return RBY.Contains(g2) || g2 == Stadium || g2 == EventsGBGen1 || g2 == VCEvents;
|
||||
|
||||
case GS: return g2 == GD || g2 == SV;
|
||||
case Stadium2:
|
||||
case EventsGBGen2:
|
||||
case GSC:
|
||||
return GS.Contains(g2) || g2 == C;
|
||||
case Gen2:
|
||||
return GSC.Contains(g2) || g2 == Stadium2 || g2 == EventsGBGen2;
|
||||
case GBCartEraOnly:
|
||||
return g2 == Stadium || g2 == Stadium2 || g2 == EventsGBGen1 || g2 == EventsGBGen2;
|
||||
|
||||
case RS: return g2 == R || g2 == S;
|
||||
case RSE:
|
||||
return RS.Contains(g2) || g2 == E;
|
||||
case FRLG: return g2 == FR || g2 == LG;
|
||||
case COLO:
|
||||
case XD: return g2 == CXD;
|
||||
case CXD: return g2 == COLO || g2 == XD;
|
||||
case RSBOX: return RS.Contains(g2) || g2 == E || FRLG.Contains(g2);
|
||||
case Gen3:
|
||||
return RSE.Contains(g2) || FRLG.Contains(g2) || CXD.Contains(g2) || g2 == RSBOX;
|
||||
|
||||
case DP: return g2 == D || g2 == P;
|
||||
case HGSS: return g2 == HG || g2 == SS;
|
||||
case DPPt:
|
||||
return DP.Contains(g2) || g2 == Pt;
|
||||
case BATREV: return DP.Contains(g2) || g2 == Pt || HGSS.Contains(g2);
|
||||
case Gen4:
|
||||
return DPPt.Contains(g2) || HGSS.Contains(g2) || g2 == BATREV;
|
||||
|
||||
case BW: return g2 == B || g2 == W;
|
||||
case B2W2: return g2 == B2 || g2 == W2;
|
||||
case Gen5:
|
||||
return BW.Contains(g2) || B2W2.Contains(g2);
|
||||
|
||||
case XY: return g2 == X || g2 == Y;
|
||||
case ORAS: return g2 == OR || g2 == AS;
|
||||
case Gen6:
|
||||
return XY.Contains(g2) || ORAS.Contains(g2);
|
||||
|
||||
case SM:
|
||||
return g2 == SN || g2 == MN;
|
||||
case USUM:
|
||||
return g2 == US || g2 == UM;
|
||||
case GG:
|
||||
return g2 == GP || g2 == GE;
|
||||
case Gen7:
|
||||
return SM.Contains(g2) || USUM.Contains(g2) || GG.Contains(g2);
|
||||
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of possible <see cref="GameVersion"/> values within the provided <see cref="generation"/>.
|
||||
/// </summary>
|
||||
/// <param name="generation">Generation to look within</param>
|
||||
public static GameVersion[] GetVersionsInGeneration(int generation) => GameVersions.Where(z => z.GetGeneration() == generation).ToArray();
|
||||
}
|
||||
}
|
||||
456
pkNX.Structures/GameVersion.cs
Normal file
456
pkNX.Structures/GameVersion.cs
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
namespace pkNX.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// Game Version ID enum shared between actual Version IDs and lumped version groupings.
|
||||
/// </summary>
|
||||
public enum GameVersion
|
||||
{
|
||||
#region Indicators for method empty arguments & result indication. Not stored values.
|
||||
Invalid = -2,
|
||||
Any = -1,
|
||||
Unknown = 0,
|
||||
#endregion
|
||||
|
||||
// The following values are IDs stored within PKM data, and can also identify individual games.
|
||||
|
||||
#region Gen3
|
||||
/// <summary>
|
||||
/// Pokémon Sapphire (GBA)
|
||||
/// </summary>
|
||||
S = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby (GBA)
|
||||
/// </summary>
|
||||
R = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Emerald (GBA)
|
||||
/// </summary>
|
||||
E = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon FireRed (GBA)
|
||||
/// </summary>
|
||||
FR = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon LeafGreen (GBA)
|
||||
/// </summary>
|
||||
LG = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Colosseum & Pokémon XD (GameCube)
|
||||
/// </summary>
|
||||
CXD = 15,
|
||||
#endregion
|
||||
|
||||
#region Gen4
|
||||
/// <summary>
|
||||
/// Pokémon Diamond (NDS)
|
||||
/// </summary>
|
||||
D = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Pearl (NDS)
|
||||
/// </summary>
|
||||
P = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Platinum (NDS)
|
||||
/// </summary>
|
||||
Pt = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Heart Gold (NDS)
|
||||
/// </summary>
|
||||
HG = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Soul Silver (NDS)
|
||||
/// </summary>
|
||||
SS = 8,
|
||||
#endregion
|
||||
|
||||
#region Gen5
|
||||
/// <summary>
|
||||
/// Pokémon White (NDS)
|
||||
/// </summary>
|
||||
W = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black (NDS)
|
||||
/// </summary>
|
||||
B = 21,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon White 2 (NDS)
|
||||
/// </summary>
|
||||
W2 = 22,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black 2 (NDS)
|
||||
/// </summary>
|
||||
B2 = 23,
|
||||
#endregion
|
||||
|
||||
#region Gen6
|
||||
/// <summary>
|
||||
/// Pokémon X (3DS)
|
||||
/// </summary>
|
||||
X = 24,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Y (3DS)
|
||||
/// </summary>
|
||||
Y = 25,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Alpha Sapphire (3DS)
|
||||
/// </summary>
|
||||
AS = 26,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby (3DS)
|
||||
/// </summary>
|
||||
OR = 27,
|
||||
#endregion
|
||||
|
||||
#region Gen7
|
||||
/// <summary>
|
||||
/// Pokémon Sun (3DS)
|
||||
/// </summary>
|
||||
SN = 30,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Moon (3DS)
|
||||
/// </summary>
|
||||
MN = 31,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Sun (3DS)
|
||||
/// </summary>
|
||||
US = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Moon (3DS)
|
||||
/// </summary>
|
||||
UM = 33,
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Pokémon GO (Unused)
|
||||
/// </summary>
|
||||
GO = 34,
|
||||
|
||||
#region Virtual Console (3DS) Gen1
|
||||
/// <summary>
|
||||
/// Pokémon Red (3DS Virtual Console)
|
||||
/// </summary>
|
||||
RD = 35,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Green[JP]/Blue[INT] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
GN = 36,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Blue[JP] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
BU = 37,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Yellow [JP] (3DS Virtual Console)
|
||||
/// </summary>
|
||||
YW = 38,
|
||||
#endregion
|
||||
|
||||
#region Virtual Console (3DS) Gen2
|
||||
/// <summary>
|
||||
/// Pokémon Gold (3DS Virtual Console)
|
||||
/// </summary>
|
||||
GD = 39,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Silver (3DS Virtual Console)
|
||||
/// </summary>
|
||||
SV = 40,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Crystal (3DS Virtual Console)
|
||||
/// </summary>
|
||||
C = 41,
|
||||
#endregion
|
||||
|
||||
#region Nintendo Switch
|
||||
/// <summary>
|
||||
/// Pokémon Let's Go Pikachu (NX)
|
||||
/// </summary>
|
||||
GP = 42,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Let's Go Eevee (NX)
|
||||
/// </summary>
|
||||
GE = 43,
|
||||
#endregion
|
||||
|
||||
// The following values are not actually stored values in pkm data,
|
||||
// These values are assigned within PKHeX as properties for various logic branching.
|
||||
|
||||
#region Game Groupings (SaveFile type, roughly)
|
||||
/// <summary>
|
||||
/// Pokémon Red & Blue identifier.
|
||||
/// </summary>
|
||||
/// <seealso cref="RD"/>
|
||||
/// <seealso cref="GN"/>
|
||||
/// <seealso cref="BU"/>
|
||||
RB,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Red/Blue/Yello identifier.
|
||||
/// </summary>
|
||||
/// <see cref="RD"/>
|
||||
/// <see cref="GN"/>
|
||||
/// <see cref="BU"/>
|
||||
/// <see cref="YW"/>
|
||||
RBY,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Gold & Silver identifier.
|
||||
/// </summary>
|
||||
/// <see cref="GD"/>
|
||||
/// <see cref="SV"/>
|
||||
GS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Gold/Silver/Crystal identifier.
|
||||
/// </summary>
|
||||
/// <see cref="GD"/>
|
||||
/// <see cref="SV"/>
|
||||
/// <see cref="C"/>
|
||||
GSC,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby & Sapphire identifier.
|
||||
/// </summary>
|
||||
/// <see cref="R"/>
|
||||
/// <see cref="S"/>
|
||||
RS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ruby/Sapphire/Emerald identifier.
|
||||
/// </summary>
|
||||
/// <see cref="R"/>
|
||||
/// <see cref="S"/>
|
||||
/// <see cref="E"/>
|
||||
RSE,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon FireRed/LeafGreen identifier.
|
||||
/// </summary>
|
||||
/// <see cref="FR"/>
|
||||
/// <see cref="LG"/>
|
||||
FRLG,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Box Ruby & Sapphire identifier.
|
||||
/// </summary>
|
||||
RSBOX,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Colosseum identifier.
|
||||
/// </summary>
|
||||
/// <see cref="CXD"/>
|
||||
/// <remarks>Also used to mark Colosseum-only origin data as this game shares a version ID with <see cref="XD"/></remarks>
|
||||
COLO,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon XD identifier.
|
||||
/// </summary>
|
||||
/// <see cref="CXD"/>
|
||||
/// <remarks>Also used to mark XD-only origin data as this game shares a version ID with <see cref="COLO"/></remarks>
|
||||
XD,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Diamond & Pearl identifier.
|
||||
/// </summary>
|
||||
/// <see cref="D"/>
|
||||
/// <see cref="P"/>
|
||||
DP,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Diamond/Pearl/Platinum version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="D"/>
|
||||
/// <see cref="P"/>
|
||||
/// <see cref="Pt"/>
|
||||
DPPt,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Heart Gold & Soul Silver identifier.
|
||||
/// </summary>
|
||||
/// <see cref="HG"/>
|
||||
/// <see cref="SS"/>
|
||||
HGSS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Battle Revolution identifier.
|
||||
/// </summary>
|
||||
BATREV,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black & White version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="B"/>
|
||||
/// <see cref="W"/>
|
||||
BW,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Black 2 & White 2 version group.
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="B2"/>
|
||||
/// <see cref="W2"/>
|
||||
B2W2,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon X & Y
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="X"/>
|
||||
/// <see cref="Y"/>
|
||||
XY,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby & Alpha Sapphire Demo identifier.
|
||||
/// </summary>
|
||||
/// <see cref="ORAS"/>
|
||||
ORASDEMO,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Omega Ruby & Alpha Sapphire version group.
|
||||
/// </summary>
|
||||
/// <see cref="OR"/>
|
||||
/// <see cref="AS"/>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
ORAS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Sun & Moon
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="SN"/>
|
||||
/// <see cref="MN"/>
|
||||
SM,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Sun & Moon Demo
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="SN"/>
|
||||
/// <see cref="MN"/>
|
||||
SMDEMO,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Ultra Sun & Ultra Moon
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="US"/>
|
||||
/// <see cref="UM"/>
|
||||
USUM,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Let's Go (TBD)
|
||||
/// </summary>
|
||||
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
|
||||
/// <see cref="GP"/>
|
||||
/// <see cref="GE"/>
|
||||
GG,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 1 Games
|
||||
/// </summary>
|
||||
/// <see cref="RBY"/>
|
||||
Gen1,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 2 Games
|
||||
/// </summary>
|
||||
/// <see cref="GSC"/>
|
||||
Gen2,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 3 Games
|
||||
/// </summary>
|
||||
/// <see cref="RSE"/>
|
||||
/// <see cref="FRLG"/>
|
||||
Gen3,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 4 Games
|
||||
/// </summary>
|
||||
/// <see cref="DPPt"/>
|
||||
/// <see cref="HGSS"/>
|
||||
Gen4,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 5 Games
|
||||
/// </summary>
|
||||
/// <see cref="BW"/>
|
||||
/// <see cref="B2W2"/>
|
||||
Gen5,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 6 Games
|
||||
/// </summary>
|
||||
/// <see cref="XY"/>
|
||||
/// <see cref="ORAS"/>
|
||||
Gen6,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 7 Games
|
||||
/// </summary>
|
||||
/// <see cref="SM"/>
|
||||
/// <see cref="USUM"/>
|
||||
Gen7,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 1/2 Game Boy Cartridge Era Only
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Since the original run of <see cref="RBY"/> and <see cref="GSC"/> could not transfer to future games,
|
||||
/// any special encounters (event data) can only be allowed if the savedata originated from that era.
|
||||
/// </remarks>
|
||||
GBCartEraOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Stadium data origin identifier
|
||||
/// </summary>
|
||||
/// <see cref="GBCartEraOnly"/>
|
||||
Stadium,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Stadium 2 data origin identifier
|
||||
/// </summary>
|
||||
/// <see cref="GBCartEraOnly"/>
|
||||
Stadium2,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 1 Game Boy Cartridge Era Only data origin identifier
|
||||
/// </summary>
|
||||
EventsGBGen1,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 2 Game Boy Cartridge Era Only data origin identifier
|
||||
/// </summary>
|
||||
EventsGBGen2,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 1/2 3DS Virtual Console data origin identifier
|
||||
/// </summary>
|
||||
VCEvents,
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
14
pkNX.Structures/Item/BattlePocket.cs
Normal file
14
pkNX.Structures/Item/BattlePocket.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
[Flags]
|
||||
public enum BattlePocket : byte
|
||||
{
|
||||
None,
|
||||
Ball = 1 << 0,
|
||||
Boosts = 1 << 1,
|
||||
Restore = 1 << 2,
|
||||
Misc = 1 << 3,
|
||||
}
|
||||
}
|
||||
50
pkNX.Structures/Item/BattleStatusFlags.cs
Normal file
50
pkNX.Structures/Item/BattleStatusFlags.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
[Flags]
|
||||
public enum BattleStatusFlags : byte
|
||||
{
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Sleep
|
||||
/// </summary>
|
||||
SLP = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Poison
|
||||
/// </summary>
|
||||
PSN = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Burn
|
||||
/// </summary>
|
||||
BRN = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Freeze
|
||||
/// </summary>
|
||||
FRZ = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Paralysis
|
||||
/// </summary>
|
||||
PAR = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// Confusion
|
||||
/// </summary>
|
||||
CFZ = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// Infatuation
|
||||
/// </summary>
|
||||
INF = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Guard Spec.
|
||||
/// </summary>
|
||||
GSP = 1 << 7,
|
||||
}
|
||||
}
|
||||
132
pkNX.Structures/Item/Item.cs
Normal file
132
pkNX.Structures/Item/Item.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Item
|
||||
{
|
||||
public Item(byte[] data) => this = data.ToStructure<Item>();
|
||||
public byte[] Write() => this.ToBytes();
|
||||
private const string Battle = "Battle";
|
||||
private const string Field = "Field";
|
||||
private const string Mart = "Mart";
|
||||
private const string Heal = "Heal";
|
||||
|
||||
#region Structure
|
||||
private ushort Price;
|
||||
|
||||
[Category(Battle)]
|
||||
public byte HeldEffect { get; set; }
|
||||
|
||||
public byte HeldArgument { get; set; }
|
||||
public byte NaturalGiftEffect { get; set; }
|
||||
public byte FlingEffect { get; set; }
|
||||
public byte FlingPower { get; set; }
|
||||
public byte NaturalGiftPower { get; set; }
|
||||
public ushort Packed { get; set; }
|
||||
|
||||
[Category(Field), Description("Routine # to call when used; 0=unusable.")]
|
||||
public byte EffectField { get; set; }
|
||||
|
||||
[Category(Battle), Description("Routine # to call when used; 0=unusable.")]
|
||||
public byte EffectBattle { get; set; } // Battle Type
|
||||
|
||||
public byte _0xC { get; set; } // 0 or 1
|
||||
public byte _0xD { get; set; } // Classification (0-3 Battle, 4 Balls, 5 Mail)
|
||||
public byte Consumable { get; set; }
|
||||
public byte SortIndex { get; set; }
|
||||
public BattleStatusFlags CureInflict { get; set; } // Bitflags
|
||||
private byte Boost0; // Revive 1, Sacred Ash 3, Rare Candy 5, EvoStone 8, upper4 for BoostAtk
|
||||
private byte Boost1; // DEF, SPA
|
||||
private byte Boost2; // SPD, SPE
|
||||
private byte Boost3; // ACC, CRIT PPUpFlags
|
||||
public ItemFlags1 FunctionFlags0 { get; set; }
|
||||
public ItemFlags2 FunctionFlags1 { get; set; }
|
||||
|
||||
[Category(Field), Description("Adds EVs to the HP stat.")]
|
||||
public sbyte EVHP { get; set; }
|
||||
|
||||
[Category(Field), Description("Adds EVs to the Attack stat.")]
|
||||
public sbyte EVATK { get; set; }
|
||||
|
||||
[Category(Field), Description("Adds EVs to the Defense stat.")]
|
||||
public sbyte EVDEF { get; set; }
|
||||
|
||||
[Category(Field), Description("Adds EVs to the Speed stat.")]
|
||||
public sbyte EVSPE { get; set; }
|
||||
|
||||
[Category(Field), Description("Adds EVs to the Sp. Attack stat.")]
|
||||
public sbyte EVSPA { get; set; }
|
||||
|
||||
[Category(Field), Description("Adds EVs to the Sp. Defense stat.")]
|
||||
public sbyte EVSPD { get; set; }
|
||||
|
||||
[Category(Heal), Description("Determines the healing percent, or if a flat value is used."), RefreshProperties(RefreshProperties.All)]
|
||||
public Heal HealAmount { get; set; }
|
||||
|
||||
[Category(Field), Description("PP to be added to the move's current PP if used.")]
|
||||
public byte PPGain { get; set; }
|
||||
|
||||
public sbyte Friendship1 { get; set; }
|
||||
public sbyte Friendship2 { get; set; }
|
||||
public sbyte Friendship3 { get; set; }
|
||||
public byte _0x23, _0x24;
|
||||
#endregion
|
||||
|
||||
[Category(Mart), RefreshProperties(RefreshProperties.All)]
|
||||
public int BuyPrice { get => Price * 10; set => Price = (ushort)(value / 10); }
|
||||
|
||||
[Category(Mart), ReadOnly(true)]
|
||||
public int SellPrice { get => Price * 5; set => Price = (ushort)(value / 5); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int NaturalGiftType { get => Packed & 0x1F; set => Packed = (ushort)((NaturalGiftEffect & ~0x1F) | value); }
|
||||
|
||||
[Category(Battle)]
|
||||
public bool Flag1 { get => ((Packed >> 5) & 1) == 1; set => Packed = (ushort)((Packed & ~(1 << 5)) | ((value ? 1 : 0) << 5)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public bool Flag2 { get => ((Packed >> 6) & 1) == 1; set => Packed = (ushort)((Packed & ~(1 << 6)) | ((value ? 1 : 0) << 6)); }
|
||||
|
||||
[Category(Field)]
|
||||
public int PocketField { get => (Packed >> 7) & 0xF; set => Packed = (ushort)((Packed & 0xF87F) | ((value & 0xF) << 7)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public BattlePocket PocketBattle { get => (BattlePocket)(Packed >> 11); set => Packed = (ushort)((Packed & 0x077F) | (((byte)value & 0x1F) << 11)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int FieldEffect { get => Boost0 & 0xF; set => Boost0 = (byte)((Boost0 & ~0xF) | (value & 0xF)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int BoostATK { get => Boost0 >> 4; set => Boost0 = (byte)((Boost0 & 0xF) | (value << 4)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int BoostDEF { get => Boost1 & 0xF; set => Boost1 = (byte)((Boost1 & ~0xF) | (value & 0xF)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int BoostSPA { get => Boost1 >> 4; set => Boost1 = (byte)((Boost1 & 0xF) | (value << 4)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int BoostSPD { get => Boost2 & 0xF; set => Boost2 = (byte)((Boost2 & ~0xF) | (value & 0xF)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int BoostSPE { get => Boost2 >> 4; set => Boost2 = (byte)((Boost2 & 0xF) | (value << 4)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int BoostACC { get => Boost0 & 0xF; set => Boost0 = (byte)((Boost3 & ~0xF) | (value & 0xF)); }
|
||||
|
||||
[Category(Battle)]
|
||||
public int BoostCRIT { get => (Boost3 >> 4) & 3; set => Boost3 = (byte)((Boost3 & ~0x30) | ((value & 3) << 4)); }
|
||||
|
||||
[Category(Battle), Description("1 = One PP Up, 3 = PP Max")]
|
||||
public int BoostPP { get => (Boost3 >> 6) & 3; set => Boost3 = (byte)((Boost3 & 0x3F) | ((value & 3) << 6)); }
|
||||
|
||||
[Category(Heal), Description("Raw value of the Heal enum."), RefreshProperties(RefreshProperties.All)]
|
||||
public int HealValue
|
||||
{
|
||||
get => (int)HealAmount;
|
||||
set => HealAmount = (Heal)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
pkNX.Structures/Item/ItemFlags1.cs
Normal file
18
pkNX.Structures/Item/ItemFlags1.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
[Flags]
|
||||
public enum ItemFlags1 : byte
|
||||
{
|
||||
None,
|
||||
RestorePP = 1 << 0,
|
||||
RestorePPAll = 1 << 1,
|
||||
RestoreHP = 1 << 2,
|
||||
AddEVHP = 1 << 3,
|
||||
AddEVAtk = 1 << 4,
|
||||
AddEVDef = 1 << 5,
|
||||
AddEVSpe = 1 << 6,
|
||||
AddEVSpA = 1 << 7,
|
||||
}
|
||||
}
|
||||
18
pkNX.Structures/Item/ItemFlags2.cs
Normal file
18
pkNX.Structures/Item/ItemFlags2.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
[Flags]
|
||||
public enum ItemFlags2 : byte
|
||||
{
|
||||
None,
|
||||
AddEVSpD = 1 << 0,
|
||||
AddEVAbove100 = 1 << 1,
|
||||
AddFriendship1 = 1 << 2,
|
||||
AddFriendship2 = 1 << 3,
|
||||
AddFriendship3 = 1 << 4,
|
||||
Unused1 = 1 << 5,
|
||||
Unused2 = 1 << 6,
|
||||
Unused3 = 1 << 7,
|
||||
}
|
||||
}
|
||||
82
pkNX.Structures/Learnset/Learnset.cs
Normal file
82
pkNX.Structures/Learnset/Learnset.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public abstract class Learnset
|
||||
{
|
||||
public int Count { get; protected set; }
|
||||
public int[] Moves { get; protected set; }
|
||||
public int[] Levels { get; protected set; }
|
||||
|
||||
public abstract byte[] Write();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the moves a Pokémon can learn between the specified level range.
|
||||
/// </summary>
|
||||
/// <param name="maxLevel">Maximum level</param>
|
||||
/// <param name="minLevel">Minimum level</param>
|
||||
/// <returns>Array of Move IDs</returns>
|
||||
public int[] GetMoves(int maxLevel, int minLevel = 0)
|
||||
{
|
||||
if (minLevel <= 1 && maxLevel >= 100)
|
||||
return Moves;
|
||||
if (minLevel > maxLevel)
|
||||
return Array.Empty<int>();
|
||||
int start = Array.FindIndex(Levels, z => z >= minLevel);
|
||||
if (start < 0)
|
||||
return Array.Empty<int>();
|
||||
int end = Array.FindLastIndex(Levels, z => z <= maxLevel);
|
||||
if (end < 0)
|
||||
return Array.Empty<int>();
|
||||
int[] result = new int[end - start + 1];
|
||||
Array.Copy(Moves, start, result, 0, result.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Returns the moves a Pokémon would have if it were encountered at the specified level.</summary>
|
||||
/// <remarks>In Generation 1, it is not possible to learn any moves lower than these encounter moves.</remarks>
|
||||
/// <param name="level">The level the Pokémon was encountered at.</param>
|
||||
/// <returns>Array of Move IDs</returns>
|
||||
public int[] GetEncounterMoves(int level)
|
||||
{
|
||||
const int count = 4;
|
||||
IList<int> moves = new int[count];
|
||||
int ctr = 0;
|
||||
for (int i = 0; i < Moves.Length; i++)
|
||||
{
|
||||
if (Levels[i] > level)
|
||||
break;
|
||||
int move = Moves[i];
|
||||
if (moves.Contains(move))
|
||||
continue;
|
||||
|
||||
moves[ctr++] = move;
|
||||
ctr &= 3;
|
||||
}
|
||||
return (int[])moves;
|
||||
}
|
||||
|
||||
/// <summary>Returns the index of the lowest level move if the Pokémon were encountered at the specified level.</summary>
|
||||
/// <remarks>Helps determine the minimum level an encounter can be at.</remarks>
|
||||
/// <param name="level">The level the Pokémon was encountered at.</param>
|
||||
/// <returns>Array of Move IDs</returns>
|
||||
public int GetMinMoveLevel(int level)
|
||||
{
|
||||
if (Levels.Length == 0)
|
||||
return 1;
|
||||
|
||||
int end = Array.FindLastIndex(Levels, z => z <= level);
|
||||
return Math.Max(end - 4, 1);
|
||||
}
|
||||
|
||||
/// <summary>Returns the level that a Pokémon can learn the specified move.</summary>
|
||||
/// <param name="move">Move ID</param>
|
||||
/// <returns>Level the move is learned at. If the result is below 0, it cannot be learned by levelup.</returns>
|
||||
public int GetLevelLearnMove(int move)
|
||||
{
|
||||
int index = Array.IndexOf(Moves, move);
|
||||
return index < 0 ? index : Levels[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
50
pkNX.Structures/Learnset/Learnset6.cs
Normal file
50
pkNX.Structures/Learnset/Learnset6.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public class Learnset6 : Learnset
|
||||
{
|
||||
public Learnset6(byte[] data)
|
||||
{
|
||||
if (data.Length < 4 || data.Length % 4 != 0)
|
||||
{ Count = 0; Levels = Moves = Array.Empty<int>(); return; }
|
||||
Count = (data.Length / 4) - 1;
|
||||
Moves = new int[Count];
|
||||
Levels = new int[Count];
|
||||
using (var ms = new MemoryStream(data))
|
||||
using (var br = new BinaryReader(ms))
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
Moves[i] = br.ReadInt16();
|
||||
Levels[i] = br.ReadInt16();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] Write()
|
||||
{
|
||||
Count = (ushort)Moves.Length;
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
using (BinaryWriter bw = new BinaryWriter(ms))
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
bw.Write((short)Moves[i]);
|
||||
bw.Write((short)Levels[i]);
|
||||
}
|
||||
bw.Write(-1);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static Learnset[] GetArray(byte[][] entries)
|
||||
{
|
||||
Learnset[] data = new Learnset[entries.Length];
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
data[i] = new Learnset6(entries[i]);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
pkNX.Structures/Legality/Encounters.cs
Normal file
19
pkNX.Structures/Legality/Encounters.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public static partial class Legal
|
||||
{
|
||||
public static readonly int[] BasicStarters_6 =
|
||||
{
|
||||
001, 004, 007, 010, 013, 016, 029, 032, 041, 043, 060, 063, 066, 069, 074, 081, 092, 111, 116, 137, 147, 152, 155, 158, 172, 173, 174, 175, 179, 187, 220, 239, 240, 246, 252, 255, 258,
|
||||
265, 270, 273, 280, 287, 293, 298, 304, 328, 355, 363, 371, 374, 387, 390, 393, 396, 403, 406, 440, 443, 495, 498, 501, 506, 519, 524, 532, 535, 540, 543, 551, 574, 577, 582, 599, 602,
|
||||
607, 610, 633, 650, 653, 656, 661, 664, 669, 679, 704
|
||||
};
|
||||
|
||||
public static readonly int[] BasicStarters_7 = BasicStarters_6.Concat(new[]
|
||||
{
|
||||
722, 725, 728, 731, 736, 761, 782, 789
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
98
pkNX.Structures/Legality/GameInfo.cs
Normal file
98
pkNX.Structures/Legality/GameInfo.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// Misc information pertaining to the game.
|
||||
/// </summary>
|
||||
public class GameInfo
|
||||
{
|
||||
public readonly GameVersion Game;
|
||||
public readonly int Generation;
|
||||
|
||||
public int MaxSpeciesID { get; private set; }
|
||||
public int MaxItemID { get; private set; }
|
||||
public int MaxMoveID { get; private set; }
|
||||
public ushort[] HeldItems { get; private set; }
|
||||
public int MaxAbilityID { get; private set; }
|
||||
|
||||
public bool XY { get; private set; }
|
||||
public bool AO { get; private set; }
|
||||
public bool SM { get; private set; }
|
||||
public bool USUM { get; private set; }
|
||||
public bool GG { get; private set; }
|
||||
|
||||
public GameInfo(GameVersion game)
|
||||
{
|
||||
Game = game;
|
||||
Generation = game.GetGeneration();
|
||||
GetInitMethod(game)();
|
||||
}
|
||||
|
||||
private Action GetInitMethod(GameVersion game)
|
||||
{
|
||||
switch (game)
|
||||
{
|
||||
case GameVersion.XY: return LoadXY;
|
||||
case GameVersion.ORASDEMO:
|
||||
case GameVersion.ORAS: return LoadAO;
|
||||
case GameVersion.SMDEMO:
|
||||
case GameVersion.SM: return LoadSM;
|
||||
case GameVersion.USUM: return LoadUSUM;
|
||||
case GameVersion.GG: return LoadGG;
|
||||
default:
|
||||
throw new ArgumentException(nameof(game));
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadXY()
|
||||
{
|
||||
XY = true;
|
||||
MaxSpeciesID = Legal.MaxSpeciesID_6;
|
||||
MaxMoveID = Legal.MaxMoveID_6_XY;
|
||||
MaxItemID = Legal.MaxItemID_6_XY;
|
||||
HeldItems = Legal.HeldItem_XY;
|
||||
MaxAbilityID = Legal.MaxAbilityID_6_XY;
|
||||
}
|
||||
|
||||
private void LoadAO()
|
||||
{
|
||||
AO = true;
|
||||
MaxSpeciesID = Legal.MaxSpeciesID_6;
|
||||
MaxMoveID = Legal.MaxMoveID_6_AO;
|
||||
MaxItemID = Legal.MaxItemID_6_AO;
|
||||
HeldItems = Legal.HeldItem_AO;
|
||||
MaxAbilityID = Legal.MaxAbilityID_6_AO;
|
||||
}
|
||||
|
||||
private void LoadSM()
|
||||
{
|
||||
SM = true;
|
||||
MaxSpeciesID = Legal.MaxSpeciesID_7_SM;
|
||||
MaxMoveID = Legal.MaxMoveID_7_SM;
|
||||
MaxItemID = Legal.MaxItemID_7_SM;
|
||||
HeldItems = Legal.HeldItems_SM;
|
||||
MaxAbilityID = Legal.MaxAbilityID_7_SM;
|
||||
}
|
||||
|
||||
private void LoadUSUM()
|
||||
{
|
||||
USUM = true;
|
||||
MaxSpeciesID = Legal.MaxSpeciesID_7_USUM;
|
||||
MaxMoveID = Legal.MaxMoveID_7_USUM;
|
||||
MaxItemID = Legal.MaxItemID_7_USUM;
|
||||
HeldItems = Legal.HeldItems_USUM;
|
||||
MaxAbilityID = Legal.MaxAbilityID_7_USUM;
|
||||
}
|
||||
|
||||
private void LoadGG()
|
||||
{
|
||||
GG = true;
|
||||
MaxSpeciesID = Legal.MaxSpeciesID_7_GG;
|
||||
MaxMoveID = Legal.MaxMoveID_7_GG;
|
||||
MaxItemID = Legal.MaxItemID_7_GG;
|
||||
HeldItems = new ushort[1];
|
||||
MaxAbilityID = Legal.MaxAbilityID_7_GG;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
pkNX.Structures/Legality/Item.cs
Normal file
46
pkNX.Structures/Legality/Item.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public static partial class Legal
|
||||
{
|
||||
private static readonly int[] Items_HeldXY =
|
||||
{
|
||||
/* 000, */ 001, 002, 003, 004, 005, 006, 007, 008, 009, 010, 011, 012, 013, 014, 015, 017, 018, 019, 020, 021, 022,
|
||||
023, 024, 025, 026, 027, 028, 029, 030, 031, 032, 033, 034, 035,
|
||||
036, 037, 038, 039, 040, 041, 042, 043, 044, 045, 046, 047, 048, 049, 050, 051, 052, 053, 054, 055, 056, 057,
|
||||
058, 059, 060, 061, 062, 063, 064, 065, 066, 067, 068, 069, 070,
|
||||
071, 072, 073, 074, 075, 076, 077, 078, 079, 080, 081, 082, 083, 084, 085, 086, 087, 088, 089, 090, 091, 092,
|
||||
093, 094, 099, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
|
||||
110, 112, 116, 117, 118, 119, 134, 135, 136, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
|
||||
162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
|
||||
175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
|
||||
197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
|
||||
210, 211, 212, 213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
|
||||
233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
|
||||
245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266,
|
||||
267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
|
||||
280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301,
|
||||
302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
|
||||
315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 504, 537, 538, 539, 540, 541, 542, 543, 544,
|
||||
545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557,
|
||||
558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 577, 580, 581, 582, 583, 584,
|
||||
585, 586, 587, 588, 589, 590, 591, 639, 640, 644, 645, 646, 647,
|
||||
648, 649, 650, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670,
|
||||
671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683,
|
||||
684, 685, 686, 687, 688, 699, 704, 708, 709, 710, 711, 715,
|
||||
};
|
||||
|
||||
private static readonly int[] Items_HeldAO = Items_HeldXY.Concat(new[]
|
||||
{
|
||||
534, 535,
|
||||
752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 767, 768, 769, 770,
|
||||
}).ToArray();
|
||||
|
||||
private static readonly int[] Items_Ball =
|
||||
{
|
||||
000, 001, 002, 003, 004, 005, 006, 007, 008, 009, 010, 011, 012,
|
||||
013, 014, 015, 016, 492, 493, 494, 495, 496, 497, 498, 499, 576,
|
||||
};
|
||||
}
|
||||
}
|
||||
58
pkNX.Structures/Legality/Legal.cs
Normal file
58
pkNX.Structures/Legality/Legal.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public static partial class Legal
|
||||
{
|
||||
internal static int GetRandomForme(int species, bool mega, bool alola, PersonalInfo[] stats = null)
|
||||
{
|
||||
if (stats == null)
|
||||
return 0;
|
||||
if (stats[species].FormeCount <= 1)
|
||||
return 0;
|
||||
|
||||
if (species == 658 && !mega) // Greninja
|
||||
return 0;
|
||||
if (species == 664 || species == 665 || species == 666) // vivillon
|
||||
return 30; // save file specific
|
||||
if (species == 774) // minior
|
||||
return Util.Rand.Next(7);
|
||||
|
||||
if (alola && EvolveToAlolanForms.Contains(species))
|
||||
return Util.Rand.Next(2);
|
||||
if (!BattleExclusiveForms.Contains(species) || mega)
|
||||
return Util.Rand.Next(stats[species].FormeCount); // Slot-Random
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies the current level with a scaling factor, returning a modified level.
|
||||
/// </summary>
|
||||
/// <param name="level">Current Level.</param>
|
||||
/// <param name="factor">Modification factor.</param>
|
||||
/// <returns>Boosted (or reduced) level.</returns>
|
||||
internal static int GetModifiedLevel(int level, decimal factor)
|
||||
{
|
||||
int newlvl = (int)(level * factor);
|
||||
if (newlvl < 1)
|
||||
return 1;
|
||||
if (newlvl > 100)
|
||||
return 100;
|
||||
return newlvl;
|
||||
}
|
||||
|
||||
internal static int[] GetRandomItemList(GameVersion game)
|
||||
{
|
||||
if (GameVersion.ORAS.Contains(game) || game == GameVersion.ORASDEMO)
|
||||
return Items_HeldAO.Concat(Items_Ball).Where(i => i != 0).ToArray();
|
||||
|
||||
if (GameVersion.XY.Contains(game))
|
||||
return Items_HeldXY.Concat(Items_Ball).Where(i => i != 0).ToArray();
|
||||
|
||||
if (GameVersion.SM.Contains(game) || GameVersion.USUM.Contains(game))
|
||||
return HeldItemsBuy_SM.Select(i => (int)i).Concat(Items_Ball).Where(i => i != 0).ToArray();
|
||||
|
||||
return new int[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
57
pkNX.Structures/Legality/Species.cs
Normal file
57
pkNX.Structures/Legality/Species.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public static partial class Legal
|
||||
{
|
||||
/// <summary>
|
||||
/// All final evolutions, excluding Event-exclusive Forms that do not evolve (Pikachu, Floette, etc). Used for forcing fully evolved species.
|
||||
/// Overrides all other randomizer settings, allowing all Generations and Legendary/Mythical Pokemon as well.
|
||||
/// </summary>
|
||||
public static readonly int[] FinalEvolutions_6 =
|
||||
{
|
||||
003, 006, 009, 012, 015, 018, 020, 022, 024, 026, 028, 031, 034, 036, 038, 040, 045, 047, 049, 051, 053, 055, 057, 059, 062, 065, 068, 071, 073, 076, 078, 080, 083, 085, 087, 089, 091,
|
||||
094, 097, 099, 101, 103, 105, 106, 107, 110, 115, 119, 121, 122, 124, 127, 128, 130, 131, 132, 134, 135, 136, 139, 141, 142, 143, 144, 145, 146, 149, 150, 151, 154, 157, 160, 162, 164,
|
||||
166, 168, 169, 171, 178, 181, 182, 184, 185, 186, 189, 192, 195, 196, 197, 199, 201, 202, 203, 205, 206, 208, 210, 211, 212, 213, 214, 217, 219, 222, 224, 225, 226, 227, 229, 230, 232,
|
||||
234, 235, 237, 241, 242, 243, 244, 245, 248, 249, 250, 251, 254, 257, 260, 262, 264, 267, 269, 272, 275, 277, 279, 282, 284, 286, 289, 291, 292, 295, 297, 301, 302, 303, 306, 308, 310,
|
||||
311, 312, 313, 314, 317, 319, 321, 323, 324, 326, 327, 330, 332, 334, 335, 336, 337, 338, 340, 342, 344, 346, 348, 350, 351, 352, 354, 357, 358, 359, 362, 365, 367, 368, 369, 370, 373,
|
||||
376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 389, 392, 395, 398, 400, 402, 405, 407, 409, 411, 413, 414, 416, 417, 419, 421, 423, 424, 426, 428, 429, 430, 432, 435, 437, 441,
|
||||
442, 445, 448, 450, 452, 454, 455, 457, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488,
|
||||
489, 490, 491, 492, 493, 494, 497, 500, 503, 505, 508, 510, 512, 514, 516, 518, 521, 523, 526, 528, 530, 531, 534, 537, 538, 539, 542, 545, 547, 549, 550, 553, 555, 556, 558, 560, 561,
|
||||
563, 565, 567, 569, 571, 573, 576, 579, 581, 584, 586, 587, 589, 591, 593, 594, 596, 598, 601, 604, 606, 609, 612, 614, 615, 617, 618, 620, 621, 623, 625, 626, 628, 630, 631, 632, 635,
|
||||
637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 652, 655, 658, 660, 663, 666, 668, 671, 673, 675, 676, 678, 681, 683, 685, 687, 689, 691, 693, 695, 697, 699, 700, 701,
|
||||
702, 703, 706, 707, 709, 711, 713, 715, 716, 717, 718, 719, 720, 721
|
||||
};
|
||||
|
||||
public static readonly int[] FinalEvolutions_SM = FinalEvolutions_6.Concat(new[]
|
||||
{
|
||||
724, 727, 730, 733, 735, 738, 740, 741, 743, 745, 746, 748, 750, 752, 754, 756, 758, 760, 763, 764, 765, 766, 768, 770, 771, 773, 774, 775, 776, 777, 779, 780, 781, 784, 785, 786, 787,
|
||||
788, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802
|
||||
}).ToArray();
|
||||
|
||||
public static readonly int[] FinalEvolutions_USUM = FinalEvolutions_SM.Concat(new[]
|
||||
{
|
||||
804, 805, 806, 807
|
||||
}).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// All Legendary and Mythical Pokemon. Used for Legendary-for-Legendary replacement.
|
||||
/// Does not include un-evolved species in the array due to potential error with Force Fully Evolved.
|
||||
/// </summary>
|
||||
public static readonly int[] Legendary_Mythical_6 =
|
||||
{
|
||||
144, 145, 146, 150, 151, 243, 244, 245, 249, 250, 251, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 638,
|
||||
639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 716, 717, 718, 719, 720, 721
|
||||
};
|
||||
|
||||
public static readonly int[] Legendary_Mythical_SM = Legendary_Mythical_6.Concat(new[]
|
||||
{
|
||||
773, 785, 786, 787, 788, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802
|
||||
}).ToArray();
|
||||
|
||||
public static readonly int[] Legendary_Mythical_USUM = Legendary_Mythical_SM.Concat(new[]
|
||||
{
|
||||
804, 805, 806, 807
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
557
pkNX.Structures/Legality/Tables6.cs
Normal file
557
pkNX.Structures/Legality/Tables6.cs
Normal file
|
|
@ -0,0 +1,557 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public static partial class Legal
|
||||
{
|
||||
public const int MaxSpeciesID_6 = 721;
|
||||
public const int MaxMoveID_6_XY = 617;
|
||||
public const int MaxMoveID_6_AO = 621;
|
||||
public const int MaxItemID_6_XY = 717;
|
||||
public const int MaxItemID_6_AO = 775;
|
||||
public const int MaxAbilityID_6_XY = 188;
|
||||
public const int MaxAbilityID_6_AO = 191;
|
||||
public const int MaxBallID_6 = 0x19;
|
||||
public const int MaxGameID_6 = 27; // OR
|
||||
|
||||
#region Met Locations
|
||||
public static readonly int[] Met_XYc = {0, 60002, 30002,};
|
||||
|
||||
public static readonly int[] Met_XY_0 =
|
||||
{
|
||||
/* XY */ 2, 6, 8, 10, 12, 14, 16, 17, 18, 20, 22, 24, 26, 28, 30, 32,
|
||||
34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 82, 84, 86, 88,
|
||||
90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134,
|
||||
136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, /* ORAS */ 170, 172,
|
||||
174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216,
|
||||
218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260,
|
||||
262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, 304,
|
||||
306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334, 336, 338, 340, 342, 344, 346, 348,
|
||||
350, 352, 354,
|
||||
};
|
||||
|
||||
public static readonly int[] Met_XY_3 =
|
||||
{
|
||||
30001, 30003, 30004, 30005, 30006, 30007, 30008, 30009, 30010, 30011,
|
||||
};
|
||||
|
||||
public static readonly int[] Met_XY_4 =
|
||||
{
|
||||
40001, 40002, 40003, 40004, 40005, 40006, 40007, 40008, 40009, 40010,
|
||||
40011, 40012, 40013, 40014, 40015, 40016, 40017, 40018, 40019, 40020, 40021, 40022, 40023, 40024, 40025,
|
||||
40026, 40027, 40028, 40029, 40030, 40031, 40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039, 40040,
|
||||
40041, 40042, 40043, 40044, 40045, 40046, 40047, 40048, 40049, 40050, 40051, 40052, 40053, 40054, 40055,
|
||||
40056, 40057, 40058, 40059, 40060, 40061, 40062, 40063, 40064, 40065, 40066, 40067, 40068, 40069, 40070,
|
||||
40071, 40072, 40073, 40074, 40075, 40076, 40077, 40078, 40079,
|
||||
};
|
||||
|
||||
public static readonly int[] Met_XY_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004,};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inventory Pouch
|
||||
|
||||
public static readonly ushort[] Pouch_Items_XY =
|
||||
{
|
||||
001, 002, 003, 004, 005, 006, 007, 008, 009, 010, 011, 012, 013, 014, 015, 016, 055, 056,
|
||||
057, 058, 059, 060, 061, 062, 063, 064, 065, 066, 067, 068, 069, 070, 071, 072, 073, 074, 075,
|
||||
076, 077, 078, 079, 080, 081, 082, 083, 084, 085, 086, 087, 088, 089, 090, 091, 092, 093, 094,
|
||||
099, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 116, 117, 118, 119, 135, 136,
|
||||
213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
|
||||
233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
|
||||
252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270,
|
||||
271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
|
||||
290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
|
||||
309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327,
|
||||
492, 493, 494, 495, 496, 497, 498, 499, 500, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546,
|
||||
547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 571,
|
||||
572, 573, 576, 577, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 639, 640, 644, 646,
|
||||
647, 648, 649, 650, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666,
|
||||
667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685,
|
||||
699, 704, 710, 711, 715,
|
||||
};
|
||||
|
||||
public static readonly ushort[] Pouch_Items_AO =
|
||||
{
|
||||
001, 002, 003, 004, 005, 006, 007, 008, 009, 010, 011, 012, 013, 014, 015, 016, 055, 056,
|
||||
057, 058, 059, 060, 061, 062, 063, 064, 068, 069, 070, 071, 072, 073, 074, 075,
|
||||
076, 077, 078, 079, 080, 081, 082, 083, 084, 085, 086, 087, 088, 089, 090, 091, 092, 093, 094,
|
||||
099, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 116, 117, 118, 119, 135, 136,
|
||||
213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
|
||||
233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
|
||||
252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270,
|
||||
271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
|
||||
290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
|
||||
309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327,
|
||||
492, 493, 494, 495, 496, 497, 498, 499, 500, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546,
|
||||
547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 571,
|
||||
572, 573, 576, 577, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 639, 640, 644, 646,
|
||||
647, 648, 649, 650, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666,
|
||||
667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685,
|
||||
699, 704, 710, 711, 715,
|
||||
|
||||
// ORAS
|
||||
534, 535,
|
||||
752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 767, 768, 769, 770,
|
||||
};
|
||||
|
||||
public static readonly ushort[] Pouch_Key_XY =
|
||||
{
|
||||
216, 431, 442, 445, 446, 447, 450, 465, 466, 471, 628,
|
||||
629, 631, 632, 638, 641, 642, 643, 689, 695, 696, 697, 698,
|
||||
700, 701, 702, 703, 705, 706, 707, 712, 713, 714,
|
||||
|
||||
// Illegal
|
||||
716, 717, // For the cheaters who want useless items...
|
||||
};
|
||||
|
||||
public static readonly ushort[] Pouch_Key_AO =
|
||||
{
|
||||
216, 445, 446, 447, 465, 466, 471, 628,
|
||||
629, 631, 632, 638, 697,
|
||||
|
||||
// Illegal
|
||||
716, 717, 745, 746, 747, 748, 749, 750, // For the cheaters who want useless items...
|
||||
|
||||
// ORAS
|
||||
457, 474, 503,
|
||||
|
||||
718, 719,
|
||||
720, 721, 722, 723, 724, 725, 726, 727, 728, 729,
|
||||
730, 731, 732, 733, 734, 735, 736, 738, 739,
|
||||
740, 741, 742, 743, 744,
|
||||
751, 765, 766, 771, 772, 774, 775,
|
||||
};
|
||||
|
||||
public static readonly ushort[] Pouch_TMHM_XY =
|
||||
{
|
||||
328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345,
|
||||
346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363,
|
||||
364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381,
|
||||
382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399,
|
||||
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417,
|
||||
418, 419, 618, 619, 620, 690, 691, 692, 693, 694,
|
||||
|
||||
420, 421, 422, 423, 424,
|
||||
};
|
||||
|
||||
public static readonly ushort[] Pouch_TMHM_AO =
|
||||
{
|
||||
328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345,
|
||||
346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363,
|
||||
364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381,
|
||||
382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399,
|
||||
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417,
|
||||
418, 419, 618, 619, 620, 690, 691, 692, 693, 694,
|
||||
|
||||
420, 421, 422, 423, 424,
|
||||
|
||||
// ORAS
|
||||
425, 737,
|
||||
};
|
||||
|
||||
public static readonly ushort[] Pouch_Medicine_XY =
|
||||
{
|
||||
017, 018, 019, 020, 021, 022, 023, 024, 025, 026, 027, 028, 029, 030, 031, 032, 033,
|
||||
034, 035, 036, 037, 038, 039, 040, 041, 042, 043, 044, 045, 046, 047, 048, 049, 050, 051,
|
||||
052, 053, 054, 134, 504, 565, 566, 567, 568, 569, 570, 571, 591, 645, 708, 709,
|
||||
};
|
||||
|
||||
public static readonly ushort[] Pouch_Medicine_AO =
|
||||
{
|
||||
017, 018, 019, 020, 021, 022, 023, 024, 025, 026, 027, 028, 029, 030, 031, 032, 033,
|
||||
034, 035, 036, 037, 038, 039, 040, 041, 042, 043, 044, 045, 046, 047, 048, 049, 050, 051,
|
||||
052, 053, 054, 134, 504, 565, 566, 567, 568, 569, 570, 571, 591, 645, 708, 709,
|
||||
|
||||
//ORAS
|
||||
065, 066, 067
|
||||
};
|
||||
|
||||
public static readonly ushort[] Pouch_Berry_XY =
|
||||
{
|
||||
149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
|
||||
163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
|
||||
178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
|
||||
193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
|
||||
208, 209, 210, 211, 212, 686, 687, 688,
|
||||
};
|
||||
|
||||
public static readonly ushort[] HeldItem_XY = new ushort[1].Concat(Pouch_Items_XY).Concat(Pouch_Medicine_XY).Concat(Pouch_Berry_XY).ToArray();
|
||||
public static readonly ushort[] HeldItem_AO = new ushort[1].Concat(Pouch_Items_AO).Concat(Pouch_Medicine_AO).Concat(Pouch_Berry_XY).ToArray();
|
||||
#endregion
|
||||
|
||||
#region TMHM
|
||||
|
||||
public static readonly int[] TMHM_AO =
|
||||
{
|
||||
468, 337, 473, 347, 046, 092, 258, 339, 474, 237,
|
||||
241, 269, 058, 059, 063, 113, 182, 240, 355, 219,
|
||||
218, 076, 479, 085, 087, 089, 216, 091, 094, 247,
|
||||
280, 104, 115, 482, 053, 188, 201, 126, 317, 332,
|
||||
259, 263, 488, 156, 213, 168, 490, 496, 497, 315,
|
||||
211, 411, 412, 206, 503, 374, 451, 507, 510, 511,
|
||||
261, 512, 373, 153, 421, 371, 514, 416, 397, 148,
|
||||
444, 521, 086, 360, 014, 522, 244, 523, 524, 157,
|
||||
404, 525, 611, 398, 138, 447, 207, 214, 369, 164,
|
||||
430, 433, 528, 290, 555, 267, 399, 612, 605, 590,
|
||||
|
||||
15, 19, 57, 70, 127, 249, 291,
|
||||
};
|
||||
|
||||
public static readonly int[] TMHM_XY =
|
||||
{
|
||||
468, 337, 473, 347, 046, 092, 258, 339, 474, 237,
|
||||
241, 269, 058, 059, 063, 113, 182, 240, 355, 219,
|
||||
218, 076, 479, 085, 087, 089, 216, 091, 094, 247,
|
||||
280, 104, 115, 482, 053, 188, 201, 126, 317, 332,
|
||||
259, 263, 488, 156, 213, 168, 490, 496, 497, 315,
|
||||
211, 411, 412, 206, 503, 374, 451, 507, 510, 511,
|
||||
261, 512, 373, 153, 421, 371, 514, 416, 397, 148,
|
||||
444, 521, 086, 360, 014, 522, 244, 523, 524, 157,
|
||||
404, 525, 611, 398, 138, 447, 207, 214, 369, 164,
|
||||
430, 433, 528, 249, 555, 267, 399, 612, 605, 590,
|
||||
|
||||
15, 19, 57, 70, 127,
|
||||
};
|
||||
|
||||
public static readonly int[] TypeTutor6 = {520, 519, 518, 338, 307, 308, 434, 620};
|
||||
|
||||
public static readonly int[][] Tutors_AO =
|
||||
{
|
||||
new[] {450, 343, 162, 530, 324, 442, 402, 529, 340, 067, 441, 253, 009, 007, 008},
|
||||
new[] {277, 335, 414, 492, 356, 393, 334, 387, 276, 527, 196, 401, 399, 428, 406, 304, 231},
|
||||
new[] {020, 173, 282, 235, 257, 272, 215, 366, 143, 220, 202, 409, 355, 264, 351, 352},
|
||||
new[] {380, 388, 180, 495, 270, 271, 478, 472, 283, 200, 278, 289, 446, 214, 285},
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
public static readonly int[] EggLocations6 = {60002, 30002};
|
||||
|
||||
public static readonly int[] ValidMet_XY =
|
||||
{
|
||||
006, 008, 009, 010, 012, 013, 014, 016, 017, 018, 020, 021, 022, 024, 026, 028, 029, 030, 032, 034, 035, 036,
|
||||
038, 039, 040, 042, 043, 044, 046, 047, 048, 050, 051, 052, 054, 055, 056, 058, 060, 062, 063, 064, 066, 067,
|
||||
068, 069, 070, 072, 074, 075, 076, 078, 079, 082, 084, 085, 086, 088, 089, 090, 092, 093, 094, 096, 097, 098,
|
||||
100, 101, 102, 103, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 135, 136,
|
||||
138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168
|
||||
};
|
||||
|
||||
public static readonly int[] ValidMet_AO =
|
||||
{
|
||||
170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198,
|
||||
200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238, 240, 242,
|
||||
244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286,
|
||||
288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330,
|
||||
332, 334, 336, 338, 340, 342, 344, 346, 348, 350, 352, 354
|
||||
};
|
||||
|
||||
public static readonly int[] FriendSafari =
|
||||
{
|
||||
190, 206, 216, 506, 294, 352, 531, 572, 113, 132, 133, 235,
|
||||
012, 046, 165, 415, 267, 284, 313, 314, 049, 127, 214, 666,
|
||||
262, 274, 624, 629, 215, 332, 342, 551, 302, 359, 510, 686,
|
||||
444, 611, 148, 372, 714, 621, 705,
|
||||
101, 417, 587, 702, 025, 125, 618, 694, 310, 404, 523, 596,
|
||||
175, 209, 281, 702, 039, 303, 682, 684, 035, 670,
|
||||
056, 067, 307, 619, 538, 539, 674, 236, 286, 297, 447,
|
||||
058, 077, 126, 513, 005, 218, 636, 668, 038, 654, 662,
|
||||
016, 021, 083, 084, 163, 520, 527, 581, 357, 627, 662, 701,
|
||||
353, 608, 708, 710, 356, 426, 442, 623,
|
||||
043, 114, 191, 511, 002, 541, 548, 586, 556, 651, 673,
|
||||
027, 194, 231, 328, 051, 105, 290, 323, 423, 536, 660,
|
||||
225, 361, 363, 459, 215, 614, 712, 087, 091, 131, 221,
|
||||
014, 044, 268, 336, 049, 168, 317, 569, 089, 452, 454, 544,
|
||||
063, 096, 326, 517, 202, 561, 677, 178, 203, 575, 578,
|
||||
299, 525, 557, 095, 219, 222, 247, 112, 213, 689,
|
||||
082, 303, 597, 205, 227, 375, 600, 437, 530, 707,
|
||||
098, 224, 400, 515, 008, 130, 195, 419, 061, 184, 657
|
||||
};
|
||||
|
||||
public static readonly int[] CosplayPikachuMoves = { 309, 556, 577, 604, 560, 0 };
|
||||
public static readonly int[] WildPokeballs6 = { 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
|
||||
|
||||
public static readonly int[] Ban_NoHidden6 =
|
||||
{
|
||||
//Not avaliable at Friend Safari or Horde Encounter
|
||||
669 + (2 << 11), //Flabébé-Orange
|
||||
670 + (2 << 11), //Floette-Orange
|
||||
671 + (2 << 11), //Florges-Orange
|
||||
669 + (4 << 11), //Flabébé-White
|
||||
670 + (4 << 11), //Floette-White
|
||||
671 + (4 << 11), //Florges-White
|
||||
|
||||
710 + (1 << 11), //Pumpkaboo-Small
|
||||
711 + (1 << 11), //Gourgeist-Small
|
||||
710 + (2 << 11), //Pumpkaboo-Large
|
||||
711 + (2 << 11), //Gourgeist-Large
|
||||
//Super Size can be obtained as a Pumpkaboo from event distributions
|
||||
};
|
||||
#region Ball Table
|
||||
public static readonly int[] Inherit_Sport =
|
||||
{
|
||||
010, 013, 046, 048, 123, 127, 265, 290, 314, 401, 415,
|
||||
|
||||
313, // Via Illumise
|
||||
};
|
||||
|
||||
public static readonly int[] Inherit_Safari =
|
||||
{
|
||||
016, 019, 020, 021, 022, 023, 024, 025, 027, 029, 035, 039, 041,
|
||||
043, 046, 048, 050, 054, 055, 060, 063, 066, 069, 070, 074, 077, 079, 080, 083, 084, 088, 092, 095, 096, 098,
|
||||
099, 102, 104, 108, 109, 111, 113, 114, 115, 118, 122, 123, 125, 126, 127, 129, 131, 147, 161, 163, 165, 167,
|
||||
177, 179, 183, 187, 189, 190, 191, 193, 194, 198, 200, 202, 203, 204, 207, 209, 213, 214, 216, 223, 228, 229,
|
||||
231, 234, 235, 241, 246, 263, 264, 270, 271, 273, 283, 284, 285, 286, 288, 298, 299, 304, 305, 307, 308, 309,
|
||||
310, 314, 315, 316, 318, 324, 327, 328, 331, 332, 335, 336, 339, 341, 352, 353, 354, 355, 356, 357, 358, 363,
|
||||
364, 371, 372, 397, 399, 400, 403, 404, 406, 417, 418, 419, 433, 443, 447, 449, 451, 453, 455,
|
||||
|
||||
032, // Via Nidoran-F
|
||||
313, // Via Illumise
|
||||
|
||||
173, // Via Clefairy
|
||||
239, // Via Electabuzz
|
||||
240, // Via Magmar
|
||||
298, // Via Marill
|
||||
360, // Via Wobbuffet
|
||||
406, // Via Roselia
|
||||
433, // Via Chimecho
|
||||
439, // Via Mr. Mime
|
||||
440, // Via Chansey
|
||||
};
|
||||
|
||||
public static readonly int[] Inherit_Dream =
|
||||
{
|
||||
010, 013, 016, 019, 021, 023, 027, 029, 037, 041, 043, 046, 048, 050, 052, 054, 056, 058, 060, 063, 066, 069,
|
||||
072, 074, 077, 079, 083, 084, 086, 088, 090, 092, 095, 096, 098, 102, 104, 108, 109, 111, 113, 114, 115, 116,
|
||||
118, 122, 123, 127, 129, 131, 133, 138, 140, 142, 143, 147, 161, 163, 165, 167, 170, 173, 174, 175, 177, 179,
|
||||
183, 185, 187, 190, 191, 193, 194, 198, 200, 202, 203, 204, 206, 207, 209, 211, 213, 214, 215, 216, 218, 220,
|
||||
222, 223, 225, 226, 227, 228, 231, 234, 235, 238, 239, 240, 241, 246, 261, 263, 265, 270, 273, 276, 278, 280,
|
||||
283, 285, 287, 290, 293, 296, 299, 300, 302, 303, 304, 307, 309, 311, 312, 314, 315, 316, 318, 320, 322, 324,
|
||||
325, 327, 328, 331, 333, 335, 336, 339, 341, 345, 347, 349, 351, 352, 353, 355, 357, 358, 359, 361, 363, 366,
|
||||
369, 370, 371, 396, 399, 401, 403, 408, 410, 412, 415, 417, 418, 420, 422, 425, 427, 431, 434, 441, 442, 443,
|
||||
447, 449, 451, 453, 455, 456, 459, 517, 519, 525, 529, 531, 533, 535, 545, 546, 548, 550, 553, 556, 558, 559,
|
||||
561, 564, 578, 580, 583, 587, 588, 594, 596, 605, 610, 616, 618, 621, 624, 631, 632,
|
||||
|
||||
032, // Via Nidoran-F
|
||||
313, // Via Illumise
|
||||
|
||||
// Via Evolution
|
||||
524, // Roggenrola
|
||||
532, // Timburr
|
||||
543, // Venipede
|
||||
551, // Sandile
|
||||
557, // Dwebble
|
||||
577, // Solosis
|
||||
582, // Vanillite
|
||||
595, // Joltik
|
||||
|
||||
// Via Incense Breeding
|
||||
298, 360, 406, 433, 438, 439, 440, 446, 458,
|
||||
};
|
||||
|
||||
public static readonly int[] Ban_DreamHidden =
|
||||
{
|
||||
311, // Plusle
|
||||
312, // Minun
|
||||
352, // Kecleon
|
||||
355, // Duskull
|
||||
|
||||
// Via Pre-Evolution
|
||||
356, // Dusclops
|
||||
477, // Dusknoir
|
||||
};
|
||||
|
||||
public static readonly int[] Ban_Gen3Ball =
|
||||
{
|
||||
252, 255, 258, //1 - Treeko, Torchic, Mudkip
|
||||
253, 256, 259, //2
|
||||
254, 257, 260, //3
|
||||
387, 390, 393, //1 - Turtwig, Chimchar, Piplup
|
||||
388, 391, 394, //2
|
||||
389, 392, 395, //3
|
||||
495, 498, 501, //1 - Snivy, Tepig, Oshawott
|
||||
496, 499, 502, //2
|
||||
497, 500, 503, //3
|
||||
566, 567, 696, 697, 698, 699 // Fossil Only obtain
|
||||
};
|
||||
|
||||
public static readonly int[] Ban_Gen3BallHidden =
|
||||
{
|
||||
// can have HA and can be in gen 3 ball as eggs but can not at same time.
|
||||
152, 155, 158, //1 - Gen2 Starters
|
||||
153, 156, 159, //2
|
||||
154, 157, 160, //3
|
||||
585 + (1 << 11), //Deerling-Summer
|
||||
586 + (1 << 11), //Sawsbuck-Summer
|
||||
585 + (2 << 11), //Deerling-Autumn
|
||||
586 + (2 << 11), //Sawsbuck-Autumn
|
||||
585 + (3 << 11), //Deerling-Winter
|
||||
586 + (3 << 11), //Sawsbuck-Winter
|
||||
710 + (3 << 11), //Pumpkaboo-Super
|
||||
711 + (3 << 11), //Gourgeist-Super
|
||||
};
|
||||
|
||||
public static readonly int[] Ban_Gen4Ball_6 =
|
||||
{
|
||||
152, 155, 158, //1 - Chikorita, Cyndaquil, Totodile
|
||||
153, 156, 159, //2
|
||||
154, 157, 160, //3
|
||||
252, 255, 258, //1 - Treeko, Torchic, Mudkip
|
||||
253, 256, 259, //2
|
||||
254, 257, 260, //3
|
||||
387, 390, 393, //1 - Turtwig, Chimchar, Piplup
|
||||
388, 391, 394, //2
|
||||
389, 392, 395, //3
|
||||
495, 498, 501, //1 - Snivy, Tepig, Oshawott
|
||||
496, 499, 502, //2
|
||||
497, 500, 503, //3
|
||||
566, 567, 696, 697, 698, 699 // Fossil Only obtain
|
||||
};
|
||||
|
||||
public static readonly int[] WurmpleEvolutions =
|
||||
{
|
||||
266, 267, // Silcoon Beautifly
|
||||
268, 269, // Cascoon Dustox
|
||||
};
|
||||
#endregion
|
||||
#region Memory Table
|
||||
public static readonly int[] Memory_NotXY =
|
||||
{
|
||||
65, // {0} was with {1} when (he/she) built a Secret Base. {4} that {3}.
|
||||
66, // {0} participated in a contest with {1} and impressed many people. {4} that {3}.
|
||||
67, // {0} participated in a contest with {1} and won the title. {4} that {3}.
|
||||
68, // {0} soared through the sky with {1} and went to many different places. {4} that {3}.
|
||||
69, // {1} asked {0} to dive. Down it went, deep into the ocean, to explore the bottom of the sea. {4} that {3}.
|
||||
};
|
||||
|
||||
public static readonly int[] Memory_NotAO =
|
||||
{
|
||||
11, // {0} went clothes shopping with {1}. {4} that {3}.
|
||||
43, // {0} was impressed by the speed of the train it took with {1}. {4} that {3}.
|
||||
44, // {0} encountered {2} with {1} using the Poké Radar. {4} that {3}.
|
||||
56, // {0} was with {1} when (he/she) went to a boutique and tried on clothes, but (he/she) left the boutique without buying anything. {4} that {3}.
|
||||
57, // {0} went to a nice restaurant with {1} and ate until it got totally full. {4} that {3}.
|
||||
62, // {0} saw itself in a mirror in a mirror cave that it went to with {1}. {4} that {3}.
|
||||
};
|
||||
|
||||
public static readonly int[][] MoveSpecificMemories =
|
||||
{
|
||||
new[] {
|
||||
20, // {0} surfed across the water, carrying {1} on its back. {4} that {3}.
|
||||
24, // {0} flew, carrying {1} on its back, to {2}. {4} that {3}.
|
||||
35, // {0} proudly used Strength at {1}’s instruction in... {2}. {4} that {3}.
|
||||
36, // {0} proudly used Cut at {1}’s instruction in... {2}. {4} that {3}.
|
||||
37, // {0} shattered rocks to its heart’s content at {1}’s instruction in... {2}. {4} that {3}.
|
||||
38, // {0} used Waterfall while carrying {1} on its back in... {2}. {4} that {3}.
|
||||
69, // {1} asked {0} to dive. Down it went, deep into the ocean, to explore the bottom of the sea. {4} that {3}.
|
||||
},
|
||||
new[] { 57, 19, 70, 15, 249, 127, 291}, // Move IDs
|
||||
};
|
||||
|
||||
public static readonly int[][] LocationsWithPKCenter =
|
||||
{
|
||||
new[] {
|
||||
// Kalos locations with a PKMN CENTER
|
||||
18, // Santalune City
|
||||
22, // Lumiose City
|
||||
30, // Camphrier Town
|
||||
40, // Cyllage City
|
||||
44, // Ambrette Town
|
||||
52, // Geosenge Towny
|
||||
58, // Shalour City
|
||||
64, // Coumarine City
|
||||
70, // Laverre City
|
||||
76, // Dendemille Town
|
||||
86, // Anistar City
|
||||
90, // Couriway Town
|
||||
94, // Snowbelle City
|
||||
106, // Pokémon League (X/Y)
|
||||
// Hoenn locations with a PKMN CENTER
|
||||
172, // Oldale Town
|
||||
174, // Dewford Town
|
||||
176, // Lavaridge Town
|
||||
178, // Fallarbor Town
|
||||
180, // Verdanturf Town
|
||||
182, // Pacifidlog Town
|
||||
184, // Petalburg City
|
||||
186, // Slateport City
|
||||
188, // Mauville City
|
||||
190, // Rustboro City
|
||||
192, // Fortree City
|
||||
194, // Lilycove City
|
||||
196, // Mossdeep City
|
||||
198, // Sootopolis City
|
||||
200, // Ever Grande City
|
||||
202, // Pokémon League (OR/AS)
|
||||
},
|
||||
new[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // Region matching
|
||||
};
|
||||
|
||||
public static readonly HashSet<int> MemoryGeneral = new HashSet<int> { 1, 2, 3, 4, 19, 24, 31, 32, 33, 35, 36, 37, 38, 39, 42, 52, 59 };
|
||||
public static readonly HashSet<int> MemorySpecific = new HashSet<int> { 6 };
|
||||
public static readonly HashSet<int> MemoryMove = new HashSet<int> { 12, 16, 48, 49 };
|
||||
public static readonly HashSet<int> MemoryItem = new HashSet<int> { 5, 15, 26, 34, 40, 51 };
|
||||
public static readonly HashSet<int> MemorySpecies = new HashSet<int> { 7, 9, 13, 14, 17, 21, 18, 25, 29, 44, 45, 50, 60 };
|
||||
#endregion
|
||||
|
||||
public static readonly int[] MovePP_XY =
|
||||
{
|
||||
00,
|
||||
35, 25, 10, 15, 20, 20, 15, 15, 15, 35, 30, 05, 10, 20, 30, 35, 35, 20, 15, 20, 20, 25, 20, 30, 05, 10, 15, 15, 15, 25, 20, 05, 35, 15, 20, 20, 10, 15, 30, 35, 20, 20, 30, 25, 40, 20, 15, 20, 20, 20,
|
||||
30, 25, 15, 30, 25, 05, 15, 10, 05, 20, 20, 20, 05, 35, 20, 25, 20, 20, 20, 15, 25, 15, 10, 20, 25, 10, 35, 30, 15, 10, 40, 10, 15, 30, 15, 20, 10, 15, 10, 05, 10, 10, 25, 10, 20, 40, 30, 30, 20, 20,
|
||||
15, 10, 40, 15, 10, 30, 10, 20, 10, 40, 40, 20, 30, 30, 20, 30, 10, 10, 20, 05, 10, 30, 20, 20, 20, 05, 15, 15, 20, 10, 15, 35, 20, 15, 10, 10, 30, 15, 40, 20, 15, 10, 05, 10, 30, 10, 15, 20, 15, 40,
|
||||
20, 10, 05, 15, 10, 10, 10, 15, 30, 30, 10, 10, 20, 10, 01, 01, 10, 25, 10, 05, 15, 25, 15, 10, 15, 30, 05, 40, 15, 10, 25, 10, 30, 10, 20, 10, 10, 10, 10, 10, 20, 05, 40, 05, 05, 15, 05, 10, 05, 10,
|
||||
10, 10, 10, 20, 20, 40, 15, 10, 20, 20, 25, 05, 15, 10, 05, 20, 15, 20, 25, 20, 05, 30, 05, 10, 20, 40, 05, 20, 40, 20, 15, 35, 10, 05, 05, 05, 15, 05, 20, 05, 05, 15, 20, 10, 05, 05, 15, 10, 15, 15,
|
||||
10, 10, 10, 20, 10, 10, 10, 10, 15, 15, 15, 10, 20, 20, 10, 20, 20, 20, 20, 20, 10, 10, 10, 20, 20, 05, 15, 10, 10, 15, 10, 20, 05, 05, 10, 10, 20, 05, 10, 20, 10, 20, 20, 20, 05, 05, 15, 20, 10, 15,
|
||||
20, 15, 10, 10, 15, 10, 05, 05, 10, 15, 10, 05, 20, 25, 05, 40, 15, 05, 40, 15, 20, 20, 05, 15, 20, 20, 15, 15, 05, 10, 30, 20, 30, 15, 05, 40, 15, 05, 20, 05, 15, 25, 25, 15, 20, 15, 20, 15, 20, 10,
|
||||
20, 20, 05, 05, 10, 05, 40, 10, 10, 05, 10, 10, 15, 10, 20, 15, 30, 10, 20, 05, 10, 10, 15, 10, 10, 05, 15, 05, 10, 10, 30, 20, 20, 10, 10, 05, 05, 10, 05, 20, 10, 20, 10, 15, 10, 20, 20, 20, 15, 15,
|
||||
10, 15, 15, 15, 10, 10, 10, 20, 10, 30, 05, 10, 15, 10, 10, 05, 20, 30, 10, 30, 15, 15, 15, 15, 30, 10, 20, 15, 10, 10, 20, 15, 05, 05, 15, 15, 05, 10, 05, 20, 05, 15, 20, 05, 20, 20, 20, 20, 10, 20,
|
||||
10, 15, 20, 15, 10, 10, 05, 10, 05, 05, 10, 05, 05, 10, 05, 05, 05, 15, 10, 10, 10, 10, 10, 10, 15, 20, 15, 10, 15, 10, 15, 10, 20, 10, 15, 10, 20, 20, 20, 20, 20, 15, 15, 15, 15, 15, 15, 20, 15, 10,
|
||||
15, 15, 15, 15, 10, 10, 10, 10, 10, 15, 15, 15, 15, 05, 05, 15, 05, 10, 10, 10, 20, 20, 20, 10, 10, 30, 15, 15, 10, 15, 25, 10, 15, 10, 10, 10, 20, 10, 10, 10, 10, 10, 15, 15, 05, 05, 10, 10, 10, 05,
|
||||
05, 10, 05, 05, 15, 10, 05, 05, 05, 10, 10, 10, 10, 20, 25, 10, 20, 30, 25, 20, 20, 15, 20, 15, 20, 20, 10, 10, 10, 10, 10, 20, 10, 30, 15, 10, 10, 10, 20, 20, 05, 05, 05, 20, 10, 10, 20, 15, 20, 20,
|
||||
10, 20, 30, 10, 10, 40, 40, 30, 20, 40, 20, 20, 10, 10, 10, 10, 05, 10, 10, 05, 05,
|
||||
};
|
||||
|
||||
public static readonly int[] UnreleasedItems_6 =
|
||||
{
|
||||
005, // Safari Ball
|
||||
016, // Cherish Ball
|
||||
492, // Fast Ball
|
||||
493, // Level Ball
|
||||
494, // Lure Ball
|
||||
495, // Heavy Ball
|
||||
496, // Love Ball
|
||||
497, // Friend Ball
|
||||
498, // Moon Ball
|
||||
499, // Sport Ball
|
||||
500, // Park Ball
|
||||
548, // Fire Gem
|
||||
549, // Water Gem
|
||||
550, // Electric Gem
|
||||
551, // Grass Gem
|
||||
552, // Ice Gem
|
||||
553, // Fighting Gem
|
||||
554, // Poison Gem
|
||||
555, // Ground Gem
|
||||
556, // Flying Gem
|
||||
557, // Psychic Gem
|
||||
558, // Bug Gem
|
||||
559, // Rock Gem
|
||||
560, // Ghost Gem
|
||||
561, // Dragon Gem
|
||||
562, // Dark Gem
|
||||
563, // Steel Gem
|
||||
576, // Dream Ball
|
||||
584, // Relic Copper
|
||||
585, // Relic Silver
|
||||
587, // Relic Vase
|
||||
588, // Relic Band
|
||||
589, // Relic Statue
|
||||
590, // Relic Crown
|
||||
715, // Fairy Gem
|
||||
};
|
||||
|
||||
public static readonly bool[] ReleasedHeldItems_6 = Enumerable.Range(0, MaxItemID_6_AO+1).Select(i => HeldItem_AO.Contains((ushort)i) && !UnreleasedItems_6.Contains(i)).ToArray();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user