Initial Commit

This commit is contained in:
Kurt 2018-11-13 19:44:43 -08:00
parent c2cf579d4f
commit 52d70f0b9f
175 changed files with 13734 additions and 0 deletions

674
LICENSE Normal file
View 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>.

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# pkNX
pkNX... for lack of a better name!

View 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;
}
}
}

View 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);
}
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace pkNX.Containers
{
public class FileCountDeterminedEventArgs : EventArgs
{
public int Total { get; set; }
}
}

View 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; }
}
}

View 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,
}
}

View File

@ -0,0 +1,11 @@
namespace pkNX.Containers
{
public enum ContainerType
{
GARC,
Mini,
SARC,
Folder,
SingleFile,
}
}

View 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
}
}
}

View 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
}
}
}

View 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);
}
}
}
}

View File

@ -0,0 +1,8 @@
namespace pkNX.Containers
{
public class FATBSubEntry : LargeContainerEntry
{
public bool Exists;
public static string GetFileNumber(int index) => $"{index:00}";
}
}

View 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);
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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}");
}
}
}
}

View File

@ -0,0 +1,8 @@
namespace pkNX.Containers
{
public enum GARCVersion : ushort
{
VER_4 = 0x0400,
VER_6 = 0x0600,
}
}

View 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));
}
}

View 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();
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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; }
}
}
}

View File

@ -0,0 +1,114 @@
using System.Collections.Generic;
namespace pkNX.Containers
{
/// <summary>
/// FowlerNollVo 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;
}
}
}

View 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);
}
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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);
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
<Description>Packing &amp; Unpacking</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="lz4net" Version="1.0.15.93" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,7 @@
namespace pkNX.Game
{
public class EditorBase
{
}
}

View File

@ -0,0 +1,6 @@
namespace pkNX.Game
{
class EditorFactory
{
}
}

146
pkNX.Game/File/GameFile.cs Normal file
View 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,
}
}

View 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 &amp; 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 &amp; 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
}
}

View 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
View 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
View 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
View 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
View 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 &amp; 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)),
};
}
}
}

View 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;
}
}
}

View 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 =
{
};
}
}

View 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
}
}

View 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;
}
}
}

View 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>

View 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;
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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,
}
}

View 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
}
}

View 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;
}
}
}
}

View File

@ -0,0 +1,7 @@
namespace pkNX.Randomization
{
public class RandSettings
{
}
}

View File

@ -0,0 +1,11 @@
using System;
namespace pkNX.Randomization
{
public abstract class Randomizer
{
public abstract void Execute();
protected readonly Random Rand = Util.Random;
}
}

View 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;
}
}
}

View 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)); // *
}
}
}

View 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;
}
}
}

View 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;
}
}
}
}

View 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>

View 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,
};
}
}

View 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();
}
}

View 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();
}
}
}
}

View 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();
}
}
}
}

View 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];
}
}
}
}

View 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; }
}
}

View 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; }
}
}

View 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;
}
}

View 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));
}
}
}
}

View 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;
}
}
}

View 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));
}
}
}

View 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;
}
}

View 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} }},";
}
}
}

View 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];
}
}
}
}

View 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; }
}
}

View File

@ -0,0 +1,11 @@
namespace pkNX.Structures
{
public enum FixedGender : byte
{
Random = 0,
Male = 1,
Female = 2,
Genderless = Random,
}
}

View 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,
}
}

View 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,
}
}

View 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
};
}
}
}

View 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();
}
}

View 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();
}
}
}
}

View 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();
}
}
}
}

View 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,
}
}

View 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]}");
}
}
}

View 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
View 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();
}
}

View 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; Soul Silver identifier.
/// </summary>
/// <see cref="HG"/>
/// <see cref="SS"/>
HGSS,
/// <summary>
/// Pokémon Battle Revolution identifier.
/// </summary>
BATREV,
/// <summary>
/// Pokémon Black &amp; 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 &amp; 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 &amp; 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 &amp; Alpha Sapphire Demo identifier.
/// </summary>
/// <see cref="ORAS"/>
ORASDEMO,
/// <summary>
/// Pokémon Omega Ruby &amp; 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 &amp; 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 &amp; 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 &amp; 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
}
}

View 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,
}
}

View 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,
}
}

View 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;
}
}
}

View 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,
}
}

View 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,
}
}

View 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];
}
}
}

View 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;
}
}
}

View 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();
}
}

View 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;
}
}
}

View 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,
};
}
}

View 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];
}
}
}

View 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();
}
}

View 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 hearts 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