rewrite everything
This commit is contained in:
339
Source/sys-clk/overlay/lib/libultrahand/libultra/LICENSE
Normal file
339
Source/sys-clk/overlay/lib/libultrahand/libultra/LICENSE
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) 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
|
||||
this service 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 make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. 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.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
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
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE 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.
|
||||
|
||||
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
|
||||
convey 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 2 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, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision 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, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This 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.
|
||||
69
Source/sys-clk/overlay/lib/libultrahand/libultra/README.md
Normal file
69
Source/sys-clk/overlay/lib/libultrahand/libultra/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# libultra
|
||||
|
||||

|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
`libultra` is a powerful collection of helper functions designed specifically for the Ultrahand Overlay project. This library offers a comprehensive set of utilities aimed at simplifying common tasks and enhancing the efficiency of your projects within the Ultrahand Overlay ecosystem.
|
||||
|
||||
## Features
|
||||
|
||||
### [Ultra Header](/libultra/include/ultra.hpp)
|
||||
|
||||
- **ultra.hpp**: The main header file for the `libultra` library, including all essential functions and declarations for seamless integration into your projects.
|
||||
|
||||
### [Debug Functions](/libultra/include/debug_funcs.hpp)
|
||||
|
||||
- **debug_funcs.hpp**: A collection of functions tailored for debugging purposes, aiding in identifying and resolving issues within your codebase.
|
||||
|
||||
### [Download Functions](/libultra/include/download_funcs.hpp)
|
||||
|
||||
- **download_funcs.hpp**: Utilities for managing file downloads, enabling seamless retrieval of resources from external sources.
|
||||
|
||||
### [Get Functions](/libultra/include/get_funcs.hpp)
|
||||
|
||||
- **get_funcs.hpp**: Functions for retrieving data from various sources, simplifying the process of fetching information within your applications.
|
||||
|
||||
### [Hex Functions](/libultra/include/hex_funcs.hpp)
|
||||
|
||||
- **hex_funcs.hpp**: Tools for working with hexadecimal data, facilitating operations such as conversion and manipulation of hexadecimal values.
|
||||
|
||||
### [INI Functions](/libultra/include/ini_funcs.hpp)
|
||||
|
||||
- **ini_funcs.hpp**: Helper functions for handling INI configuration files, offering a convenient way to manage application settings.
|
||||
|
||||
### [JSON Functions](/libultra/include/json_funcs.hpp)
|
||||
|
||||
- **json_funcs.hpp**: Utilities for parsing and manipulating JSON data, providing easy integration with JSON-based APIs and services.
|
||||
|
||||
### [List Functions](/libultra/include/list_funcs.hpp)
|
||||
|
||||
- **list_funcs.hpp**: Functions for working with lists and arrays, offering essential operations like sorting, searching, and manipulation of list elements.
|
||||
|
||||
### [Mod Functions](/libultra/include/mod_funcs.hpp)
|
||||
|
||||
- **mod_funcs.hpp**: Functions for manipulating game mods, facilitating operations and management tasks related to modifications in gaming environments.
|
||||
|
||||
### [Path Functions](/libultra/include/path_funcs.hpp)
|
||||
|
||||
- **path_funcs.hpp**: Tools for working with file paths and directories, simplifying file system operations within your applications.
|
||||
|
||||
### [String Functions](/libultra/include/string_funcs.hpp)
|
||||
|
||||
- **string_funcs.hpp**: A collection of string manipulation functions, providing utilities for tasks such as formatting, searching, and modification of strings.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
To build Ultrahand overlays with `libultra` + `libtesla`, simply add the `lib` folder to your project, then import `tesla.hpp`.
|
||||
|
||||
## Contribution
|
||||
|
||||
Contributions to `libultra` are welcome. If you have ideas for additional helper functions or improvements to existing ones, feel free to submit a pull request or open an issue on GitHub.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under both [GPLv2](LICENSE) and [CC-BY-4.0](SUB_LICENSE).
|
||||
|
||||
Copyright (c) 2024 ppkantorski
|
||||
395
Source/sys-clk/overlay/lib/libultrahand/libultra/SUB_LICENSE
Normal file
395
Source/sys-clk/overlay/lib/libultrahand/libultra/SUB_LICENSE
Normal file
@@ -0,0 +1,395 @@
|
||||
Attribution 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More_considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution 4.0 International Public License ("Public License"). To the
|
||||
extent this Public License may be interpreted as a contract, You are
|
||||
granted the Licensed Rights in consideration of Your acceptance of
|
||||
these terms and conditions, and the Licensor grants You such rights in
|
||||
consideration of benefits the Licensor receives from making the
|
||||
Licensed Material available under these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
d. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
f. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
g. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
h. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
4. If You Share Adapted Material You produce, the Adapter's
|
||||
License You apply must not prevent recipients of the Adapted
|
||||
Material from complying with this Public License.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
@@ -0,0 +1,52 @@
|
||||
/********************************************************************************
|
||||
* File: debug_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file contains debugging functions for the Ultrahand Overlay project.
|
||||
* These functions allow logging messages with timestamps to a log file.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#ifndef DEBUG_FUNCS_HPP
|
||||
#define DEBUG_FUNCS_HPP
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE // For not using fstream (needs implementing)
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
namespace ult {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
|
||||
// Specify the log file path
|
||||
extern const std::string defaultLogFilePath;
|
||||
|
||||
extern std::string logFilePath; // Declare logFilePath as extern
|
||||
extern bool disableLogging; // Declare disableLogging as extern
|
||||
|
||||
// Global mutex for thread-safe logging
|
||||
extern std::mutex logMutex; // Declare logMutex as extern
|
||||
|
||||
/**
|
||||
* @brief Logs a message with a timestamp to a log file in a thread-safe manner.
|
||||
*
|
||||
* @param message The message to be logged.
|
||||
*/
|
||||
void logMessage(const std::string& message);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // DEBUG_FUNCS_HPP
|
||||
@@ -0,0 +1,89 @@
|
||||
/********************************************************************************
|
||||
* File: download_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file contains functions for downloading and extracting files
|
||||
* using libcurl and miniz. It includes functions for downloading files from URLs,
|
||||
* writing received data to a file, and extracting files from ZIP archives.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
#pragma once
|
||||
#ifndef DOWNLOAD_FUNCS_HPP
|
||||
#define DOWNLOAD_FUNCS_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <switch.h>
|
||||
|
||||
#define CURL_DISABLE_DEFLATE
|
||||
#include <curl/curl.h>
|
||||
#include <zlib.h>
|
||||
#include <minizip/unzip.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
#include "global_vars.hpp"
|
||||
#include "string_funcs.hpp"
|
||||
#include "get_funcs.hpp"
|
||||
#include "path_funcs.hpp"
|
||||
#include "debug_funcs.hpp"
|
||||
|
||||
namespace ult {
|
||||
// Constants for buffer sizes
|
||||
|
||||
extern size_t DOWNLOAD_READ_BUFFER;
|
||||
extern size_t DOWNLOAD_WRITE_BUFFER;
|
||||
extern size_t UNZIP_READ_BUFFER;
|
||||
extern size_t UNZIP_WRITE_BUFFER;
|
||||
|
||||
// Path to the CA certificate
|
||||
//extern const std::string cacertPath;
|
||||
//extern const std::string cacertURL;
|
||||
|
||||
// Thread-safe atomic flags for operation control
|
||||
extern std::atomic<bool> abortDownload;
|
||||
extern std::atomic<bool> abortUnzip;
|
||||
extern std::atomic<int> downloadPercentage;
|
||||
extern std::atomic<int> unzipPercentage;
|
||||
|
||||
// User agent string for curl requests
|
||||
inline constexpr const char* userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36";
|
||||
|
||||
// Custom deleters for CURL handles
|
||||
struct CurlDeleter {
|
||||
void operator()(CURL* curl) const;
|
||||
};
|
||||
|
||||
// Thread-safe callback functions
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
size_t writeCallback(void* ptr, size_t size, size_t nmemb, FILE* stream);
|
||||
#else
|
||||
size_t writeCallback(void* ptr, size_t size, size_t nmemb, std::ostream* stream);
|
||||
#endif
|
||||
|
||||
int progressCallback(void* ptr, curl_off_t totalToDownload, curl_off_t nowDownloaded, curl_off_t totalToUpload, curl_off_t nowUploaded);
|
||||
|
||||
|
||||
// Thread-safe initialization and cleanup functions
|
||||
void initializeCurl();
|
||||
void cleanupCurl();
|
||||
|
||||
// Main API functions - thread-safe and optimized
|
||||
bool downloadFile(const std::string& url, const std::string& toDestination, bool noPercentagePolling=false);
|
||||
bool unzipFile(const std::string& zipFilePath, const std::string& extractTo);
|
||||
}
|
||||
|
||||
#endif // DOWNLOAD_FUNCS_HPP
|
||||
@@ -0,0 +1,160 @@
|
||||
/********************************************************************************
|
||||
* File: get_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file contains functions for retrieving information and data
|
||||
* from various sources, including file system and JSON files. It includes
|
||||
* functions for obtaining overlay module information, reading file contents,
|
||||
* and parsing JSON data.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef GET_FUNCS_HPP
|
||||
#define GET_FUNCS_HPP
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <fnmatch.h>
|
||||
#include "debug_funcs.hpp"
|
||||
#include "string_funcs.hpp"
|
||||
|
||||
|
||||
namespace ult {
|
||||
struct DirCloser {
|
||||
void operator()(DIR* dir) const {
|
||||
if (dir) closedir(dir);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reads the contents of a file and returns it as a string, normalizing line endings.
|
||||
*
|
||||
* @param filePath The path to the file to be read.
|
||||
* @return The content of the file as a string with line endings normalized to '\n'.
|
||||
*/
|
||||
//std::string getFileContents(const std::string& filePath);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Concatenates the provided directory and file names to form a destination path.
|
||||
*
|
||||
* @param destinationDir The directory where the file should be placed.
|
||||
* @param fileName The name of the file.
|
||||
* @return The destination path as a string.
|
||||
*/
|
||||
std::string getDestinationPath(const std::string& destinationDir, const std::string& fileName);
|
||||
|
||||
/**
|
||||
* @brief Extracts the value part from a string line containing a key-value pair.
|
||||
*
|
||||
* @param line The string line containing a key-value pair (e.g., "key=value").
|
||||
* @return The extracted value as a string. If no value is found, an empty string is returned.
|
||||
*/
|
||||
std::string getValueFromLine(const std::string& line);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the name from a file path, including handling directories.
|
||||
*
|
||||
* @param path The file path from which to extract the name.
|
||||
* @return The extracted name as a string. If the path indicates a directory, it extracts the last directory name.
|
||||
* If the path is empty or no name is found, an empty string is returned.
|
||||
*/
|
||||
std::string getNameFromPath(const std::string& path);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the file name from a full file path.
|
||||
*
|
||||
* This function takes a filesystem path and returns only the file name,
|
||||
* stripping away any directory paths that precede it.
|
||||
*
|
||||
* @param path The full path to the file.
|
||||
* @return The file name extracted from the full path.
|
||||
*/
|
||||
std::string getFileName(const std::string& path);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the name of the parent directory from a given file path at a specified level.
|
||||
*
|
||||
* @param path The file path from which to extract the parent directory name.
|
||||
* @param level The level of the parent directory to extract (0 for immediate parent, 1 for grandparent, and so on).
|
||||
* @return The name of the parent directory at the specified level.
|
||||
*/
|
||||
std::string getParentDirNameFromPath(const std::string& path, size_t level = 0);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the parent directory path from a given file path.
|
||||
*
|
||||
* @param path The file path from which to extract the parent directory path.
|
||||
* @return The parent directory path.
|
||||
*/
|
||||
std::string getParentDirFromPath(const std::string& path);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets a list of subdirectories in a directory.
|
||||
*
|
||||
* @param directoryPath The path of the directory to search.
|
||||
* @return A vector of strings containing the names of subdirectories.
|
||||
*/
|
||||
std::vector<std::string> getSubdirectories(const std::string& directoryPath);
|
||||
|
||||
// Cache to store directory status
|
||||
// Assuming a very simple cache implementation
|
||||
//extern std::vector<std::pair<std::string, bool>> directoryCache;
|
||||
|
||||
bool isDirectory(struct dirent* entry, const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief Recursively retrieves a list of files from a directory.
|
||||
*
|
||||
* @param directoryPath The path of the directory to search.
|
||||
* @return A vector of strings containing the paths of the files.
|
||||
*/
|
||||
std::vector<std::string> getFilesListFromDirectory(const std::string& directoryPath);
|
||||
|
||||
// Helper function to check if a path is a directory
|
||||
//bool isDirectoryCached(const struct dirent* entry, const std::string& fullPath) {
|
||||
// struct stat st;
|
||||
// if (stat(fullPath.c_str(), &st) != 0) return false;
|
||||
// return S_ISDIR(st.st_mode);
|
||||
//}
|
||||
|
||||
|
||||
|
||||
void handleDirectory(const std::string& basePath, const std::vector<std::string>& parts, size_t partIndex, std::vector<std::string>& results, bool directoryOnly, size_t maxLines=0);
|
||||
/**
|
||||
* @brief Gets a list of files and folders based on a wildcard pattern.
|
||||
*
|
||||
* This function searches for files and folders in a directory that match the
|
||||
* specified wildcard pattern.
|
||||
*
|
||||
* @param pathPattern The wildcard pattern to match files and folders.
|
||||
* @return A vector of strings containing the paths of matching files and folders.
|
||||
*/
|
||||
std::vector<std::string> getFilesListByWildcards(const std::string& pathPattern, size_t maxLines=0);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
/********************************************************************************
|
||||
* File: global_vars.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file provides the definitions of global constants and paths used
|
||||
* throughout the Ultrahand Overlay project. These constants are essential for
|
||||
* file management and configuration settings within the application.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository:
|
||||
* GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay
|
||||
*
|
||||
* Note: This notice is integral to the project's documentation and must not be
|
||||
* altered or removed.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
|
||||
// Auto-detect constexpr std::string support based on C++ version
|
||||
#if __cplusplus >= 202400L
|
||||
#define CONSTEXPR_STRING constexpr
|
||||
#else
|
||||
#define CONSTEXPR_STRING const
|
||||
#endif
|
||||
|
||||
namespace ult {
|
||||
extern const std::string CONFIG_FILENAME;
|
||||
|
||||
extern const std::string ULTRAHAND_PROJECT_NAME;
|
||||
extern const std::string CAPITAL_ULTRAHAND_PROJECT_NAME;
|
||||
|
||||
extern const std::string ROOT_PATH;
|
||||
extern const std::string SETTINGS_PATH;
|
||||
extern const std::string ULTRAHAND_CONFIG_INI_PATH;
|
||||
extern const std::string TESLA_CONFIG_INI_PATH;
|
||||
extern const std::string LANG_PATH;
|
||||
extern const std::string THEMES_PATH;
|
||||
extern const std::string WALLPAPERS_PATH;
|
||||
extern const std::string FLAGS_PATH;
|
||||
extern const std::string NOTIFICATIONS_PATH;
|
||||
extern const std::string PAYLOADS_PATH;
|
||||
extern const std::string HB_APPSTORE_JSON;
|
||||
|
||||
// Can be overriden with APPEARANCE_OVERRIDE_PATH directive
|
||||
extern std::string THEME_CONFIG_INI_PATH;
|
||||
extern std::string WALLPAPER_PATH;
|
||||
|
||||
|
||||
//#if IS_LAUNCHER_DIRECTIVE
|
||||
extern const std::string SPLIT_PROJECT_NAME_1;
|
||||
extern const std::string SPLIT_PROJECT_NAME_2;
|
||||
extern const std::string BOOT_PACKAGE_FILENAME;
|
||||
extern const std::string EXIT_PACKAGE_FILENAME;
|
||||
extern const std::string PACKAGE_FILENAME;
|
||||
|
||||
extern const std::string DOWNLOADS_PATH;
|
||||
extern const std::string EXPANSION_PATH;
|
||||
extern const std::string FUSE_DATA_INI_PATH;
|
||||
extern const std::string PACKAGE_PATH;
|
||||
extern const std::string OVERLAY_PATH;
|
||||
extern const std::string OVERLAYS_INI_FILEPATH;
|
||||
extern const std::string PACKAGES_INI_FILEPATH;
|
||||
|
||||
extern const std::string NOTIFICATIONS_FLAG_FILEPATH;
|
||||
|
||||
extern const std::set<std::string> PROTECTED_FILES;
|
||||
|
||||
extern const std::string ULTRAHAND_REPO_URL;
|
||||
extern const std::string INCLUDED_THEME_FOLDER_URL;
|
||||
extern const std::string LATEST_RELEASE_INFO_URL;
|
||||
extern const std::string NX_OVLLOADER_ZIP_URL;
|
||||
extern const std::string NX_OVLLOADER_PLUS_ZIP_URL;
|
||||
extern const std::string OLD_NX_OVLLOADER_ZIP_URL;
|
||||
extern const std::string OLD_NX_OVLLOADER_PLUS_ZIP_URL;
|
||||
extern const std::string UPDATER_PAYLOAD_URL;
|
||||
|
||||
extern const std::string LAUNCH_ARGS_STR;
|
||||
extern const std::string USE_LAUNCH_ARGS_STR;
|
||||
extern const std::string USE_QUICK_LAUNCH_STR;
|
||||
extern const std::string USE_BOOT_PACKAGE_STR;
|
||||
extern const std::string USE_EXIT_PACKAGE_STR;
|
||||
extern const std::string USE_LOGGING_STR;
|
||||
|
||||
//#endif
|
||||
|
||||
extern const std::string TESLA_COMBO_STR;
|
||||
extern const std::string ULTRAHAND_COMBO_STR;
|
||||
|
||||
extern const std::string FUSE_STR;
|
||||
extern const std::string TESLA_STR;
|
||||
extern const std::string ERISTA_STR;
|
||||
extern const std::string MARIKO_STR;
|
||||
extern const std::string KEY_COMBO_STR;
|
||||
extern const std::string DEFAULT_LANG_STR;
|
||||
|
||||
|
||||
extern const std::string LIST_STR;
|
||||
extern const std::string LIST_FILE_STR;
|
||||
extern const std::string JSON_STR;
|
||||
extern const std::string JSON_FILE_STR;
|
||||
extern const std::string INI_FILE_STR;
|
||||
extern const std::string HEX_FILE_STR;
|
||||
extern const std::string PACKAGE_STR;
|
||||
extern const std::string PACKAGES_STR;
|
||||
extern const std::string OVERLAY_STR;
|
||||
extern const std::string OVERLAYS_STR;
|
||||
extern const std::string IN_OVERLAY_STR;
|
||||
extern const std::string IN_HIDDEN_OVERLAY_STR;
|
||||
extern const std::string IN_HIDDEN_PACKAGE_STR;
|
||||
extern const std::string FILE_STR;
|
||||
extern const std::string SYSTEM_STR;
|
||||
extern const std::string MODE_STR;
|
||||
extern const std::string GROUPING_STR;
|
||||
extern const std::string FOOTER_STR;
|
||||
extern const std::string TOGGLE_STR;
|
||||
extern const std::string LEFT_STR;
|
||||
extern const std::string RIGHT_STR;
|
||||
extern const std::string CENTER_STR;
|
||||
extern const std::string HIDE_STR;
|
||||
extern const std::string STAR_STR;
|
||||
extern const std::string PRIORITY_STR;
|
||||
extern const std::string ON_STR;
|
||||
extern const std::string OFF_STR;
|
||||
extern const std::string CAPITAL_ON_STR;
|
||||
extern const std::string CAPITAL_OFF_STR;
|
||||
extern const std::string TRUE_STR;
|
||||
extern const std::string FALSE_STR;
|
||||
extern const std::string GLOBAL_STR;
|
||||
extern const std::string DEFAULT_STR;
|
||||
extern const std::string SLOT_STR;
|
||||
extern const std::string OPTION_STR;
|
||||
extern const std::string FORWARDER_STR;
|
||||
extern const std::string TEXT_STR;
|
||||
extern const std::string TABLE_STR;
|
||||
extern const std::string TRACKBAR_STR;
|
||||
extern const std::string STEP_TRACKBAR_STR;
|
||||
extern const std::string NAMED_STEP_TRACKBAR_STR;
|
||||
extern const std::string NULL_STR;
|
||||
extern const std::string THEME_STR;
|
||||
extern const std::string NOT_AVAILABLE_STR;
|
||||
extern const std::string MEMORY_STR;
|
||||
|
||||
// Pre-defined symbols
|
||||
extern const std::string OPTION_SYMBOL;
|
||||
extern const std::string DROPDOWN_SYMBOL;
|
||||
extern const std::string CHECKMARK_SYMBOL;
|
||||
extern const std::string CROSSMARK_SYMBOL;
|
||||
extern const std::string DOWNLOAD_SYMBOL;
|
||||
extern const std::string UNZIP_SYMBOL;
|
||||
extern const std::string COPY_SYMBOL;
|
||||
extern const std::string INPROGRESS_SYMBOL;
|
||||
extern const std::string STAR_SYMBOL;
|
||||
extern const std::string DIVIDER_SYMBOL;
|
||||
|
||||
extern const std::vector<std::string> THROBBER_SYMBOLS;
|
||||
|
||||
extern std::atomic<int> downloadPercentage;
|
||||
extern std::atomic<int> unzipPercentage;
|
||||
extern std::atomic<int> copyPercentage;
|
||||
extern std::atomic<int> displayPercentage;
|
||||
|
||||
void resetPercentages();
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/********************************************************************************
|
||||
* File: hex_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file provides functions for working with hexadecimal data in C++.
|
||||
* It includes functions for converting between ASCII and hexadecimal strings,
|
||||
* finding hexadecimal data offsets in a file, and editing hexadecimal data in a file.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef HEX_FUNCS_HPP
|
||||
#define HEX_FUNCS_HPP
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE // For not using fstream (needs implementing)
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
//#include <cstdio> // Added for FILE and fopen
|
||||
#include <cstring> // Added for std::memcmp
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include <global_vars.hpp>
|
||||
#include <debug_funcs.hpp>
|
||||
#include <string_funcs.hpp>
|
||||
|
||||
|
||||
namespace ult {
|
||||
extern size_t HEX_BUFFER_SIZE;
|
||||
|
||||
|
||||
// For improving the speed of hexing consecutively with the same file and asciiPattern.
|
||||
extern std::unordered_map<std::string, std::string> hexSumCache; // MOVED TO main.cpp
|
||||
|
||||
// Lookup table for hex characters
|
||||
inline constexpr char hexLookup[] = "0123456789ABCDEF";
|
||||
|
||||
|
||||
// ULTRA-FAST hex conversion with lookup table
|
||||
inline constexpr unsigned char hexTable[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
|
||||
0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
|
||||
extern void clearHexSumCache();
|
||||
extern size_t getHexSumCacheSize();
|
||||
|
||||
/**
|
||||
* @brief Converts an ASCII string to a hexadecimal string.
|
||||
*
|
||||
* This function takes an ASCII string as input and converts it into a hexadecimal string.
|
||||
*
|
||||
* @param asciiStr The ASCII string to convert.
|
||||
* @return The corresponding hexadecimal string.
|
||||
*/
|
||||
|
||||
|
||||
// Function to convert ASCII string to Hex string
|
||||
std::string asciiToHex(const std::string& asciiStr);
|
||||
|
||||
/**
|
||||
* @brief Converts a decimal string to a hexadecimal string.
|
||||
*
|
||||
* This function takes a decimal string as input and converts it into a hexadecimal string.
|
||||
*
|
||||
* @param decimalStr The decimal string to convert.
|
||||
* @return The corresponding hexadecimal string.
|
||||
*/
|
||||
std::string decimalToHex(const std::string& decimalStr, int byteGroupSize = 2);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a hexadecimal string to a decimal string.
|
||||
*
|
||||
* This function takes a hexadecimal string as input and converts it into a decimal string.
|
||||
*
|
||||
* @param hexStr The hexadecimal string to convert.
|
||||
* @return The corresponding decimal string.
|
||||
*/
|
||||
std::string hexToDecimal(const std::string& hexStr);
|
||||
|
||||
|
||||
|
||||
std::string hexToReversedHex(const std::string& hexadecimal, int order = 2);
|
||||
|
||||
/**
|
||||
* @brief Converts a decimal string to a reversed hexadecimal string.
|
||||
*
|
||||
* This function takes a decimal string as input, converts it into a hexadecimal
|
||||
* string, and reverses the resulting hexadecimal string in groups of byteGroupSize.
|
||||
*
|
||||
* @param decimalStr The decimal string to convert.
|
||||
* @param byteGroupSize The grouping byteGroupSize for reversing the hexadecimal string.
|
||||
* @return The reversed hexadecimal string.
|
||||
*/
|
||||
std::string decimalToReversedHex(const std::string& decimalStr, int byteGroupSize = 2);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Finds the offsets of hexadecimal data in a file.
|
||||
*
|
||||
* This function searches for occurrences of hexadecimal data in a binary file
|
||||
* and returns the file offsets where the data is found.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param hexData The hexadecimal data to search for.
|
||||
* @return A vector of strings containing the file offsets where the data is found.
|
||||
*/
|
||||
std::vector<std::string> findHexDataOffsets(const std::string& filePath, const std::string& hexData);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Edits hexadecimal data in a file at a specified offset.
|
||||
*
|
||||
* This function opens a binary file, seeks to a specified offset, and replaces
|
||||
* the data at that offset with the provided hexadecimal data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param offsetStr The offset in the file to performthe edit.
|
||||
* @param hexData The hexadecimal data to replace at the offset.
|
||||
*/
|
||||
void hexEditByOffset(const std::string& filePath, const std::string& offsetStr, const std::string& hexData);
|
||||
|
||||
/**
|
||||
* @brief Edits a specific offset in a file with custom hexadecimal data.
|
||||
*
|
||||
* This function searches for a custom pattern in the file and calculates a new offset
|
||||
* based on user-provided offsetStr and the found pattern. It then replaces the data
|
||||
* at the calculated offset with the provided hexadecimal data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param offsetStr The user-provided offset for the edit.
|
||||
* @param customPattern The custom pattern to search for in the file.
|
||||
* @param hexDataReplacement The hexadecimal data to replace at the calculated offset.
|
||||
* @param occurrence The occurrence/index of the data to replace (default is "0" to replace all occurrences).
|
||||
*/
|
||||
void hexEditByCustomOffset(const std::string& filePath, const std::string& customAsciiPattern, const std::string& offsetStr, const std::string& hexDataReplacement, size_t occurrence = 0);
|
||||
|
||||
/**
|
||||
* @brief Finds and replaces hexadecimal data in a file.
|
||||
*
|
||||
* This function searches for occurrences of hexadecimal data in a binary file
|
||||
* and replaces them with a specified hexadecimal replacement data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param hexDataToReplace The hexadecimal data to search for and replace.
|
||||
* @param hexDataReplacement The hexadecimal data to replace with.
|
||||
* @param occurrence The occurrence/index of the data to replace (default is "0" to replace all occurrences).
|
||||
*/
|
||||
void hexEditFindReplace(const std::string& filePath, const std::string& hexDataToReplace, const std::string& hexDataReplacement, size_t occurrence = 0);
|
||||
|
||||
/**
|
||||
* @brief Finds and replaces hexadecimal data in a file.
|
||||
*
|
||||
* This function searches for occurrences of hexadecimal data in a binary file
|
||||
* and replaces them with a specified hexadecimal replacement data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param hexDataToReplace The hexadecimal data to search for and replace.
|
||||
* @param hexDataReplacement The hexadecimal data to replace with.
|
||||
* @param occurrence The occurrence/index of the data to replace (default is "0" to replace all occurrences).
|
||||
*/
|
||||
std::string parseHexDataAtCustomOffset(const std::string& filePath, const std::string& customAsciiPattern, const std::string& offsetStr, size_t length, size_t occurrence = 0);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Finds and replaces hexadecimal data in a file.
|
||||
*
|
||||
* This function searches for occurrences of hexadecimal data in a binary file
|
||||
* and replaces them with a specified hexadecimal replacement data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param hexDataToReplace The hexadecimal data to search for and replace.
|
||||
* @param hexDataReplacement The hexadecimal data to replace with.
|
||||
* @param occurrence The occurrence/index of the data to replace (default is "0" to replace all occurrences).
|
||||
*/
|
||||
|
||||
std::string replaceHexPlaceholder(const std::string& arg, const std::string& hexPath);
|
||||
|
||||
|
||||
|
||||
std::string extractVersionFromBinary(const std::string &filePath);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,344 @@
|
||||
/********************************************************************************
|
||||
* File: ini_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file provides functions for working with INI (Initialization) files
|
||||
* in C++. It includes functions for reading, parsing, and editing INI files,
|
||||
* as well as cleaning INI file formatting.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef INI_FUNCS_HPP
|
||||
#define INI_FUNCS_HPP
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE // For not using fstream (needs implementing)
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
//#include "nx_fstream.hpp"
|
||||
#endif
|
||||
|
||||
#include <cstring> // For std::string, strlen(), etc.
|
||||
#include <string> // For std::string
|
||||
#include <vector> // For std::vector
|
||||
#include <map> // For std::map
|
||||
//#include <sstream> // For std::istringstream
|
||||
#include <algorithm> // For std::remove_if
|
||||
//#include <cctype> // For ::isspace
|
||||
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
#include "get_funcs.hpp"
|
||||
#include "path_funcs.hpp"
|
||||
|
||||
namespace ult {
|
||||
|
||||
extern void clearIniMutexCache();
|
||||
|
||||
extern size_t INI_BUFFER_SIZE;
|
||||
extern size_t INI_BUFFER_LARGE;
|
||||
|
||||
/**
|
||||
* @brief Represents a package header structure.
|
||||
*
|
||||
* This structure holds information about a package header, including version,
|
||||
* creator, and description.
|
||||
*/
|
||||
struct PackageHeader {
|
||||
std::string title;
|
||||
std::string display_title;
|
||||
std::string version;
|
||||
std::string creator;
|
||||
std::string about;
|
||||
std::string credits;
|
||||
std::string color;
|
||||
std::string show_version;
|
||||
std::string show_widget;
|
||||
|
||||
void clear() {
|
||||
title.clear();
|
||||
display_title.clear();
|
||||
version.clear();
|
||||
creator.clear();
|
||||
about.clear();
|
||||
credits.clear();
|
||||
color.clear();
|
||||
show_version.clear();
|
||||
show_widget.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Retrieves the package header information from an INI file.
|
||||
*
|
||||
* This function parses an INI file and extracts the package header information.
|
||||
*
|
||||
* @param filePath The path to the INI file.
|
||||
* @return The package header structure.
|
||||
*/
|
||||
PackageHeader getPackageHeaderFromIni(const std::string& filePath);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Splits a string into a vector of substrings using a specified delimiter.
|
||||
*
|
||||
* This function splits a given string into multiple substrings based on the specified delimiter.
|
||||
*
|
||||
* @param str The input string to be split.
|
||||
* @param delim The delimiter character used for splitting (default is space ' ').
|
||||
* @return A vector of substrings obtained by splitting the input string.
|
||||
*/
|
||||
std::vector<std::string> split(const std::string& str, char delim = ' ');
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parses an INI-formatted string into a map of sections and key-value pairs.
|
||||
*
|
||||
* This function parses an INI-formatted string and organizes the data into a map,
|
||||
* where sections are keys and key-value pairs are stored within each section.
|
||||
*
|
||||
* @param str The INI-formatted string to parse.
|
||||
* @return A map representing the parsed INI data.
|
||||
*/
|
||||
std::map<std::string, std::map<std::string, std::string>> parseIni(const std::string &str);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parses an INI file and returns its content as a map of sections and key-value pairs.
|
||||
*
|
||||
* This function reads the contents of an INI file located at the specified path,
|
||||
* parses it into a map structure, where section names are keys and key-value pairs
|
||||
* are stored within each section.
|
||||
*
|
||||
* @param configIniPath The path to the INI file to be parsed.
|
||||
* @return A map representing the parsed INI data.
|
||||
*/
|
||||
std::map<std::string, std::map<std::string, std::string>> getParsedDataFromIniFile(const std::string& configIniPath);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parses an INI file and retrieves key-value pairs from a specific section.
|
||||
*
|
||||
* This function reads the contents of an INI file located at the specified path,
|
||||
* and returns the key-value pairs within a specific section.
|
||||
*
|
||||
* @param configIniPath The path to the INI file to be parsed.
|
||||
* @param sectionName The name of the section to retrieve key-value pairs from.
|
||||
* @return A map representing the key-value pairs in the specified section.
|
||||
*/
|
||||
std::map<std::string, std::string> getKeyValuePairsFromSection(const std::string& configIniPath, const std::string& sectionName);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parses sections from an INI file and returns them as a list of strings.
|
||||
*
|
||||
* This function reads an INI file and extracts the section names from it.
|
||||
*
|
||||
* @param filePath The path to the INI file.
|
||||
* @return A vector of section names.
|
||||
*/
|
||||
std::vector<std::string> parseSectionsFromIni(const std::string& filePath);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parses a specific value from a section and key in an INI file.
|
||||
*
|
||||
* @param filePath The path to the INI file.
|
||||
* @param sectionName The name of the section containing the desired key.
|
||||
* @param keyName The name of the key whose value is to be retrieved.
|
||||
* @return The value as a string, or an empty string if the key or section isn't found.
|
||||
*/
|
||||
std::string parseValueFromIniSection(const std::string& filePath, const std::string& sectionName, const std::string& keyName);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Cleans the formatting of an INI file by removing empty lines and standardizing section formatting.
|
||||
*
|
||||
* This function takes an INI file located at the specified path, removes empty lines,
|
||||
* and standardizes the formatting of sections by ensuring that there is a newline
|
||||
* between each section's closing ']' and the next section's opening '['.
|
||||
*
|
||||
* @param filePath The path to the INI file to be cleaned.
|
||||
*/
|
||||
void cleanIniFormatting(const std::string& filePath);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Modifies or creates an INI file by adding or updating key-value pairs in the specified section.
|
||||
*
|
||||
* This function attempts to open the specified INI file for reading. If the file doesn't exist,
|
||||
* it creates a new file and adds the specified section and key-value pair. If the file exists,
|
||||
* it reads its contents, modifies or adds the key-value pair in the specified section, and saves
|
||||
* the changes back to the original file.
|
||||
*
|
||||
* @param fileToEdit The path to the INI file to be modified or created.
|
||||
* @param desiredSection The name of the section in which the key-value pair should be added or updated.
|
||||
* @param desiredKey The key for the key-value pair to be added or updated.
|
||||
* @param desiredValue The new value for the key-value pair.
|
||||
* @param desiredNewKey (Optional) If provided, the function will rename the key while preserving the original value.
|
||||
*/
|
||||
void setIniFile(const std::string& fileToEdit, const std::string& desiredSection, const std::string& desiredKey, const std::string& desiredValue, const std::string& desiredNewKey = "", const std::string& comment = "");
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a key in an INI file within the specified section and cleans the formatting.
|
||||
*
|
||||
* This function sets the value of the specified key within the given section of the INI file.
|
||||
* If the key or section does not exist, it creates them. After updating the INI file,
|
||||
* it cleans the formatting to ensure proper INI file structure.
|
||||
*
|
||||
* @param fileToEdit The path to the INI file to be modified or created.
|
||||
* @param desiredSection The name of the section in which the key-value pair should be added or updated.
|
||||
* @param desiredKey The key for the key-value pair to be added or updated.
|
||||
* @param desiredValue The new value for the key-value pair.
|
||||
*/
|
||||
void setIniFileValue(const std::string& fileToEdit, const std::string& desiredSection, const std::string& desiredKey, const std::string& desiredValue, const std::string& comment="");
|
||||
|
||||
/**
|
||||
* @brief Sets the key name to a new name in an INI file within the specified section and cleans the formatting.
|
||||
*
|
||||
* This function sets the key name to a new name within the given section of the INI file.
|
||||
* If the key or section does not exist, it creates them. After updating the INI file,
|
||||
* it cleans the formatting to ensure proper INI file structure.
|
||||
*
|
||||
* @param fileToEdit The path to the INI file to be modified or created.
|
||||
* @param desiredSection The name of the section in which the key-name change should occur.
|
||||
* @param desiredKey The key name to be changed.
|
||||
* @param desiredNewKey The new key name to replace the original key name.
|
||||
*/
|
||||
void setIniFileKey(const std::string& fileToEdit, const std::string& desiredSection, const std::string& desiredKey, const std::string& desiredNewKey, const std::string& comment="");
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Adds a new section to an INI file.
|
||||
*
|
||||
* This function adds a new section with the specified name to the INI file located at the
|
||||
* specified path. If the section already exists, it does nothing.
|
||||
*
|
||||
* @param filePath The path to the INI file.
|
||||
* @param sectionName The name of the section to add.
|
||||
*/
|
||||
void addIniSection(const std::string& filePath, const std::string& sectionName);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Renames a section in an INI file.
|
||||
*
|
||||
* This function renames the section with the specified current name to the specified new name
|
||||
* in the INI file located at the specified path. If the current section does not exist, or if the
|
||||
* new section name already exists, it does nothing.
|
||||
*
|
||||
* @param filePath The path to the INI file.
|
||||
* @param currentSectionName The name of the section to rename.
|
||||
* @param newSectionName The new name for the section.
|
||||
*/
|
||||
void renameIniSection(const std::string& filePath, const std::string& currentSectionName, const std::string& newSectionName);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes a section from an INI file.
|
||||
*
|
||||
* This function removes the section with the specified name, including all its associated key-value
|
||||
* pairs, from the INI file located at the specified path. If the section does not exist in the file,
|
||||
* it does nothing.
|
||||
*
|
||||
* @param filePath The path to the INI file.
|
||||
* @param sectionName The name of the section to remove.
|
||||
*/
|
||||
void removeIniSection(const std::string& filePath, const std::string& sectionName);
|
||||
|
||||
|
||||
// Removes a key-value pair from an ini accordingly.
|
||||
void removeIniKey(const std::string& filePath, const std::string& sectionName, const std::string& keyName);
|
||||
|
||||
|
||||
//void saveIniFileData(const std::string& filePath, const std::map<std::string, std::map<std::string, std::string>>& data) {
|
||||
// std::ofstream file(filePath);
|
||||
// if (!file.is_open()) {
|
||||
// // Handle error: could not open file
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// for (const auto& section : data) {
|
||||
// file << "[" << section.first << "]\n";
|
||||
// for (const auto& kv : section.second) {
|
||||
// file << kv.first << "=" << kv.second << "\n";
|
||||
// }
|
||||
// file << "\n"; // Separate sections with a newline
|
||||
// }
|
||||
//
|
||||
// file.close();
|
||||
//}
|
||||
|
||||
|
||||
void syncIniValue(std::map<std::string, std::map<std::string, std::string>>& packageConfigData,
|
||||
const std::string& packageConfigIniPath,
|
||||
const std::string& optionName,
|
||||
const std::string& key,
|
||||
std::string& value);
|
||||
|
||||
/**
|
||||
* @brief Parses a command line into individual parts, handling quoted strings.
|
||||
*
|
||||
* @param line The command line to parse.
|
||||
* @return A vector of strings containing the parsed command parts.
|
||||
*/
|
||||
std::vector<std::string> parseCommandLine(const std::string& line);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Loads and parses options from an INI file.
|
||||
*
|
||||
* This function reads and parses options from an INI file, organizing them by section.
|
||||
*
|
||||
* @param packageIniPath The path to the INI file.
|
||||
* @return A vector containing pairs of section names and their associated key-value pairs.
|
||||
*/
|
||||
std::vector<std::pair<std::string, std::vector<std::vector<std::string>>>> loadOptionsFromIni(const std::string& packageIniPath);
|
||||
|
||||
/**
|
||||
* @brief Loads a specific section from an INI file.
|
||||
*
|
||||
* This function reads and parses a specific section from an INI file.
|
||||
*
|
||||
* @param packageIniPath The path to the INI file.
|
||||
* @param sectionName The name of the section to load.
|
||||
* @return A vector of commands within the specified section.
|
||||
*/
|
||||
std::vector<std::vector<std::string>> loadSpecificSectionFromIni(const std::string& packageIniPath, const std::string& sectionName);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Saves INI data structure to a file.
|
||||
*
|
||||
* This function writes a complete INI data structure to the specified file path.
|
||||
* The data structure should be organized as sections containing key-value pairs.
|
||||
*
|
||||
* @param filePath The path to the INI file to write.
|
||||
* @param data The complete INI data structure to save.
|
||||
*/
|
||||
void saveIniFileData(const std::string& filePath, const std::map<std::string, std::map<std::string, std::string>>& data);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
/********************************************************************************
|
||||
* File: json_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file provides functions for working with JSON files in C++ using
|
||||
* the `cJSON` library. It includes a function to read JSON data from a file.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
#pragma once
|
||||
#ifndef JSON_FUNCS_HPP
|
||||
#define JSON_FUNCS_HPP
|
||||
#if !USING_FSTREAM_DIRECTIVE // For not using fstream (needs implementing)
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <cJSON.h>
|
||||
#include "string_funcs.hpp"
|
||||
#include <switch.h>
|
||||
|
||||
namespace ult {
|
||||
// Define json_t as an opaque type to maintain API compatibility
|
||||
typedef void json_t;
|
||||
|
||||
// Define a custom deleter for json_t*
|
||||
struct JsonDeleter {
|
||||
void operator()(json_t* json) const {
|
||||
if (json) {
|
||||
cJSON_Delete(reinterpret_cast<cJSON*>(json));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reads JSON data from a file and returns it as a `json_t` object.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @return A `json_t` object representing the parsed JSON data. Returns `nullptr` on error.
|
||||
*/
|
||||
json_t* readJsonFromFile(const std::string& filePath);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parses a JSON string into a json_t object.
|
||||
*
|
||||
* This function takes a JSON string as input and parses it into a json_t object using cJSON library's `cJSON_Parse` function.
|
||||
* If parsing fails, it logs the error and returns nullptr.
|
||||
*
|
||||
* @param input The input JSON string to parse.
|
||||
* @return A json_t object representing the parsed JSON, or nullptr if parsing fails.
|
||||
*/
|
||||
json_t* stringToJson(const std::string& input);
|
||||
|
||||
|
||||
// Function to get a string from a JSON object
|
||||
std::string getStringFromJson(const json_t* root, const char* key);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Loads a JSON file from the specified path and retrieves a string value for a given key.
|
||||
*
|
||||
* This function combines the functionality of loading a JSON file and retrieving a string value associated
|
||||
* with a given key in a single step.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param key The key whose associated string value is to be retrieved.
|
||||
* @return A string containing the value associated with the given key, or an empty string if the key is not found.
|
||||
*/
|
||||
std::string getStringFromJsonFile(const std::string& filePath, const std::string& key);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sets a value in a JSON file, creating the file if it doesn't exist.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param key The key to set.
|
||||
* @param value The value to set (auto-detected type).
|
||||
* @param createIfNotExists Whether to create the file if it doesn't exist.
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
bool setJsonValue(const std::string& filePath, const std::string& key, const std::string& value, bool createIfNotExists = false);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Renames a key in a JSON file.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param oldKey The current key name.
|
||||
* @param newKey The new key name.
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
bool renameJsonKey(const std::string& filePath, const std::string& oldKey, const std::string& newKey);
|
||||
|
||||
|
||||
void pushNotificationJson(const std::string& text, size_t fontSize=28);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
/********************************************************************************
|
||||
* File: list_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file contains function declarations and utility functions related
|
||||
* to working with lists and vectors of strings. These functions are used in the
|
||||
* Ultrahand Overlay project to perform various operations on lists, such as
|
||||
* removing entries, filtering, and more.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef LIST_FUNCS_HPP
|
||||
#define LIST_FUNCS_HPP
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE // For not using fstream (needs implementing)
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include "debug_funcs.hpp"
|
||||
#include "string_funcs.hpp"
|
||||
#include "get_funcs.hpp"
|
||||
|
||||
|
||||
namespace ult {
|
||||
|
||||
std::vector<std::string> splitIniList(const std::string& value);
|
||||
|
||||
|
||||
std::string joinIniList(const std::vector<std::string>& list);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes entries from a vector of strings that match a specified entry.
|
||||
*
|
||||
* This function removes entries from the `itemsList` vector of strings that match the `entry`.
|
||||
*
|
||||
* @param entry The entry to be compared against the elements in `itemsList`.
|
||||
* @param itemsList The vector of strings from which matching entries will be removed.
|
||||
*/
|
||||
void removeEntryFromList(const std::string& entry, std::vector<std::string>& itemsList);
|
||||
|
||||
/**
|
||||
* @brief Filters a list of strings based on a specified filter list.
|
||||
*
|
||||
* This function filters a list of strings (`itemsList`) by removing entries that match any
|
||||
* of the criteria specified in the `filterList`. It uses the `removeEntryFromList` function
|
||||
* to perform the removal.
|
||||
*
|
||||
* @param filterList The list of entries to filter by. Entries in `itemsList` matching any entry in this list will be removed.
|
||||
* @param itemsList The list of stringsto be filtered.
|
||||
*/
|
||||
void filterItemsList(const std::vector<std::string>& filterList, std::vector<std::string>& itemsList);
|
||||
|
||||
|
||||
// Function to read file into a vector of strings
|
||||
std::vector<std::string> readListFromFile(const std::string& filePath, size_t maxLines=0);
|
||||
|
||||
|
||||
// Function to get an entry from the list based on the index
|
||||
std::string getEntryFromListFile(const std::string& listPath, size_t listIndex);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Splits a string into a vector of strings using a delimiter.
|
||||
*
|
||||
* This function splits the input string into multiple strings using the specified delimiter.
|
||||
*
|
||||
* @param str The input string to split.
|
||||
* @return A vector of strings containing the split values.
|
||||
*/
|
||||
std::vector<std::string> stringToList(const std::string& str);
|
||||
|
||||
|
||||
// Function to read file into a set of strings
|
||||
std::unordered_set<std::string> readSetFromFile(const std::string& filePath);
|
||||
|
||||
|
||||
// Function to write a set to a file
|
||||
void writeSetToFile(const std::unordered_set<std::string>& fileSet, const std::string& filePath);
|
||||
|
||||
|
||||
// Function to compare two file lists and save duplicates to an output file
|
||||
void compareFilesLists(const std::string& txtFilePath1, const std::string& txtFilePath2, const std::string& outputTxtFilePath);
|
||||
|
||||
|
||||
// Helper function to read a text file and process each line with a callback
|
||||
void processFileLines(const std::string& filePath, const std::function<void(const std::string&)>& callback);
|
||||
|
||||
|
||||
void compareWildcardFilesLists(const std::string& wildcardPatternFilePath, const std::string& txtFilePath, const std::string& outputTxtFilePath);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
/********************************************************************************
|
||||
* File: mod_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file contains function declarations and utility functions for IPS
|
||||
* binary generations. These functions are used in the Ultrahand Overlay project
|
||||
* to convert `.pchtxt` mods into `.ips` binaries.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef MOD_FUNCS_HPP
|
||||
#define MOD_FUNCS_HPP
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE // For not using fstream (needs implementing)
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
//#include <sstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include "debug_funcs.hpp"
|
||||
#include "path_funcs.hpp"
|
||||
#include "hex_funcs.hpp"
|
||||
#include <iomanip> // Include this header for std::setw and std::setfill
|
||||
|
||||
namespace ult {
|
||||
inline constexpr const char* IPS32_HEAD_MAGIC = "IPS32";
|
||||
inline constexpr const char* IPS32_FOOT_MAGIC = "EEOF";
|
||||
|
||||
//const std::string CHEAT_HEADER = "// auto generated by pchtxt2cheat\n\n";
|
||||
extern const std::string CHEAT_TYPE;
|
||||
extern const std::string CHEAT_EXT;
|
||||
extern const std::string CHEAT_ENCODING;
|
||||
|
||||
/**
|
||||
* @brief Checks if a cheat already exists in the cheat file.
|
||||
* @param cheatFilePath The path to the cheat file.
|
||||
* @param newCheat The new cheat to check.
|
||||
* @return True if the cheat exists, otherwise false.
|
||||
*/
|
||||
bool cheatExists(const std::string &cheatFilePath, const std::string &newCheat);
|
||||
|
||||
/**
|
||||
* @brief Appends a new cheat to the cheat file.
|
||||
* @param cheatFilePath The path to the cheat file.
|
||||
* @param newCheat The new cheat to append.
|
||||
*/
|
||||
void appendCheatToFile(const std::string &cheatFilePath, const std::string &newCheat);
|
||||
|
||||
/**
|
||||
* @brief Extracts the cheat name from the given file path.
|
||||
* @param filePath The full file path.
|
||||
* @return The extracted cheat name.
|
||||
*/
|
||||
std::string extractCheatName(const std::string &filePath);
|
||||
|
||||
// Helper function to determine if a string is a valid title ID
|
||||
bool isValidTitleID(const std::string &str);
|
||||
|
||||
// Function to find the title ID in the text, avoiding the @nsobid- line
|
||||
std::string findTitleID(const std::string &text);
|
||||
|
||||
/**
|
||||
* @brief Converts a .pchtxt file to a cheat file.
|
||||
* @param pchtxtPath The file path to the .pchtxt file.
|
||||
* @param cheatName The name of the cheat.
|
||||
* @param outCheatPath The file path for the output cheat file.
|
||||
* @return True if the conversion was successful, false otherwise.
|
||||
*/
|
||||
bool pchtxt2cheat(const std::string &pchtxtPath, std::string cheatName = "", std::string outCheatPath = "");
|
||||
|
||||
// Corrected helper function to convert values to big-endian format
|
||||
uint32_t toBigEndian(uint32_t value);
|
||||
|
||||
uint16_t toBigEndian(uint16_t value);
|
||||
|
||||
// Helper function to convert a vector of bytes to a hex string for logging
|
||||
|
||||
std::string hexToString(const std::vector<uint8_t>& bytes);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a .pchtxt file to an IPS file using fstream.
|
||||
*
|
||||
* This function reads the contents of a .pchtxt file, extracts the address-value pairs,
|
||||
* and generates an IPS file with the provided output folder.
|
||||
*
|
||||
* @param pchtxtPath The file path to the .pchtxt file.
|
||||
* @param outputFolder The folder path for the output IPS file.
|
||||
* @return True if the conversion was successful, false otherwise.
|
||||
*/
|
||||
bool pchtxt2ips(const std::string& pchtxtPath, const std::string& outputFolder);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,279 @@
|
||||
/********************************************************************************
|
||||
* File: path_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file contains function declarations and utility functions related
|
||||
* to file and directory path manipulation. These functions are used in the
|
||||
* Ultrahand Overlay project to handle file operations, such as creating
|
||||
* directories, moving files, and more.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef PATH_FUNCS_HPP
|
||||
#define PATH_FUNCS_HPP
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE // For not using fstream (needs implementing)
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include "global_vars.hpp"
|
||||
#include "string_funcs.hpp"
|
||||
#include "get_funcs.hpp"
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace ult {
|
||||
extern std::atomic<bool> abortFileOp;
|
||||
|
||||
extern size_t COPY_BUFFER_SIZE; // Made const for thread safety
|
||||
extern std::atomic<int> copyPercentage;
|
||||
|
||||
// Mutex for thread-safe logging operations
|
||||
extern std::mutex logMutex;
|
||||
|
||||
/**
|
||||
* @brief Checks if a path points to a directory.
|
||||
*
|
||||
* This function checks if the specified path points to a directory.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the path is a directory, false otherwise.
|
||||
*/
|
||||
bool isDirectory(const std::string& path);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Checks if a path points to a file.
|
||||
*
|
||||
* This function checks if the specified path points to a file.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the path is a file, false otherwise.
|
||||
*/
|
||||
bool isFile(const std::string& path);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Checks if a path points to a file or directory.
|
||||
*
|
||||
* This function checks if the specified path points to either a file or a directory.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the path points to a file or directory, false otherwise.
|
||||
*/
|
||||
bool isFileOrDirectory(const std::string& path);
|
||||
|
||||
|
||||
bool isDirectoryEmpty(const std::string& dirPath);
|
||||
|
||||
/**
|
||||
* @brief Creates a single directory if it doesn't exist.
|
||||
*
|
||||
* This function checks if the specified directory exists, and if not, it creates the directory.
|
||||
*
|
||||
* @param directoryPath The path of the directory to be created.
|
||||
*/
|
||||
void createSingleDirectory(const std::string& directoryPath);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Creates a directory and its parent directories if they don't exist.
|
||||
*
|
||||
* This function creates a directory specified by `directoryPath` and also creates any parent directories
|
||||
* if they don't exist. It handles nested directory creation.
|
||||
*
|
||||
* @param directoryPath The path of the directory to be created.
|
||||
*/
|
||||
void createDirectory(const std::string& directoryPath);
|
||||
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
void writeLog(FILE* logFile, const std::string& line);
|
||||
#else
|
||||
void writeLog(std::ofstream& logFile, const std::string& line);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Creates a text file with the specified content.
|
||||
*
|
||||
* This function creates a text file specified by `filePath` and writes the given `content` to the file.
|
||||
*
|
||||
* @param filePath The path of the text file to be created.
|
||||
* @param content The content to be written to the text file.
|
||||
*/
|
||||
void createTextFile(const std::string& filePath, const std::string& content);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deletes a file or directory.
|
||||
*
|
||||
* This function deletes the file or directory specified by `path`. It can delete both files and directories.
|
||||
*
|
||||
* @param path The path of the file or directory to be deleted.
|
||||
*/
|
||||
void deleteFileOrDirectory(const std::string& pathToDelete, const std::string& logSource = "");
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deletes files or directories that match a specified pattern.
|
||||
*
|
||||
* This function deletes files or directories specified by `pathPattern` by matching against a pattern.
|
||||
* It identifies files or directories that match the pattern and deletes them.
|
||||
*
|
||||
* @param pathPattern The pattern used to match and delete files or directories.
|
||||
*/
|
||||
void deleteFileOrDirectoryByPattern(const std::string& pathPattern, const std::string& logSource = "");
|
||||
|
||||
|
||||
void moveDirectory(const std::string& sourcePath, const std::string& destinationPath,
|
||||
const std::string& logSource = "", const std::string& logDestination = "");
|
||||
|
||||
|
||||
|
||||
bool moveFile(const std::string& sourcePath, const std::string& destinationPath,
|
||||
const std::string& logSource = "", const std::string& logDestination = "");
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Moves a file or directory to a new destination.
|
||||
*
|
||||
* This function moves a file or directory from the `sourcePath` to the `destinationPath`. It can handle both
|
||||
* files and directories and ensures that the destination directory exists before moving.
|
||||
*
|
||||
* @param sourcePath The path of the source file or directory.
|
||||
* @param destinationPath The path of the destination where the file or directory will be moved.
|
||||
*/
|
||||
void moveFileOrDirectory(const std::string& sourcePath, const std::string& destinationPath,
|
||||
const std::string& logSource = "", const std::string& logDestination = "");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Moves files or directories matching a specified pattern to a destination directory.
|
||||
*
|
||||
* This function identifies files or directories that match the `sourcePathPattern` and moves them to the `destinationPath`.
|
||||
* It processes each matching entry in the source directory pattern and moves them to the specified destination.
|
||||
*
|
||||
* @param sourcePathPattern The pattern used to match files or directories to be moved.
|
||||
* @param destinationPath The destination directory where matching files or directories will be moved.
|
||||
*/
|
||||
void moveFilesOrDirectoriesByPattern(const std::string& sourcePathPattern, const std::string& destinationPath,
|
||||
const std::string& logSource = "", const std::string& logDestination = "");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copies a single file from the source path to the destination path.
|
||||
*
|
||||
* This function copies a single file specified by `fromFile` to the location specified by `toFile`.
|
||||
*
|
||||
* @param fromFile The path of the source file to be copied.
|
||||
* @param toFile The path of the destination where the file will be copied.
|
||||
*/
|
||||
void copySingleFile(const std::string& fromFile, const std::string& toFile, long long& totalBytesCopied, const long long totalSize,
|
||||
const std::string& logSource = "", const std::string& logDestination = "");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Recursively calculates the total size of the given file or directory.
|
||||
* @param path The path to the file or directory.
|
||||
* @return The total size in bytes of all files within the directory or the size of a file.
|
||||
*/
|
||||
long long getTotalSize(const std::string& path);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copies a file or directory from the source path to the destination path.
|
||||
*
|
||||
* This function copies a file or directory specified by `fromFileOrDirectory` to the location specified by `toFileOrDirectory`.
|
||||
* If the source is a regular file, it copies the file to the destination. If the source is a directory, it recursively copies
|
||||
* the entire directory and its contents to the destination.
|
||||
*
|
||||
* @param fromPath The path of the source file or directory to be copied.
|
||||
* @param toPath The path of the destination where the file or directory will be copied.
|
||||
*/
|
||||
void copyFileOrDirectory(const std::string& fromPath, const std::string& toPath, long long* totalBytesCopied = nullptr, long long totalSize = 0,
|
||||
const std::string& logSource = "", const std::string& logDestination = "");
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copies files or directories matching a specified pattern to a destination directory.
|
||||
*
|
||||
* This function identifies files or directories that match the `sourcePathPattern` and copies them to the `toDirectory`.
|
||||
* It processes each matching entry in the source directory pattern and copies them to the specified destination.
|
||||
*
|
||||
* @param sourcePathPattern The pattern used to match files or directories to be copied.
|
||||
* @param toDirectory The destination directory where matching files or directories will be copied.
|
||||
*/
|
||||
void copyFileOrDirectoryByPattern(const std::string& sourcePathPattern, const std::string& toDirectory,
|
||||
const std::string& logSource = "", const std::string& logDestination = "");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Mirrors the deletion of files from a source directory to a target directory.
|
||||
*
|
||||
* This function mirrors the deletion of files from a `sourcePath` directory to a `targetPath` directory.
|
||||
* It deletes corresponding files in the `targetPath` that match the source directory structure.
|
||||
*
|
||||
* @param sourcePath The path of the source directory.
|
||||
* @param targetPath The path of the target directory where files will be mirrored and deleted.
|
||||
* Default is "sdmc:/". You can specify a different target path if needed.
|
||||
*/
|
||||
void mirrorFiles(const std::string& sourcePath, const std::string targetPath, const std::string mode);
|
||||
|
||||
|
||||
/**
|
||||
* @brief For each match of the wildcard pattern, creates an empty text file
|
||||
* named basename.txt inside the output directory.
|
||||
* Uses FILE* if !USING_FSTREAM_DIRECTIVE is defined, otherwise uses std::ofstream.
|
||||
*
|
||||
* @param wildcardPattern A path with a wildcard, such as /some/path/[*].
|
||||
* Each match results in a file named after the basename.
|
||||
* @param outputDir Directory where the output files will be written.
|
||||
* Created if it doesn't already exist.
|
||||
*/
|
||||
void createFlagFiles(const std::string& wildcardPattern, const std::string& outputDir);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes all files starting with "._" from a directory and its subdirectories.
|
||||
*
|
||||
* This function recursively scans the specified directory and removes all files
|
||||
* whose names start with "._" (commonly macOS metadata files). It processes
|
||||
* all subdirectories recursively.
|
||||
*
|
||||
* @param sourcePath The path of the directory to clean.
|
||||
*/
|
||||
void dotCleanDirectory(const std::string& sourcePath);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,273 @@
|
||||
/********************************************************************************
|
||||
* File: string_funcs.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This header file contains function declarations and utility functions for string
|
||||
* manipulation. These functions are used in the Ultrahand Overlay project to
|
||||
* perform operations like trimming whitespaces, removing quotes, replacing
|
||||
* multiple slashes, and more.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef STRING_FUNCS_HPP
|
||||
#define STRING_FUNCS_HPP
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
//#include <jansson.h>
|
||||
//#include <regex>
|
||||
#include <algorithm>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include "global_vars.hpp"
|
||||
#include "debug_funcs.hpp"
|
||||
|
||||
namespace ult {
|
||||
|
||||
extern std::string to_string(int value);
|
||||
extern int stoi(const std::string& str, std::size_t* pos = nullptr, int base = 10);
|
||||
extern float stof(const std::string& str);
|
||||
|
||||
|
||||
/**
|
||||
* @brief A lightweight string stream class that mimics basic functionality of std::istringstream.
|
||||
*/
|
||||
class StringStream {
|
||||
public:
|
||||
StringStream() : position(0), hexMode(false), validState(true) {}
|
||||
|
||||
|
||||
// Add this constructor to accept a string
|
||||
StringStream(const std::string& input) : data(input), position(0), hexMode(false) {}
|
||||
|
||||
// Set hex mode
|
||||
StringStream& hex() {
|
||||
hexMode = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Reset hex mode
|
||||
StringStream& resetHex() {
|
||||
hexMode = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Mimics std::getline() with a delimiter
|
||||
bool getline(std::string& output, char delimiter);
|
||||
|
||||
// Mimics operator >> to split by whitespace
|
||||
StringStream& operator>>(std::string& output);
|
||||
|
||||
// Overload the << operator to insert strings and integers
|
||||
StringStream& operator<<(const std::string& input);
|
||||
StringStream& operator<<(const char* input);
|
||||
StringStream& operator<<(char input);
|
||||
StringStream& operator<<(int input); // Handles int insertion with hex support
|
||||
StringStream& operator<<(long long input); // for long long
|
||||
|
||||
// Conversion to bool for checking stream state (success/failure)
|
||||
explicit operator bool() const {
|
||||
return validState;
|
||||
}
|
||||
|
||||
std::string str() const;
|
||||
void clear() { data.clear(); position = 0; } // Add clear function
|
||||
|
||||
private:
|
||||
std::string data;
|
||||
size_t position;
|
||||
bool hexMode;
|
||||
bool validState; // Track if the stream is in a valid state
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Trims leading and trailing whitespaces from a string.
|
||||
*
|
||||
* This function removes leading and trailing whitespaces, tabs, newlines, carriage returns, form feeds,
|
||||
* and vertical tabs from the input string.
|
||||
*
|
||||
* @param str The input string to trim.
|
||||
* @return The trimmed string.
|
||||
*/
|
||||
void trim(std::string& str);
|
||||
|
||||
|
||||
|
||||
// Function to trim newline characters from the end of a string
|
||||
void trimNewline(std::string& str);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes all white spaces from a string.
|
||||
*
|
||||
* This function removes all white spaces, including spaces, tabs, newlines, carriage returns, form feeds,
|
||||
* and vertical tabs from the input string.
|
||||
*
|
||||
* @param str The input string to remove white spaces from.
|
||||
* @return The string with white spaces removed.
|
||||
*/
|
||||
std::string removeWhiteSpaces(const std::string& str);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes quotes from a string.
|
||||
*
|
||||
* This function removes single and double quotes from the beginning and end of the input string.
|
||||
*
|
||||
* @param str The input string to remove quotes from.
|
||||
* @return The string with quotes removed.
|
||||
*/
|
||||
void removeQuotes(std::string& str);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Replaces multiple consecutive slashes with a single slash in a string.
|
||||
*
|
||||
* This function replaces sequences of two or more consecutive slashes with a single slash in the input string.
|
||||
*
|
||||
* @param input The input string to process.
|
||||
* @return The string with multiple slashes replaced.
|
||||
*/
|
||||
std::string replaceMultipleSlashes(const std::string& input);
|
||||
|
||||
|
||||
void resolveDirectoryTraversal(std::string& path);
|
||||
|
||||
/**
|
||||
* @brief Preprocesses a path string by replacing multiple slashes and adding "sdmc:" prefix.
|
||||
*
|
||||
* This function preprocesses a path string by removing multiple consecutive slashes,
|
||||
* adding the "sdmc:" prefix if not present, and modifying the input string in place.
|
||||
*
|
||||
* @param path The input path string to preprocess, passed by reference.
|
||||
*/
|
||||
void preprocessPath(std::string& path, const std::string& packagePath = "");
|
||||
|
||||
/**
|
||||
* @brief Preprocesses a URL string by adding "https://" prefix.
|
||||
*
|
||||
* This function preprocesses a URL string by adding the "https://" prefix if not already present.
|
||||
*
|
||||
* @param path The input URL string to preprocess, passed by reference and modified in-place.
|
||||
*/
|
||||
void preprocessUrl(std::string& path);
|
||||
|
||||
/**
|
||||
* @brief Drops the file extension from a filename.
|
||||
*
|
||||
* This function removes the file extension (characters after the last dot) from the input filename string.
|
||||
*
|
||||
* @param filename The input filename from which to drop the extension, passed by reference and modified in-place.
|
||||
*/
|
||||
void dropExtension(std::string& filename);
|
||||
|
||||
/**
|
||||
* @brief Checks if a string starts with a given prefix.
|
||||
*
|
||||
* This function checks if the input string starts with the specified prefix.
|
||||
*
|
||||
* @param str The input string to check.
|
||||
* @param prefix The prefix to check for.
|
||||
* @return True if the string starts with the prefix, false otherwise.
|
||||
*/
|
||||
bool startsWith(const std::string& str, const std::string& prefix);
|
||||
|
||||
|
||||
// Helper function to check if a string is a valid integer
|
||||
bool isValidNumber(const std::string& str);
|
||||
|
||||
// For properly handling placeholder replacements
|
||||
std::string returnOrNull(const std::string& value);
|
||||
|
||||
|
||||
// Function to slice a string from start to end index
|
||||
std::string sliceString(const std::string& str, size_t start, size_t end);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a string to lowercase.
|
||||
*
|
||||
* This function takes a string as input and returns a lowercase version of that string.
|
||||
*
|
||||
* @param str The input string to convert to lowercase.
|
||||
* @return The lowercase version of the input string.
|
||||
*/
|
||||
|
||||
std::string stringToLowercase(const std::string& str);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a string to uppercase.
|
||||
*
|
||||
* This function takes a string as input and returns an uppercase version of that string.
|
||||
*
|
||||
* @param str The input string to convert to uppercase.
|
||||
* @return The uppercase version of the input string.
|
||||
*/
|
||||
|
||||
std::string stringToUppercase(const std::string& str);
|
||||
|
||||
/**
|
||||
* @brief Formats a priority string to a desired width.
|
||||
*
|
||||
* This function takes a priority string and formats it to a specified desired width by padding with '0's if it's shorter
|
||||
* or truncating with '9's if it's longer.
|
||||
*
|
||||
* @param priority The input priority string to format.
|
||||
* @param desiredWidth The desired width of the formatted string (default is 4).
|
||||
* @return A formatted priority string.
|
||||
*/
|
||||
std::string formatPriorityString(const std::string& priority, int desiredWidth = 4);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes the part of the string after the first occurrence of '?' character.
|
||||
*
|
||||
* This function takes a string and removes the portion of the string that appears after
|
||||
* the first '?' character, if found. If no '?' character is present, the string remains unchanged.
|
||||
*
|
||||
* @param input The input string from which to remove the tag, passed by reference and modified in-place.
|
||||
*/
|
||||
void removeTag(std::string &input);
|
||||
|
||||
std::string getFirstLongEntry(const std::string& input, size_t minLength = 8);
|
||||
|
||||
|
||||
// This will take a string like "v1.3.5-abasdfasdfa" and output "1.3.5". string could also look like "test-1.3.5-1" or "v1.3.5" and we will only want "1.3.5"
|
||||
std::string cleanVersionLabel(const std::string& input);
|
||||
|
||||
|
||||
std::string extractTitle(const std::string& input);
|
||||
|
||||
|
||||
std::vector<std::string> splitString(const std::string& str, const std::string& delimiter);
|
||||
|
||||
|
||||
// Function to split a string by a delimiter and return a specific index
|
||||
std::string splitStringAtIndex(const std::string& str, const std::string& delimiter, size_t index);
|
||||
|
||||
|
||||
std::string customAlign(int number);
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
std::string inputExists(const std::string& input);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,719 @@
|
||||
/********************************************************************************
|
||||
* File: tsl_utils.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* 'tsl_utils.hpp' is a central utility header for the Ultrahand Overlay project,
|
||||
* containing a variety of functions and definitions related to system status,
|
||||
* input handling, and application-specific behavior on the Nintendo Switch.
|
||||
* This header provides essential utilities for interacting with the system,
|
||||
* managing key input, and enhancing overlay functionality.
|
||||
*
|
||||
* The utilities defined here are designed to operate independently, facilitating
|
||||
* robust system interaction capabilities required for custom overlays.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository:
|
||||
* GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay
|
||||
*
|
||||
* Note: This notice is integral to the project's documentation and must not be
|
||||
* altered or removed.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef TSL_UTILS_HPP
|
||||
#define TSL_UTILS_HPP
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE // For not using fstream (needs implementing)
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
#include <ultra.hpp>
|
||||
#include <switch.h>
|
||||
#include <arm_neon.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
//#include <math.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cwctype>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
//#include <chrono>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <barrier>
|
||||
|
||||
#ifndef APPROXIMATE_cos
|
||||
// Approximation for cos(x) using Taylor series around 0
|
||||
#define APPROXIMATE_cos(x) (1 - (x) * (x) / 2 + (x) * (x) * (x) * (x) / 24) // valid for small x
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef APPROXIMATE_ifloor
|
||||
#define APPROXIMATE_ifloor(x) ((int)((x) >= 0 ? (x) : (x) - 1)) // truncate toward negative infinity
|
||||
#define APPROXIMATE_iceil(x) ((int)((x) == (int)(x) ? (x) : ((x) > 0 ? (int)(x) + 1 : (int)(x)))) // truncate toward positive infinity
|
||||
#endif
|
||||
|
||||
#ifndef APPROXIMATE_sqrt
|
||||
// Fast approximation for sqrt using Newton's method
|
||||
#define APPROXIMATE_sqrt(x) ((x) <= 0 ? 0 : (x) / 2.0 * (3.0 - ((x) * (x) * 0.5))) // Approximation for x close to 1
|
||||
#define APPROXIMATE_pow(x, y) ((y) == 0 ? 1 : ((y) == 1 ? (x) : APPROXIMATE_sqrt(x))) // limited to approximate sqrt if y=0.5
|
||||
#endif
|
||||
|
||||
#ifndef APPROXIMATE_fmod
|
||||
#define APPROXIMATE_fmod(x, y) ((x) - ((int)((x) / (y)) * (y))) // equivalent to x - floor(x/y) * y
|
||||
#endif
|
||||
|
||||
#ifndef APPROXIMATE_cos
|
||||
// Approximation for cos(x) using Taylor series around 0
|
||||
#define APPROXIMATE_cos(x) (1 - (x) * (x) / 2 + (x) * (x) * (x) * (x) / 24) // valid for small x
|
||||
#endif
|
||||
|
||||
#ifndef APPROXIMATE_acos
|
||||
#define APPROXIMATE_acos(x) (1.5708 - (x) - (x)*(x)*(x) / 6) // limited approximation for acos in range [-1, 1]
|
||||
#endif
|
||||
|
||||
#ifndef APPROXIMATE_fabs
|
||||
#define APPROXIMATE_fabs(x) ((x) < 0 ? -(x) : (x))
|
||||
#endif
|
||||
|
||||
struct OverlayCombo {
|
||||
std::string path; // full overlay path
|
||||
std::string launchArg; // empty = use per-overlay launch_args key, otherwise a “mode” arg
|
||||
};
|
||||
|
||||
struct SwapDepth {
|
||||
u32 value;
|
||||
explicit SwapDepth(u32 v) : value(v) {}
|
||||
};
|
||||
|
||||
namespace ult {
|
||||
extern bool correctFrameSize; // for detecting the correct Overlay display size
|
||||
|
||||
extern u16 DefaultFramebufferWidth; ///< Width of the framebuffer
|
||||
extern u16 DefaultFramebufferHeight; ///< Height of the framebuffer
|
||||
|
||||
extern std::unordered_map<std::string, std::string> translationCache;
|
||||
|
||||
extern std::unordered_map<u64, OverlayCombo> g_entryCombos;
|
||||
extern std::atomic<bool> launchingOverlay;
|
||||
extern std::atomic<bool> settingsInitialized;
|
||||
extern std::atomic<bool> currentForeground;
|
||||
//extern std::mutex simulatedNextPageMutex;
|
||||
|
||||
//void loadOverlayKeyCombos();
|
||||
//std::string getOverlayForKeyCombo(u64 keys);
|
||||
|
||||
bool readFileContent(const std::string& filePath, std::string& content);
|
||||
void parseJsonContent(const std::string& content, std::unordered_map<std::string, std::string>& result);
|
||||
bool parseJsonToMap(const std::string& filePath, std::unordered_map<std::string, std::string>& result);
|
||||
|
||||
bool loadTranslationsFromJSON(const std::string& filePath);
|
||||
|
||||
extern u16 activeHeaderHeight;
|
||||
|
||||
bool consoleIsDocked();
|
||||
|
||||
std::string getTitleIdAsString();
|
||||
|
||||
extern std::string lastTitleID;
|
||||
extern std::atomic<bool> resetForegroundCheck;
|
||||
|
||||
|
||||
//extern bool isLauncher;
|
||||
extern std::atomic<bool> internalTouchReleased;
|
||||
extern u32 layerEdge;
|
||||
extern bool useRightAlignment;
|
||||
extern bool useSwipeToOpen;
|
||||
extern bool useLaunchCombos;
|
||||
extern bool useNotifications;
|
||||
extern bool usePageSwap;
|
||||
extern std::atomic<bool> noClickableItems;
|
||||
|
||||
extern bool useDynamicLogo;
|
||||
extern bool useSelectionBG;
|
||||
extern bool useSelectionText;
|
||||
extern bool useSelectionValue;
|
||||
|
||||
|
||||
|
||||
// Define the duration boundaries (for smooth scrolling)
|
||||
//extern const std::chrono::milliseconds initialInterval; // Example initial interval
|
||||
//extern const std::chrono::milliseconds shortInterval; // Short interval after long hold
|
||||
//extern const std::chrono::milliseconds transitionPoint; // Point at which the shortest interval is reached
|
||||
|
||||
|
||||
|
||||
//constexpr std::chrono::milliseconds initialInterval = std::chrono::milliseconds(67); // Example initial interval
|
||||
//constexpr std::chrono::milliseconds shortInterval = std::chrono::milliseconds(10); // Short interval after long hold
|
||||
//constexpr std::chrono::milliseconds transitionPoint = std::chrono::milliseconds(2000); // Point at which the shortest interval is reached
|
||||
|
||||
// Function to interpolate between two durations
|
||||
//std::chrono::milliseconds interpolateDuration(std::chrono::milliseconds start, std::chrono::milliseconds end, float t);
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
extern std::atomic<bool> overlayLaunchRequested;
|
||||
extern std::string requestedOverlayPath;
|
||||
extern std::string requestedOverlayArgs;
|
||||
extern std::mutex overlayLaunchMutex;
|
||||
#endif
|
||||
|
||||
|
||||
//#include <filesystem> // Comment out filesystem
|
||||
|
||||
// CUSTOM SECTION START
|
||||
//extern float backWidth, selectWidth, nextPageWidth;
|
||||
extern std::atomic<float> backWidth;
|
||||
extern std::atomic<float> selectWidth;
|
||||
extern std::atomic<float> nextPageWidth;
|
||||
extern std::atomic<bool> inMainMenu;
|
||||
extern std::atomic<bool> inOverlaysPage;
|
||||
extern std::atomic<bool> inPackagesPage;
|
||||
|
||||
extern bool firstBoot; // for detecting first boot
|
||||
|
||||
//static std::unordered_map<std::string, std::string> hexSumCache;
|
||||
|
||||
// Define an atomic bool for interpreter completion
|
||||
extern std::atomic<bool> threadFailure;
|
||||
extern std::atomic<bool> runningInterpreter;
|
||||
extern std::atomic<bool> shakingProgress;
|
||||
|
||||
extern std::atomic<bool> isHidden;
|
||||
extern std::atomic<bool> externalAbortCommands;
|
||||
|
||||
//bool progressAnimation = false;
|
||||
extern bool disableTransparency;
|
||||
//bool useCustomWallpaper = false;
|
||||
extern bool useMemoryExpansion;
|
||||
extern bool useOpaqueScreenshots;
|
||||
|
||||
extern std::atomic<bool> onTrackBar;
|
||||
extern std::atomic<bool> allowSlide;
|
||||
extern std::atomic<bool> unlockedSlide;
|
||||
|
||||
void atomicToggle(std::atomic<bool>& b);
|
||||
|
||||
/**
|
||||
* @brief Shutdown modes for the Ultrahand-Overlay project.
|
||||
*
|
||||
* These macros define the shutdown modes used in the Ultrahand-Overlay project:
|
||||
* - `SpsmShutdownMode_Normal`: Normal shutdown mode.
|
||||
* - `SpsmShutdownMode_Reboot`: Reboot mode.
|
||||
*/
|
||||
#define SpsmShutdownMode_Normal 0
|
||||
#define SpsmShutdownMode_Reboot 1
|
||||
|
||||
/**
|
||||
* @brief Key mapping macros for button keys.
|
||||
*
|
||||
* These macros define button keys for the Ultrahand-Overlay project to simplify key mappings.
|
||||
* For example, `KEY_A` represents the `HidNpadButton_A` key.
|
||||
*/
|
||||
#define KEY_A HidNpadButton_A
|
||||
#define KEY_B HidNpadButton_B
|
||||
#define KEY_X HidNpadButton_X
|
||||
#define KEY_Y HidNpadButton_Y
|
||||
#define KEY_L HidNpadButton_L
|
||||
#define KEY_R HidNpadButton_R
|
||||
#define KEY_ZL HidNpadButton_ZL
|
||||
#define KEY_ZR HidNpadButton_ZR
|
||||
#define KEY_PLUS HidNpadButton_Plus
|
||||
#define KEY_MINUS HidNpadButton_Minus
|
||||
#define KEY_DUP HidNpadButton_Up
|
||||
#define KEY_DDOWN HidNpadButton_Down
|
||||
#define KEY_DLEFT HidNpadButton_Left
|
||||
#define KEY_DRIGHT HidNpadButton_Right
|
||||
#define KEY_SL HidNpadButton_AnySL
|
||||
#define KEY_SR HidNpadButton_AnySR
|
||||
#define KEY_LSTICK HidNpadButton_StickL
|
||||
#define KEY_RSTICK HidNpadButton_StickR
|
||||
#define KEY_UP HidNpadButton_AnyUp
|
||||
#define KEY_DOWN HidNpadButton_AnyDown
|
||||
#define KEY_LEFT HidNpadButton_AnyLeft
|
||||
#define KEY_RIGHT HidNpadButton_AnyRight
|
||||
|
||||
#define SCRIPT_KEY HidNpadButton_Minus
|
||||
#define SYSTEM_SETTINGS_KEY HidNpadButton_Plus
|
||||
#define SETTINGS_KEY HidNpadButton_Y
|
||||
#define STAR_KEY HidNpadButton_X
|
||||
|
||||
|
||||
// Define a mask with all possible key flags
|
||||
#define ALL_KEYS_MASK (KEY_A | KEY_B | KEY_X | KEY_Y | KEY_DUP | KEY_DDOWN | KEY_DLEFT | KEY_DRIGHT | KEY_L | KEY_R | KEY_ZL | KEY_ZR | KEY_SL | KEY_SR | KEY_LSTICK | KEY_RSTICK | KEY_PLUS | KEY_MINUS)
|
||||
|
||||
|
||||
extern bool updateMenuCombos;
|
||||
|
||||
/**
|
||||
* @brief Ultrahand-Overlay Input Macros
|
||||
*
|
||||
* This block of code defines macros for handling input in the Ultrahand-Overlay project.
|
||||
* These macros simplify the mapping of input events to corresponding button keys and
|
||||
* provide aliases for touch and joystick positions.
|
||||
*
|
||||
* The macros included in this block are:
|
||||
*
|
||||
* - `touchPosition`: An alias for a constant `HidTouchState` pointer.
|
||||
* - `touchInput`: An alias for `&touchPos`, representing touch input.
|
||||
* - `JoystickPosition`: An alias for `HidAnalogStickState`, representing joystick input.
|
||||
*
|
||||
* These macros are utilized within the Ultrahand-Overlay project to manage and interpret
|
||||
* user input, including touch and joystick events.
|
||||
*/
|
||||
#define touchPosition const HidTouchState
|
||||
#define touchInput &touchPos
|
||||
#define JoystickPosition HidAnalogStickState
|
||||
|
||||
//void convertComboToUnicode(std::string& combo);
|
||||
|
||||
/**
|
||||
* @brief Combo key mapping
|
||||
*/
|
||||
struct KeyInfo {
|
||||
u64 key;
|
||||
const char* name;
|
||||
const char* glyph;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Combo key mappings
|
||||
*
|
||||
* Ordered as they should be displayed
|
||||
*/
|
||||
extern std::array<KeyInfo, 18> KEYS_INFO;
|
||||
|
||||
std::unordered_map<std::string, std::string> createButtonCharMap();
|
||||
|
||||
extern std::unordered_map<std::string, std::string> buttonCharMap;
|
||||
|
||||
|
||||
void convertComboToUnicode(std::string& combo);
|
||||
|
||||
|
||||
// English string definitions
|
||||
|
||||
extern const std::string whiteColor;
|
||||
extern const std::string blackColor;
|
||||
|
||||
extern std::atomic<bool> languageWasChanged;
|
||||
|
||||
inline constexpr double _M_PI = 3.14159265358979323846; // For double precision
|
||||
inline constexpr double RAD_TO_DEG = 180.0f / _M_PI;
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
extern std::string ENGLISH;
|
||||
extern std::string SPANISH;
|
||||
extern std::string FRENCH;
|
||||
extern std::string GERMAN;
|
||||
extern std::string JAPANESE;
|
||||
extern std::string KOREAN;
|
||||
extern std::string ITALIAN;
|
||||
extern std::string DUTCH;
|
||||
extern std::string PORTUGUESE;
|
||||
extern std::string RUSSIAN;
|
||||
extern std::string UKRAINIAN;
|
||||
extern std::string POLISH;
|
||||
extern std::string SIMPLIFIED_CHINESE;
|
||||
extern std::string TRADITIONAL_CHINESE;
|
||||
|
||||
extern std::string OVERLAYS; //defined in libTesla now
|
||||
extern std::string OVERLAYS_ABBR;
|
||||
extern std::string OVERLAY;
|
||||
extern std::string HIDDEN_OVERLAYS;
|
||||
extern std::string PACKAGES; //defined in libTesla now
|
||||
extern std::string PACKAGE;
|
||||
extern std::string HIDDEN_PACKAGES;
|
||||
extern std::string HIDDEN;
|
||||
extern std::string HIDE_OVERLAY;
|
||||
extern std::string HIDE_PACKAGE;
|
||||
extern std::string LAUNCH_ARGUMENTS;
|
||||
extern std::string QUICK_LAUNCH;
|
||||
extern std::string BOOT_COMMANDS;
|
||||
extern std::string EXIT_COMMANDS;
|
||||
extern std::string ERROR_LOGGING;
|
||||
extern std::string COMMANDS;
|
||||
extern std::string SETTINGS;
|
||||
extern std::string FAVORITE;
|
||||
extern std::string MAIN_SETTINGS;
|
||||
extern std::string UI_SETTINGS;
|
||||
|
||||
extern std::string WIDGET;
|
||||
extern std::string WIDGET_ITEMS;
|
||||
extern std::string WIDGET_SETTINGS;
|
||||
extern std::string CLOCK;
|
||||
extern std::string BATTERY;
|
||||
extern std::string SOC_TEMPERATURE;
|
||||
extern std::string PCB_TEMPERATURE;
|
||||
extern std::string BACKDROP;
|
||||
extern std::string DYNAMIC_COLORS;
|
||||
extern std::string CENTER_ALIGNMENT;
|
||||
extern std::string EXTENDED_BACKDROP;
|
||||
extern std::string MISCELLANEOUS;
|
||||
//extern std::string MENU_ITEMS;
|
||||
extern std::string MENU_SETTINGS;
|
||||
extern std::string USER_GUIDE;
|
||||
extern std::string SHOW_HIDDEN;
|
||||
extern std::string SHOW_DELETE;
|
||||
extern std::string PAGE_SWAP;
|
||||
extern std::string RIGHT_SIDE_MODE;
|
||||
extern std::string OVERLAY_VERSIONS;
|
||||
extern std::string PACKAGE_VERSIONS;
|
||||
extern std::string CLEAN_VERSIONS;
|
||||
//extern std::string VERSION_LABELS;
|
||||
extern std::string KEY_COMBO;
|
||||
extern std::string MODE;
|
||||
extern std::string MODES;
|
||||
extern std::string LANGUAGE;
|
||||
extern std::string OVERLAY_INFO;
|
||||
extern std::string SOFTWARE_UPDATE;
|
||||
extern std::string UPDATE_ULTRAHAND;
|
||||
extern std::string UPDATE_LANGUAGES;
|
||||
extern std::string SYSTEM;
|
||||
extern std::string DEVICE_INFO;
|
||||
extern std::string FIRMWARE;
|
||||
extern std::string BOOTLOADER;
|
||||
extern std::string HARDWARE;
|
||||
extern std::string MEMORY;
|
||||
extern std::string VENDOR;
|
||||
extern std::string MODEL;
|
||||
extern std::string STORAGE;
|
||||
extern std::string NOTICE;
|
||||
extern std::string UTILIZES;
|
||||
|
||||
extern std::string MEMORY_EXPANSION;
|
||||
extern std::string REBOOT_REQUIRED;
|
||||
extern std::string LOCAL_IP;
|
||||
extern std::string WALLPAPER;
|
||||
extern std::string THEME;
|
||||
extern std::string DEFAULT;
|
||||
extern std::string ROOT_PACKAGE;
|
||||
extern std::string SORT_PRIORITY;
|
||||
extern std::string OPTIONS;
|
||||
extern std::string FAILED_TO_OPEN;
|
||||
|
||||
extern std::string LAUNCH_COMBOS;
|
||||
extern std::string NOTIFICATIONS;
|
||||
extern std::string OPAQUE_SCREENSHOTS;
|
||||
|
||||
extern std::string PACKAGE_INFO;
|
||||
extern std::string _TITLE;
|
||||
extern std::string _VERSION;
|
||||
extern std::string _CREATOR;
|
||||
extern std::string _ABOUT;
|
||||
extern std::string _CREDITS;
|
||||
|
||||
extern std::string USERGUIDE_OFFSET;
|
||||
extern std::string SETTINGS_MENU;
|
||||
extern std::string SCRIPT_OVERLAY;
|
||||
extern std::string STAR_FAVORITE;
|
||||
extern std::string APP_SETTINGS;
|
||||
extern std::string ON_MAIN_MENU;
|
||||
extern std::string ON_A_COMMAND;
|
||||
extern std::string ON_OVERLAY_PACKAGE;
|
||||
extern std::string FEATURES;
|
||||
extern std::string SWIPE_TO_OPEN;
|
||||
|
||||
extern std::string THEME_SETTINGS;
|
||||
extern std::string DYNAMIC_LOGO;
|
||||
extern std::string SELECTION_BACKGROUND;
|
||||
extern std::string SELECTION_TEXT;
|
||||
extern std::string SELECTION_VALUE;
|
||||
extern std::string LIBULTRAHAND_TITLES;
|
||||
extern std::string LIBULTRAHAND_VERSIONS;
|
||||
extern std::string PACKAGE_TITLES;
|
||||
|
||||
extern std::string ULTRAHAND_HAS_STARTED;
|
||||
extern std::string NEW_UPDATE_IS_AVAILABLE;
|
||||
extern std::string REBOOT_IS_REQUIRED;
|
||||
extern std::string HOLD_A_TO_DELETE;
|
||||
extern std::string SELECTION_IS_EMPTY;
|
||||
|
||||
//extern std::string PACKAGE_VERSIONS;
|
||||
//extern std::string PROGRESS_ANIMATION;
|
||||
|
||||
extern std::string REBOOT_TO;
|
||||
extern std::string REBOOT;
|
||||
extern std::string SHUTDOWN;
|
||||
extern std::string BOOT_ENTRY;
|
||||
#endif
|
||||
|
||||
extern std::string FREE;
|
||||
|
||||
extern std::string DEFAULT_CHAR_WIDTH;
|
||||
extern std::string UNAVAILABLE_SELECTION;
|
||||
|
||||
extern std::string ON;
|
||||
extern std::string OFF;
|
||||
|
||||
extern std::string OK;
|
||||
extern std::string BACK;
|
||||
extern std::string HIDE;
|
||||
extern std::string CANCEL;
|
||||
|
||||
extern std::string GAP_1;
|
||||
extern std::string GAP_2;
|
||||
extern std::atomic<float> halfGap;
|
||||
|
||||
//extern std::string EMPTY;
|
||||
|
||||
#if USING_WIDGET_DIRECTIVE
|
||||
extern std::string SUNDAY;
|
||||
extern std::string MONDAY;
|
||||
extern std::string TUESDAY;
|
||||
extern std::string WEDNESDAY;
|
||||
extern std::string THURSDAY;
|
||||
extern std::string FRIDAY;
|
||||
extern std::string SATURDAY;
|
||||
|
||||
extern std::string JANUARY;
|
||||
extern std::string FEBRUARY;
|
||||
extern std::string MARCH;
|
||||
extern std::string APRIL;
|
||||
extern std::string MAY;
|
||||
extern std::string JUNE;
|
||||
extern std::string JULY;
|
||||
extern std::string AUGUST;
|
||||
extern std::string SEPTEMBER;
|
||||
extern std::string OCTOBER;
|
||||
extern std::string NOVEMBER;
|
||||
extern std::string DECEMBER;
|
||||
|
||||
extern std::string SUN;
|
||||
extern std::string MON;
|
||||
extern std::string TUE;
|
||||
extern std::string WED;
|
||||
extern std::string THU;
|
||||
extern std::string FRI;
|
||||
extern std::string SAT;
|
||||
|
||||
extern std::string JAN;
|
||||
extern std::string FEB;
|
||||
extern std::string MAR;
|
||||
extern std::string APR;
|
||||
extern std::string MAY_ABBR;
|
||||
extern std::string JUN;
|
||||
extern std::string JUL;
|
||||
extern std::string AUG;
|
||||
extern std::string SEP;
|
||||
extern std::string OCT;
|
||||
extern std::string NOV;
|
||||
extern std::string DEC;
|
||||
#endif
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
// Constant string definitions (English)
|
||||
void reinitializeLangVars();
|
||||
#endif
|
||||
|
||||
|
||||
// Define the updateIfNotEmpty function
|
||||
void updateIfNotEmpty(std::string& constant, const char* jsonKey, const json_t* jsonData);
|
||||
|
||||
void parseLanguage(const std::string& langFile);
|
||||
|
||||
#if USING_WIDGET_DIRECTIVE
|
||||
void localizeTimeStr(char* timeStr);
|
||||
#endif
|
||||
|
||||
// Unified function to apply replacements
|
||||
void applyLangReplacements(std::string& text, bool isValue = false);
|
||||
|
||||
|
||||
|
||||
//// Map of character widths (pre-calibrated)
|
||||
//extern std::unordered_map<wchar_t, float> characterWidths;
|
||||
|
||||
//extern float defaultNumericCharWidth;
|
||||
|
||||
|
||||
|
||||
// Predefined hexMap
|
||||
//extern const std::array<int, 256> hexMap;
|
||||
inline constexpr std::array<int, 256> hexMap = [] {
|
||||
std::array<int, 256> map = {0};
|
||||
map['0'] = 0; map['1'] = 1; map['2'] = 2; map['3'] = 3; map['4'] = 4;
|
||||
map['5'] = 5; map['6'] = 6; map['7'] = 7; map['8'] = 8; map['9'] = 9;
|
||||
map['A'] = 10; map['B'] = 11; map['C'] = 12; map['D'] = 13; map['E'] = 14; map['F'] = 15;
|
||||
map['a'] = 10; map['b'] = 11; map['c'] = 12; map['d'] = 13; map['e'] = 14; map['f'] = 15;
|
||||
return map;
|
||||
}();
|
||||
|
||||
|
||||
// Prepare a map of default settings
|
||||
extern std::map<const std::string, std::string> defaultThemeSettingsMap;
|
||||
|
||||
bool isNumericCharacter(char c);
|
||||
|
||||
bool isValidHexColor(const std::string& hexColor);
|
||||
|
||||
|
||||
|
||||
float calculateAmplitude(float x, float peakDurationFactor = 0.25f);
|
||||
|
||||
|
||||
|
||||
extern std::atomic<bool> refreshWallpaper;
|
||||
extern std::vector<u8> wallpaperData;
|
||||
extern std::atomic<bool> inPlot;
|
||||
|
||||
extern std::mutex wallpaperMutex;
|
||||
extern std::condition_variable cv;
|
||||
|
||||
|
||||
|
||||
// Function to load the RGBA file into memory and modify wallpaperData directly
|
||||
void loadWallpaperFile(const std::string& filePath, s32 width = 448, s32 height = 720);
|
||||
void loadWallpaperFileWhenSafe();
|
||||
|
||||
void reloadWallpaper();
|
||||
|
||||
// Global variables for FPS calculation
|
||||
//extern double lastTimeCount;
|
||||
//extern int frameCount;
|
||||
//extern float fps;
|
||||
//extern double elapsedTime;
|
||||
|
||||
|
||||
extern std::atomic<bool> themeIsInitialized;
|
||||
|
||||
// Variables for touch commands
|
||||
extern std::atomic<bool> touchingBack;
|
||||
extern std::atomic<bool> touchingSelect;
|
||||
extern std::atomic<bool> touchingNextPage;
|
||||
extern std::atomic<bool> touchingMenu;
|
||||
extern std::atomic<bool> shortTouchAndRelease;
|
||||
extern std::atomic<bool> longTouchAndRelease;
|
||||
extern std::atomic<bool> simulatedBack;
|
||||
//extern bool simulatedBackComplete;
|
||||
extern std::atomic<bool> simulatedSelect;
|
||||
//extern bool simulatedSelectComplete;
|
||||
extern std::atomic<bool> simulatedNextPage;
|
||||
//extern std::atomic<bool> simulatedNextPageComplete;
|
||||
extern std::atomic<bool> simulatedMenu;
|
||||
//extern bool simulatedMenuComplete;
|
||||
extern std::atomic<bool> stillTouching;
|
||||
extern std::atomic<bool> interruptedTouch;
|
||||
extern std::atomic<bool> touchInBounds;
|
||||
|
||||
|
||||
#if USING_WIDGET_DIRECTIVE
|
||||
// Battery implementation
|
||||
extern bool powerInitialized;
|
||||
extern bool powerCacheInitialized;
|
||||
extern uint32_t powerCacheCharge;
|
||||
extern bool powerCacheIsCharging;
|
||||
extern PsmSession powerSession;
|
||||
|
||||
// Define variables to store previous battery charge and time
|
||||
//extern uint32_t prevBatteryCharge;
|
||||
//extern s64 timeOut;
|
||||
|
||||
|
||||
extern std::atomic<uint32_t> batteryCharge;
|
||||
extern std::atomic<bool> isCharging;
|
||||
|
||||
//constexpr std::chrono::seconds min_delay = std::chrono::seconds(3); // Minimum delay between checks
|
||||
|
||||
bool powerGetDetails(uint32_t *_batteryCharge, bool *_isCharging);
|
||||
|
||||
void powerInit(void);
|
||||
|
||||
void powerExit(void);
|
||||
#endif
|
||||
|
||||
// Temperature Implementation
|
||||
extern std::atomic<float> PCB_temperature;
|
||||
extern std::atomic<float> SOC_temperature;
|
||||
|
||||
/*
|
||||
I2cReadRegHandler was taken from Switch-OC-Suite source code made by KazushiMe
|
||||
Original repository link (Deleted, last checked 15.04.2023): https://github.com/KazushiMe/Switch-OC-Suite
|
||||
*/
|
||||
|
||||
Result I2cReadRegHandler(u8 reg, I2cDevice dev, u16 *out);
|
||||
|
||||
|
||||
#define TMP451_SOC_TEMP_REG 0x01 // Register for SOC temperature integer part
|
||||
#define TMP451_SOC_TMP_DEC_REG 0x10 // Register for SOC temperature decimal part
|
||||
#define TMP451_PCB_TEMP_REG 0x00 // Register for PCB temperature integer part
|
||||
#define TMP451_PCB_TMP_DEC_REG 0x15 // Register for PCB temperature decimal part
|
||||
|
||||
// Common helper function to read temperature (integer and fractional parts)
|
||||
Result ReadTemperature(float *temperature, u8 integerReg, u8 fractionalReg, bool integerOnly);
|
||||
|
||||
// Function to get the SOC temperature
|
||||
Result ReadSocTemperature(float *temperature, bool integerOnly = true);
|
||||
|
||||
// Function to get the PCB temperature
|
||||
Result ReadPcbTemperature(float *temperature, bool integerOnly = true);
|
||||
|
||||
|
||||
|
||||
// Time implementation
|
||||
|
||||
extern const std::string DEFAULT_DT_FORMAT;
|
||||
extern std::string datetimeFormat;
|
||||
|
||||
|
||||
// Widget settings
|
||||
//static std::string hideClock, hideBattery, hidePCBTemp, hideSOCTemp;
|
||||
extern bool hideClock, hideBattery, hidePCBTemp, hideSOCTemp, dynamicWidgetColors;
|
||||
extern bool hideWidgetBackdrop, centerWidgetAlignment, extendedWidgetBackdrop;
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
void reinitializeWidgetVars();
|
||||
#endif
|
||||
|
||||
extern bool cleanVersionLabels, hideOverlayVersions, hidePackageVersions, useLibultrahandTitles, useLibultrahandVersions, usePackageTitles, usePackageVersions;
|
||||
|
||||
extern const std::string loaderInfo;
|
||||
extern const std::string loaderTitle;
|
||||
extern const bool expandedMemory;
|
||||
|
||||
extern std::string versionLabel;
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
void reinitializeVersionLabels();
|
||||
#endif
|
||||
|
||||
|
||||
// Number of renderer threads to use
|
||||
extern const unsigned numThreads;
|
||||
extern std::vector<std::thread> renderThreads;
|
||||
extern const s32 bmpChunkSize;
|
||||
extern std::atomic<s32> currentRow;
|
||||
|
||||
|
||||
|
||||
static std::barrier inPlotBarrier(numThreads, [](){
|
||||
inPlot.store(false, std::memory_order_release);
|
||||
});
|
||||
|
||||
|
||||
//extern std::atomic<unsigned int> barrierCounter;
|
||||
//extern std::mutex barrierMutex;
|
||||
//extern std::condition_variable barrierCV;
|
||||
//
|
||||
//extern void barrierWait();
|
||||
|
||||
|
||||
void initializeThemeVars();
|
||||
|
||||
void initializeUltrahandSettings();
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/********************************************************************************
|
||||
* File: ultra.hpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* 'ultra.hpp' serves as a central include header for the Ultrahand Overlay project,
|
||||
* bringing together a comprehensive suite of utility functions essential for the
|
||||
* development and operation of custom overlays on the Nintendo Switch. This header
|
||||
* provides consolidated access to functions facilitating debugging, string processing,
|
||||
* file management, JSON manipulation, and more, enhancing the modularity and
|
||||
* reusability of code within the project.
|
||||
*
|
||||
* These utilities are designed to operate independently, providing robust tools to
|
||||
* support complex overlay functionalities and interactions.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository:
|
||||
* GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay
|
||||
*
|
||||
* Note: This notice is integral to the project's documentation and must not be
|
||||
* altered or removed.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
|
||||
#ifndef ULTRA_HPP
|
||||
#define ULTRA_HPP
|
||||
|
||||
// Include all functional headers used in the libUltra library
|
||||
|
||||
#include "global_vars.hpp"
|
||||
#include "debug_funcs.hpp"
|
||||
#include "string_funcs.hpp"
|
||||
#include "get_funcs.hpp"
|
||||
#include "path_funcs.hpp"
|
||||
#include "list_funcs.hpp"
|
||||
#include "json_funcs.hpp"
|
||||
#include "ini_funcs.hpp"
|
||||
#include "hex_funcs.hpp"
|
||||
#include "download_funcs.hpp"
|
||||
#include "mod_funcs.hpp"
|
||||
#include "tsl_utils.hpp"
|
||||
|
||||
#endif // ULTRA_HPP
|
||||
@@ -0,0 +1,60 @@
|
||||
/********************************************************************************
|
||||
* File: debug_funcs.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file contains the implementation of debugging functions for the
|
||||
* Ultrahand Overlay project.
|
||||
********************************************************************************/
|
||||
|
||||
#include "debug_funcs.hpp"
|
||||
|
||||
namespace ult {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
// Define static variables
|
||||
const std::string defaultLogFilePath = "sdmc:/switch/.packages/log.txt";
|
||||
|
||||
std::string logFilePath = defaultLogFilePath;
|
||||
bool disableLogging = true;
|
||||
std::mutex logMutex;
|
||||
|
||||
void logMessage(const std::string& message) {
|
||||
//if (disableLogging)
|
||||
// return;
|
||||
|
||||
std::time_t currentTime = std::time(nullptr);
|
||||
std::tm* timeInfo = std::localtime(¤tTime);
|
||||
char timestamp[30]; // Exact size for "[YYYY-MM-DD HH:MM:SS] " + null terminator
|
||||
strftime(timestamp, sizeof(timestamp), "[%Y-%m-%d %H:%M:%S] ", timeInfo);
|
||||
|
||||
// Depending on the directive, use either std::ofstream or stdio functions
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
// Use stdio functions to open, write, and close the file
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logMutex); // Locks the mutex for thread-safe access
|
||||
|
||||
FILE* file = fopen(logFilePath.c_str(), "a"); // Open the file in append mode
|
||||
if (file != nullptr) {
|
||||
fprintf(file, "%s%s\n", timestamp, message.c_str());
|
||||
fclose(file); // Close the file after writing
|
||||
} else {
|
||||
// Handle error when file opening fails (if necessary)
|
||||
// printf("Failed to open log file: %s\n", logFilePath.c_str());
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Use std::ofstream if !USING_FSTREAM_DIRECTIVE is not defined
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logMutex); // Locks the mutex for the duration of this block
|
||||
|
||||
std::ofstream file(logFilePath.c_str(), std::ios::app);
|
||||
if (file.is_open()) {
|
||||
file << timestamp << message << "\n";
|
||||
} else {
|
||||
// Handle error when file opening fails
|
||||
// std::cerr << "Failed to open log file: " << logFilePath << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,812 @@
|
||||
/********************************************************************************
|
||||
* File: download_funcs.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file provides implementations for the functions declared in
|
||||
* download_funcs.hpp. These functions utilize libcurl for downloading files
|
||||
* from the internet and minizip-ng for extracting ZIP archives with proper
|
||||
* 64-bit file support.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#include "download_funcs.hpp"
|
||||
|
||||
|
||||
namespace ult {
|
||||
|
||||
size_t DOWNLOAD_READ_BUFFER = 16*1024;//64 * 1024;//4096*10;
|
||||
size_t DOWNLOAD_WRITE_BUFFER = 16*1024;//64 * 1024;
|
||||
size_t UNZIP_READ_BUFFER = 32*1024;//131072*2;//4096*4;
|
||||
size_t UNZIP_WRITE_BUFFER = 16*1024;//131072*2;//4096*4;
|
||||
|
||||
|
||||
// Path to the CA certificate
|
||||
//const std::string cacertPath = "sdmc:/config/ultrahand/cacert.pem";
|
||||
//const std::string cacertURL = "https://curl.se/ca/cacert.pem";
|
||||
|
||||
// Shared atomic flag to indicate whether to abort the download operation
|
||||
std::atomic<bool> abortDownload(false);
|
||||
// Define an atomic bool for interpreter completion
|
||||
std::atomic<bool> abortUnzip(false);
|
||||
std::atomic<int> downloadPercentage(-1);
|
||||
std::atomic<int> unzipPercentage(-1);
|
||||
|
||||
// Thread-safe curl initialization
|
||||
static std::mutex curlInitMutex;
|
||||
static std::atomic<bool> curlInitialized(false);
|
||||
|
||||
|
||||
|
||||
struct FileDeleter {
|
||||
void operator()(FILE* f) const { if (f) fclose(f); }
|
||||
};
|
||||
|
||||
// Definition of CurlDeleter
|
||||
void CurlDeleter::operator()(CURL* curl) const {
|
||||
if (curl) {
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback function to write received data to a file.
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
// Using stdio.h functions (FILE*, fwrite)
|
||||
size_t writeCallback(void* ptr, size_t size, size_t nmemb, FILE* stream) {
|
||||
if (!ptr || !stream) return 0;
|
||||
//size_t totalBytes = size * nmemb;
|
||||
//size_t writtenBytes = fwrite(ptr, 1, totalBytes, stream);
|
||||
//return writtenBytes;
|
||||
return fwrite(ptr, 1, size * nmemb, stream);
|
||||
}
|
||||
#else
|
||||
// Using std::ofstream for writing
|
||||
size_t writeCallback(void* ptr, size_t size, size_t nmemb, std::ostream* stream) {
|
||||
if (!ptr || !stream) return 0;
|
||||
auto& file = *static_cast<std::ofstream*>(stream);
|
||||
//size_t totalBytes = size * nmemb;
|
||||
file.write(static_cast<const char*>(ptr), size * nmemb);
|
||||
return totalBytes;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Your C function
|
||||
int progressCallback(void *ptr, curl_off_t totalToDownload, curl_off_t nowDownloaded, curl_off_t totalToUpload, curl_off_t nowUploaded) {
|
||||
if (!ptr) return 1;
|
||||
|
||||
auto percentage = static_cast<std::atomic<int>*>(ptr);
|
||||
|
||||
if (totalToDownload > 0) {
|
||||
//int newProgress = static_cast<int>((static_cast<double>(nowDownloaded) / static_cast<double>(totalToDownload)) * 100.0);
|
||||
percentage->store(static_cast<int>((static_cast<double>(nowDownloaded) / static_cast<double>(totalToDownload)) * 100.0), std::memory_order_release);
|
||||
}
|
||||
|
||||
if (abortDownload.load(std::memory_order_acquire)) {
|
||||
percentage->store(-1, std::memory_order_release);
|
||||
return 1; // Abort the download
|
||||
}
|
||||
|
||||
return 0; // Continue the download
|
||||
}
|
||||
|
||||
// Global initialization function
|
||||
void initializeCurl() {
|
||||
std::lock_guard<std::mutex> lock(curlInitMutex);
|
||||
if (!curlInitialized.load(std::memory_order_acquire)) {
|
||||
const CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
if (res != CURLE_OK) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("curl_global_init() failed: " + std::string(curl_easy_strerror(res)));
|
||||
#endif
|
||||
// Handle error appropriately, possibly exit the program
|
||||
} else {
|
||||
curlInitialized.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global cleanup function
|
||||
void cleanupCurl() {
|
||||
std::lock_guard<std::mutex> lock(curlInitMutex);
|
||||
if (curlInitialized.load(std::memory_order_acquire)) {
|
||||
curl_global_cleanup();
|
||||
curlInitialized.store(false, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Downloads a file from a URL to a specified destination.
|
||||
*
|
||||
* @param url The URL of the file to download.
|
||||
* @param toDestination The destination path where the file should be saved.
|
||||
* @return True if the download was successful, false otherwise.
|
||||
*/
|
||||
bool downloadFile(const std::string& url, const std::string& toDestination, bool noPercentagePolling) {
|
||||
abortDownload.store(false, std::memory_order_release);
|
||||
|
||||
if (url.find_first_of("{}") != std::string::npos) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Invalid URL: " + url);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string destination = toDestination;
|
||||
if (destination.back() == '/') {
|
||||
createDirectory(destination);
|
||||
const size_t lastSlash = url.find_last_of('/');
|
||||
if (lastSlash != std::string::npos) {
|
||||
destination += url.substr(lastSlash + 1);
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Invalid URL: " + url);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
createDirectory(destination.substr(0, destination.find_last_of('/')));
|
||||
}
|
||||
|
||||
const std::string tempFilePath = getParentDirFromPath(destination) + "." + getFileName(destination) + ".tmp";
|
||||
|
||||
#if USING_FSTREAM_DIRECTIVE
|
||||
// Use ofstream if !USING_FSTREAM_DIRECTIVE is not defined
|
||||
std::ofstream file(tempFilePath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error opening file: " + tempFilePath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// Alternative method of opening file (depending on your platform, like using POSIX open())
|
||||
std::unique_ptr<FILE, FileDeleter> file(fopen(tempFilePath.c_str(), "wb"));
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error opening file: " + tempFilePath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// ADD THIS: Set up write buffer for better performance
|
||||
std::unique_ptr<char[]> writeBuffer;
|
||||
if (DOWNLOAD_WRITE_BUFFER > 0) {
|
||||
writeBuffer = std::make_unique<char[]>(DOWNLOAD_WRITE_BUFFER);
|
||||
// _IOFBF = full buffering, _IOLBF = line buffering, _IONBF = no buffering
|
||||
setvbuf(file.get(), writeBuffer.get(), _IOFBF, DOWNLOAD_WRITE_BUFFER);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ensure curl is initialized
|
||||
initializeCurl();
|
||||
|
||||
std::unique_ptr<CURL, CurlDeleter> curl(curl_easy_init());
|
||||
if (!curl) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error initializing curl.");
|
||||
#endif
|
||||
#if USING_FSTREAM_DIRECTIVE
|
||||
file.close();
|
||||
#else
|
||||
file.reset();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only initialize downloadPercentage if we're tracking progress
|
||||
if (!noPercentagePolling) {
|
||||
downloadPercentage.store(0, std::memory_order_release);
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, writeCallback);
|
||||
#if USING_FSTREAM_DIRECTIVE
|
||||
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &file);
|
||||
#else
|
||||
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, file.get());
|
||||
#endif
|
||||
|
||||
// Conditionally set up progress callback based on noPercentagePolling
|
||||
if (noPercentagePolling) {
|
||||
// Disable progress function entirely
|
||||
curl_easy_setopt(curl.get(), CURLOPT_NOPROGRESS, 1L);
|
||||
} else {
|
||||
// Enable progress callback for percentage updates
|
||||
curl_easy_setopt(curl.get(), CURLOPT_NOPROGRESS, 0L);
|
||||
curl_easy_setopt(curl.get(), CURLOPT_XFERINFOFUNCTION, progressCallback);
|
||||
curl_easy_setopt(curl.get(), CURLOPT_XFERINFODATA, &downloadPercentage);
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, userAgent);
|
||||
curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); // Enable HTTP/2
|
||||
curl_easy_setopt(curl.get(), CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); // Force TLS 1.2
|
||||
|
||||
curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl.get(), CURLOPT_BUFFERSIZE, DOWNLOAD_READ_BUFFER); // Increase buffer size
|
||||
|
||||
// Add timeout options
|
||||
curl_easy_setopt(curl.get(), CURLOPT_CONNECTTIMEOUT, 10L); // 10 seconds to connect
|
||||
curl_easy_setopt(curl.get(), CURLOPT_LOW_SPEED_LIMIT, 1L); // 1 byte/s (virtually any progress)
|
||||
curl_easy_setopt(curl.get(), CURLOPT_LOW_SPEED_TIME, 60L); // 1 minutes of no progress
|
||||
|
||||
CURLcode result = curl_easy_perform(curl.get());
|
||||
|
||||
#if USING_FSTREAM_DIRECTIVE
|
||||
file.close();
|
||||
#else
|
||||
file.reset();
|
||||
#endif
|
||||
|
||||
if (result != CURLE_OK) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (result == CURLE_OPERATION_TIMEDOUT) {
|
||||
if (!disableLogging)
|
||||
logMessage("Download timed out: " + url);
|
||||
} else if (result == CURLE_COULDNT_CONNECT) {
|
||||
if (!disableLogging)
|
||||
logMessage("Could not connect to: " + url);
|
||||
} else {
|
||||
if (!disableLogging)
|
||||
logMessage("Error downloading file: " + std::string(curl_easy_strerror(result)));
|
||||
}
|
||||
#endif
|
||||
deleteFileOrDirectory(tempFilePath);
|
||||
// Only update percentage if we're tracking it
|
||||
if (!noPercentagePolling) {
|
||||
downloadPercentage.store(-1, std::memory_order_release);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if USING_FSTREAM_DIRECTIVE
|
||||
std::ifstream checkFile(tempFilePath);
|
||||
if (!checkFile || checkFile.peek() == std::ifstream::traits_type::eof()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error downloading file: Empty file");
|
||||
#endif
|
||||
deleteFileOrDirectory(tempFilePath);
|
||||
// Only update percentage if we're tracking it
|
||||
if (!noPercentagePolling) {
|
||||
downloadPercentage.store(-1, std::memory_order_release);
|
||||
}
|
||||
checkFile.close();
|
||||
return false;
|
||||
}
|
||||
checkFile.close();
|
||||
#else
|
||||
// Alternative method for checking if the file is empty (POSIX example)
|
||||
struct stat fileStat;
|
||||
if (stat(tempFilePath.c_str(), &fileStat) != 0 || fileStat.st_size == 0) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error downloading file: Empty file");
|
||||
#endif
|
||||
deleteFileOrDirectory(tempFilePath);
|
||||
// Only update percentage if we're tracking it
|
||||
if (!noPercentagePolling) {
|
||||
downloadPercentage.store(-1, std::memory_order_release);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Only update percentage if we're tracking it
|
||||
if (!noPercentagePolling) {
|
||||
downloadPercentage.store(100, std::memory_order_release);
|
||||
}
|
||||
|
||||
// CHECK FOR PROTECTED FILES AND ADD .ultra EXTENSION IF NEEDED
|
||||
if (PROTECTED_FILES.find(destination) != PROTECTED_FILES.end()) {
|
||||
destination += ".ultra";
|
||||
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Protected file detected, renaming download to: " + destination);
|
||||
#endif
|
||||
}
|
||||
|
||||
moveFile(tempFilePath, destination);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Custom I/O function for opening files with larger buffer
|
||||
*/
|
||||
static voidpf ZCALLBACK fopen64_file_func_custom(voidpf opaque, const void* filename, int mode) {
|
||||
FILE* file = nullptr;
|
||||
const char* mode_fopen = nullptr;
|
||||
|
||||
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)
|
||||
mode_fopen = "rb";
|
||||
else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
|
||||
mode_fopen = "r+b";
|
||||
else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
|
||||
mode_fopen = "wb";
|
||||
|
||||
if ((filename != nullptr) && (mode_fopen != nullptr)) {
|
||||
file = fopen((const char*)filename, mode_fopen);
|
||||
if (file && ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ)) {
|
||||
// Set 64KB buffer for reading the ZIP file - reduces syscalls
|
||||
//static const size_t zipReadBufferSize = UNZIP_READ_BUFFER;
|
||||
setvbuf(file, nullptr, _IOFBF, UNZIP_READ_BUFFER);
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
static int ZCALLBACK fclose64_file_func_custom(voidpf opaque, voidpf stream) {
|
||||
int ret = EOF;
|
||||
if (stream != nullptr) {
|
||||
ret = fclose((FILE*)stream);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts files from a ZIP archive to a specified destination.
|
||||
*
|
||||
* Ultra-optimized single-pass extraction with smooth byte-based progress reporting
|
||||
* using miniz with proper 64-bit file support and streaming extraction.
|
||||
* Fixed memory leaks with RAII for proper resource cleanup on abort.
|
||||
*
|
||||
* @param zipFilePath The path to the ZIP archive file.
|
||||
* @param toDestination The destination directory where files should be extracted.
|
||||
* @return True if the extraction was successful, false otherwise.
|
||||
*/
|
||||
bool unzipFile(const std::string& zipFilePath, const std::string& toDestination) {
|
||||
abortUnzip.store(false, std::memory_order_release);
|
||||
unzipPercentage.store(0, std::memory_order_release);
|
||||
|
||||
// Time-based abort checking - pre-calculated constants
|
||||
//u64 lastAbortCheck = armTicksToNs(armGetSystemTick());
|
||||
//u64 currentNanos; // Reused for all tick operations
|
||||
bool success = true;
|
||||
|
||||
// RAII wrapper for unzFile
|
||||
struct UnzFileManager {
|
||||
unzFile file = nullptr;
|
||||
|
||||
UnzFileManager(const std::string& path) {
|
||||
zlib_filefunc64_def ffunc;
|
||||
fill_fopen64_filefunc(&ffunc);
|
||||
ffunc.zopen64_file = fopen64_file_func_custom;
|
||||
ffunc.zclose_file = fclose64_file_func_custom;
|
||||
file = unzOpen2_64(path.c_str(), &ffunc);
|
||||
}
|
||||
|
||||
~UnzFileManager() {
|
||||
if (file) {
|
||||
unzClose(file);
|
||||
file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_valid() const { return file != nullptr; }
|
||||
operator unzFile() const { return file; }
|
||||
};
|
||||
|
||||
// RAII wrapper for output file
|
||||
struct OutputFileManager {
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = nullptr;
|
||||
std::unique_ptr<char[]> buffer;
|
||||
size_t bufferSize;
|
||||
|
||||
OutputFileManager(size_t bufSize) : bufferSize(bufSize) {
|
||||
buffer = std::make_unique<char[]>(bufferSize);
|
||||
}
|
||||
|
||||
bool open(const std::string& path) {
|
||||
close();
|
||||
file = fopen(path.c_str(), "wb");
|
||||
if (file) {
|
||||
setvbuf(file, buffer.get(), _IOFBF, bufferSize);
|
||||
}
|
||||
return file != nullptr;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (file) {
|
||||
fclose(file);
|
||||
file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_open() const { return file != nullptr; }
|
||||
|
||||
size_t write(const void* data, size_t size) {
|
||||
return file ? fwrite(data, 1, size, file) : 0;
|
||||
}
|
||||
|
||||
~OutputFileManager() { close(); }
|
||||
#else
|
||||
std::ofstream file;
|
||||
|
||||
OutputFileManager(size_t bufSize) {
|
||||
// Constructor for consistency with FILE* version
|
||||
}
|
||||
|
||||
bool open(const std::string& path) {
|
||||
close();
|
||||
file.open(path, std::ios::binary);
|
||||
if (file.is_open()) {
|
||||
file.rdbuf()->pubsetbuf(nullptr, UNZIP_WRITE_BUFFER);
|
||||
}
|
||||
return file.is_open();
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (file.is_open()) {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_open() const { return file.is_open(); }
|
||||
|
||||
size_t write(const void* data, size_t size) {
|
||||
if (file.is_open()) {
|
||||
file.write(static_cast<const char*>(data), size);
|
||||
return file.good() ? size : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
UnzFileManager zipFile(zipFilePath);
|
||||
if (!zipFile.is_valid()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to open zip file: " + zipFilePath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get global info about the ZIP file
|
||||
unz_global_info64 globalInfo;
|
||||
if (unzGetGlobalInfo64(zipFile, &globalInfo) != UNZ_OK) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to get zip file info");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
const uLong numFiles = globalInfo.number_entry;
|
||||
if (numFiles == 0) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("No files found in archive");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// ALWAYS calculate total size for accurate byte-based progress
|
||||
ZPOS64_T totalUncompressedSize = 0;
|
||||
char tempFilenameBuffer[512];
|
||||
unz_file_info64 fileInfo;
|
||||
|
||||
// First pass: calculate total uncompressed size
|
||||
int result = unzGoToFirstFile(zipFile);
|
||||
while (result == UNZ_OK) {
|
||||
// Time-based abort check at start of each file (only if 2+ seconds have passed)
|
||||
//currentNanos = armTicksToNs(armGetSystemTick());
|
||||
//if ((currentNanos - lastAbortCheck) >= 2000000000ULL) {
|
||||
// if (abortUnzip.load(std::memory_order_relaxed)) {
|
||||
// unzipPercentage.store(-1, std::memory_order_release);
|
||||
// #if USING_LOGGING_DIRECTIVE
|
||||
// logMessage("Extraction aborted during size calculation");
|
||||
// #endif
|
||||
// abortUnzip.store(false, std::memory_order_release);
|
||||
// return false;
|
||||
// }
|
||||
// lastAbortCheck = currentNanos;
|
||||
//}
|
||||
if (abortUnzip.load(std::memory_order_relaxed)) {
|
||||
unzipPercentage.store(-1, std::memory_order_release);
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Extraction aborted during size calculation");
|
||||
#endif
|
||||
abortUnzip.store(false, std::memory_order_release);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unzGetCurrentFileInfo64(zipFile, &fileInfo, tempFilenameBuffer, sizeof(tempFilenameBuffer),
|
||||
nullptr, 0, nullptr, 0) == UNZ_OK) {
|
||||
const size_t nameLen = strlen(tempFilenameBuffer);
|
||||
if (nameLen > 0 && tempFilenameBuffer[nameLen - 1] != '/') {
|
||||
totalUncompressedSize += std::max(fileInfo.uncompressed_size, static_cast<ZPOS64_T>(1));
|
||||
}
|
||||
}
|
||||
result = unzGoToNextFile(zipFile);
|
||||
}
|
||||
|
||||
// Fallback to 1 if no actual data (avoid division by zero)
|
||||
if (totalUncompressedSize == 0) {
|
||||
totalUncompressedSize = 1;
|
||||
}
|
||||
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging) {
|
||||
logMessage("Processing " + std::to_string(numFiles) + " files, " +
|
||||
std::to_string(totalUncompressedSize) + " total bytes from archive");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Pre-allocate ALL reusable strings and variables outside the main loop
|
||||
std::string fileName, extractedFilePath, directoryPath;
|
||||
//fileName.reserve(512);
|
||||
//extractedFilePath.reserve(1024);
|
||||
//directoryPath.reserve(1024);
|
||||
|
||||
// Single large buffer for extraction - reused for all files
|
||||
const size_t bufferSize = UNZIP_WRITE_BUFFER;
|
||||
std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bufferSize);
|
||||
char filenameBuffer[512]; // Stack allocated for filename reading
|
||||
|
||||
// Progress tracking variables - OPTIMIZED for smooth byte-based tracking
|
||||
ZPOS64_T totalBytesProcessed = 0;
|
||||
uLong filesProcessed = 0;
|
||||
int currentProgress = 0; // Current percentage (0-100)
|
||||
|
||||
// Create output file manager
|
||||
OutputFileManager outputFile(bufferSize);
|
||||
|
||||
// Loop variables moved outside
|
||||
bool extractSuccess;
|
||||
ZPOS64_T fileBytesProcessed;
|
||||
int bytesRead;
|
||||
|
||||
// String operation variables
|
||||
const char* filename;
|
||||
size_t nameLen;
|
||||
size_t lastSlashPos;
|
||||
size_t invalid_pos;
|
||||
size_t start_pos;
|
||||
|
||||
// Ensure destination directory exists
|
||||
createDirectory(toDestination);
|
||||
|
||||
// Ensure destination ends with '/' - pre-allocate final string
|
||||
std::string destination;
|
||||
//destination.reserve(toDestination.size() + 1);
|
||||
|
||||
destination = toDestination;
|
||||
if (!destination.empty() && destination.back() != '/') {
|
||||
destination += '/';
|
||||
}
|
||||
|
||||
int newProgress;;
|
||||
|
||||
// Extract files
|
||||
result = unzGoToFirstFile(zipFile);
|
||||
while (result == UNZ_OK && success) {
|
||||
// Time-based abort check at start of each file (only if 2+ seconds have passed)
|
||||
//currentNanos = armTicksToNs(armGetSystemTick());
|
||||
//if ((currentNanos - lastAbortCheck) >= 2000000000ULL) {
|
||||
// if (abortUnzip.load(std::memory_order_relaxed)) {
|
||||
// success = false;
|
||||
// break; // RAII will handle cleanup
|
||||
// }
|
||||
// lastAbortCheck = currentNanos;
|
||||
//}
|
||||
|
||||
if (abortUnzip.load(std::memory_order_relaxed)) {
|
||||
success = false;
|
||||
break; // RAII will handle cleanup
|
||||
}
|
||||
|
||||
// Get current file info - reuse fileInfo variable
|
||||
if (unzGetCurrentFileInfo64(zipFile, &fileInfo, filenameBuffer, sizeof(filenameBuffer),
|
||||
nullptr, 0, nullptr, 0) != UNZ_OK) {
|
||||
result = unzGoToNextFile(zipFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
filename = filenameBuffer;
|
||||
|
||||
// Quick filename validation
|
||||
if (!filename || filename[0] == '\0') {
|
||||
result = unzGoToNextFile(zipFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
nameLen = strlen(filename);
|
||||
if (nameLen > 0 && filename[nameLen - 1] == '/') { // Skip directories
|
||||
result = unzGoToNextFile(zipFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build extraction path - reuse allocated strings
|
||||
fileName.assign(filename, nameLen);
|
||||
//extractedFilePath.clear();
|
||||
extractedFilePath = destination;
|
||||
extractedFilePath += fileName;
|
||||
|
||||
// Optimized character cleaning - only if needed
|
||||
invalid_pos = extractedFilePath.find_first_of(":*?\"<>|");
|
||||
if (invalid_pos != std::string::npos) {
|
||||
start_pos = std::min(extractedFilePath.find(ROOT_PATH) + 5, extractedFilePath.size());
|
||||
auto it = extractedFilePath.begin() + start_pos;
|
||||
extractedFilePath.erase(std::remove_if(it, extractedFilePath.end(), [](char c) {
|
||||
return c == ':' || c == '*' || c == '?' || c == '\"' || c == '<' || c == '>' || c == '|';
|
||||
}), extractedFilePath.end());
|
||||
}
|
||||
|
||||
// CHECK FOR PROTECTED FILES AND ADD .ultra EXTENSION IF NEEDED
|
||||
if (PROTECTED_FILES.find(extractedFilePath) != PROTECTED_FILES.end()) {
|
||||
extractedFilePath += ".ultra";
|
||||
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Protected file detected, renaming to: " + extractedFilePath);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Open the current file in the ZIP
|
||||
if (unzOpenCurrentFile(zipFile) != UNZ_OK) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Could not open file in ZIP: " + fileName);
|
||||
#endif
|
||||
result = unzGoToNextFile(zipFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create directory if needed
|
||||
lastSlashPos = extractedFilePath.find_last_of('/');
|
||||
if (lastSlashPos != std::string::npos) {
|
||||
directoryPath.assign(extractedFilePath, 0, lastSlashPos + 1);
|
||||
createDirectory(directoryPath);
|
||||
}
|
||||
|
||||
// Open output file
|
||||
if (!outputFile.open(extractedFilePath)) {
|
||||
unzCloseCurrentFile(zipFile);
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error creating file: " + extractedFilePath);
|
||||
#endif
|
||||
result = unzGoToNextFile(zipFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract file data in chunks
|
||||
extractSuccess = true;
|
||||
fileBytesProcessed = 0;
|
||||
|
||||
|
||||
while ((bytesRead = unzReadCurrentFile(zipFile, buffer.get(), bufferSize)) > 0) {
|
||||
if (abortUnzip.load(std::memory_order_relaxed)) {
|
||||
extractSuccess = false;
|
||||
break; // RAII will handle cleanup
|
||||
}
|
||||
|
||||
// Write data to file
|
||||
if (outputFile.write(buffer.get(), bytesRead) != static_cast<size_t>(bytesRead)) {
|
||||
extractSuccess = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Update progress tracking
|
||||
fileBytesProcessed += bytesRead;
|
||||
totalBytesProcessed += bytesRead;
|
||||
|
||||
// FIXED: Allow progress to reach 100% naturally during processing
|
||||
if (totalUncompressedSize > 0) {
|
||||
newProgress = static_cast<int>((totalBytesProcessed * 100) / totalUncompressedSize);
|
||||
if (newProgress > currentProgress && newProgress <= 100) {
|
||||
currentProgress = newProgress;
|
||||
unzipPercentage.store(currentProgress, std::memory_order_release);
|
||||
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
// Only log at 10% intervals to avoid spam
|
||||
if (currentProgress % 10 == 0) {
|
||||
if (!disableLogging) {
|
||||
logMessage("Progress: " + std::to_string(currentProgress) + "% (" +
|
||||
std::to_string(totalBytesProcessed) + "/" +
|
||||
std::to_string(totalUncompressedSize) + " bytes)");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CRITICAL FIX: Handle 0-byte files that don't enter the while loop
|
||||
if (bytesRead == 0 && fileBytesProcessed == 0 && extractSuccess) {
|
||||
// This is a 0-byte file - update progress by 1 byte equivalent
|
||||
totalBytesProcessed += 1;
|
||||
|
||||
// Update progress for 0-byte files
|
||||
if (totalUncompressedSize > 0) {
|
||||
newProgress = static_cast<int>((totalBytesProcessed * 100) / totalUncompressedSize);
|
||||
if (newProgress > currentProgress && newProgress <= 100) {
|
||||
currentProgress = newProgress;
|
||||
unzipPercentage.store(currentProgress, std::memory_order_release);
|
||||
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (currentProgress % 10 == 0) {
|
||||
if (!disableLogging)
|
||||
logMessage("Progress: " + std::to_string(currentProgress) + "% (0-byte file processed)");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for read errors
|
||||
if (bytesRead < 0) {
|
||||
extractSuccess = false;
|
||||
}
|
||||
|
||||
// Close current file handles
|
||||
outputFile.close();
|
||||
unzCloseCurrentFile(zipFile);
|
||||
|
||||
if (!extractSuccess) {
|
||||
deleteFileOrDirectory(extractedFilePath);
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to extract: " + fileName);
|
||||
#endif
|
||||
|
||||
if (abortUnzip.load(std::memory_order_relaxed)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
filesProcessed++;
|
||||
}
|
||||
|
||||
// Move to next file
|
||||
result = unzGoToNextFile(zipFile);
|
||||
}
|
||||
|
||||
// Check final abort state
|
||||
if (abortUnzip.load(std::memory_order_relaxed)) {
|
||||
unzipPercentage.store(-1, std::memory_order_release);
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Extraction aborted by user");
|
||||
#endif
|
||||
abortUnzip.store(false, std::memory_order_release);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (success && filesProcessed > 0) {
|
||||
abortUnzip.store(false, std::memory_order_release);
|
||||
unzipPercentage.store(100, std::memory_order_release);
|
||||
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging) {
|
||||
logMessage("Extraction completed: " + std::to_string(filesProcessed) + " files, " +
|
||||
std::to_string(totalBytesProcessed) + " bytes");
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
} else {
|
||||
abortUnzip.store(false, std::memory_order_release);
|
||||
unzipPercentage.store(-1, std::memory_order_release);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,506 @@
|
||||
/********************************************************************************
|
||||
* File: get_funcs.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file provides the implementations of functions declared in
|
||||
* get_funcs.hpp. These functions are responsible for retrieving and handling
|
||||
* data from the file system and JSON files, including parsing overlay module
|
||||
* information, reading file contents, and accessing structured data used
|
||||
* in the Ultrahand Overlay project.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#include "get_funcs.hpp"
|
||||
|
||||
|
||||
namespace ult {
|
||||
|
||||
/**
|
||||
* @brief Reads the contents of a file and returns it as a string, normalizing line endings.
|
||||
*
|
||||
* @param filePath The path to the file to be read.
|
||||
* @return The content of the file as a string with line endings normalized to '\n'.
|
||||
*/
|
||||
//std::string getFileContents(const std::string& filePath) {
|
||||
// #if !USING_FSTREAM_DIRECTIVE
|
||||
// FILE* file = fopen(filePath.c_str(), "rb");
|
||||
// if (!file) {
|
||||
// #if USING_LOGGING_DIRECTIVE
|
||||
// logMessage("Failed to open file: " + filePath);
|
||||
// #endif
|
||||
// return "";
|
||||
// }
|
||||
//
|
||||
// // Determine the file size
|
||||
// fseek(file, 0, SEEK_END);
|
||||
// long size = ftell(file);
|
||||
// if (size <= 0) {
|
||||
// fclose(file);
|
||||
// return "";
|
||||
// }
|
||||
// fseek(file, 0, SEEK_SET);
|
||||
//
|
||||
// // Read the entire file into a string
|
||||
// std::string content(size, '\0');
|
||||
// if (fread(&content[0], 1, size, file) != static_cast<size_t>(size)) {
|
||||
// #if USING_LOGGING_DIRECTIVE
|
||||
// logMessage("Failed to read file: " + filePath);
|
||||
// #endif
|
||||
// fclose(file);
|
||||
// return "";
|
||||
// }
|
||||
//
|
||||
// fclose(file);
|
||||
//
|
||||
// #else
|
||||
// std::ifstream file(filePath, std::ios::binary);
|
||||
// if (!file) {
|
||||
// #if USING_LOGGING_DIRECTIVE
|
||||
// logMessage("Failed to open file: " + filePath);
|
||||
// #endif
|
||||
// return "";
|
||||
// }
|
||||
//
|
||||
// // Determine the file size
|
||||
// file.seekg(0, std::ios::end);
|
||||
// std::streamsize size = file.tellg();
|
||||
// if (size <= 0) {
|
||||
// return "";
|
||||
// }
|
||||
// file.seekg(0, std::ios::beg);
|
||||
//
|
||||
// // Read the entire file into a string
|
||||
// std::string content(size, '\0');
|
||||
// if (!file.read(&content[0], size)) {
|
||||
// #if USING_LOGGING_DIRECTIVE
|
||||
// logMessage("Failed to read file: " + filePath);
|
||||
// #endif
|
||||
// return "";
|
||||
// }
|
||||
// #endif
|
||||
//
|
||||
// // Erase any carriage return characters (normalize line endings)
|
||||
// content.erase(std::remove(content.begin(), content.end(), '\r'), content.end());
|
||||
// return content;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Concatenates the provided directory and file names to form a destination path.
|
||||
*
|
||||
* @param destinationDir The directory where the file should be placed.
|
||||
* @param fileName The name of the file.
|
||||
* @return The destination path as a string.
|
||||
*/
|
||||
std::string getDestinationPath(const std::string& destinationDir,
|
||||
const std::string& fileName)
|
||||
{
|
||||
// e.g. "foo/bar" + "/" + "baz.txt" → "foo/bar/baz.txt", but if destinationDir ended in '/',
|
||||
// you’d get "foo/bar//baz.txt" → collapse again:
|
||||
std::string combined = destinationDir + "/" + fileName;
|
||||
preprocessPath(combined);
|
||||
return combined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts the value part from a string line containing a key-value pair.
|
||||
*
|
||||
* @param line The string line containing a key-value pair (e.g., "key=value").
|
||||
* @return The extracted value as a string. If no value is found, an empty string is returned.
|
||||
*/
|
||||
std::string getValueFromLine(const std::string& line) {
|
||||
const size_t equalsPos = line.rfind('=');
|
||||
if (equalsPos == std::string::npos || equalsPos + 1 >= line.size()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// OPTIMIZATION: Find trim boundaries directly - no temporary string
|
||||
size_t start = equalsPos + 1;
|
||||
size_t end = line.size() - 1;
|
||||
|
||||
// Skip leading whitespace
|
||||
while (start <= end && (line[start] == ' ' || line[start] == '\t' ||
|
||||
line[start] == '\n' || line[start] == '\r' ||
|
||||
line[start] == '\f' || line[start] == '\v')) {
|
||||
++start;
|
||||
}
|
||||
|
||||
// Skip trailing whitespace
|
||||
while (end >= start && (line[end] == ' ' || line[end] == '\t' ||
|
||||
line[end] == '\n' || line[end] == '\r' ||
|
||||
line[end] == '\f' || line[end] == '\v')) {
|
||||
--end;
|
||||
}
|
||||
|
||||
// Return trimmed substring directly
|
||||
return (start <= end) ? line.substr(start, end - start + 1) : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the name from a file path, including handling directories.
|
||||
*
|
||||
* @param path The file path from which to extract the name.
|
||||
* @return The extracted name as a string. If the path indicates a directory, it extracts the last directory name.
|
||||
* If the path is empty or no name is found, an empty string is returned.
|
||||
*/
|
||||
std::string getNameFromPath(const std::string& path) {
|
||||
const size_t lastNonSlash = path.find_last_not_of('/');
|
||||
if (lastNonSlash == std::string::npos) {
|
||||
return ""; // All slashes, or empty string effectively
|
||||
}
|
||||
|
||||
const size_t lastSlash = path.find_last_of('/', lastNonSlash);
|
||||
if (lastSlash == std::string::npos) {
|
||||
return path.substr(0, lastNonSlash + 1); // No slashes, the entire path is a filename
|
||||
}
|
||||
|
||||
return path.substr(lastSlash + 1, lastNonSlash - lastSlash); // Standard case, efficiently handled
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the file name from a full file path.
|
||||
*
|
||||
* This function takes a filesystem path and returns only the file name,
|
||||
* stripping away any directory paths that precede it.
|
||||
*
|
||||
* @param path The full path to the file.
|
||||
* @return The file name extracted from the full path.
|
||||
*/
|
||||
std::string getFileName(const std::string& path) {
|
||||
const size_t pos = path.find_last_of('/');
|
||||
return (pos != std::string::npos) ? path.substr(pos + 1) : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the name of the parent directory from a given file path at a specified level.
|
||||
*
|
||||
* @param path The file path from which to extract the parent directory name.
|
||||
* @param level The level of the parent directory to extract (0 for immediate parent, 1 for grandparent, and so on).
|
||||
* @return The name of the parent directory at the specified level.
|
||||
*/
|
||||
std::string getParentDirNameFromPath(const std::string& path, size_t level) {
|
||||
if (path.empty()) return "";
|
||||
|
||||
// Start from the end of the string and move backwards to find the slashes
|
||||
size_t endPos = path.find_last_not_of('/');
|
||||
if (endPos == std::string::npos) return "";
|
||||
|
||||
size_t pos = path.rfind('/', endPos);
|
||||
if (pos == std::string::npos || pos == 0) return "";
|
||||
|
||||
// Navigate up the specified number of levels
|
||||
while (level-- > 0 && pos != std::string::npos) {
|
||||
endPos = pos - 1;
|
||||
pos = path.rfind('/', endPos);
|
||||
if (pos == std::string::npos || pos == 0) return "";
|
||||
}
|
||||
|
||||
size_t start = path.rfind('/', pos - 1);
|
||||
if (start == std::string::npos) start = 0;
|
||||
else start += 1;
|
||||
|
||||
// OPTIMIZATION 1: Use find_first_of instead of manual loop - much faster
|
||||
const bool hasWhitespace = (path.find_first_of(" \t\n\r\f\v", start, pos - start) != std::string::npos);
|
||||
|
||||
if (hasWhitespace) {
|
||||
// OPTIMIZATION 2: Pre-allocate exact size and build efficiently
|
||||
const size_t dirNameLen = pos - start;
|
||||
std::string result;
|
||||
result.reserve(dirNameLen + 2); // +2 for quotes
|
||||
|
||||
result = '"';
|
||||
result.append(path, start, dirNameLen);
|
||||
result += '"';
|
||||
return result;
|
||||
} else {
|
||||
return path.substr(start, pos - start);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the parent directory path from a given file path.
|
||||
*
|
||||
* @param path The file path from which to extract the parent directory path.
|
||||
* @return The parent directory path.
|
||||
*/
|
||||
std::string getParentDirFromPath(const std::string& path) {
|
||||
const size_t lastSlash = path.find_last_of('/');
|
||||
if (lastSlash != std::string::npos) {
|
||||
return path.substr(0, lastSlash + 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets a list of subdirectories in a directory.
|
||||
*
|
||||
* @param directoryPath The path of the directory to search.
|
||||
* @return A vector of strings containing the names of subdirectories.
|
||||
*/
|
||||
std::vector<std::string> getSubdirectories(const std::string& directoryPath) {
|
||||
std::vector<std::string> subdirectories;
|
||||
std::unique_ptr<DIR, DirCloser> dir(opendir(directoryPath.c_str()));
|
||||
|
||||
if (!dir) return subdirectories;
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir.get())) != nullptr) {
|
||||
const std::string entryName = entry->d_name;
|
||||
|
||||
// Skip . and ..
|
||||
if (entryName == "." || entryName == "..") continue;
|
||||
|
||||
const std::string fullPath = directoryPath + "/" + entryName;
|
||||
|
||||
if (isDirectory(entry, fullPath)) {
|
||||
subdirectories.emplace_back(entryName);
|
||||
}
|
||||
}
|
||||
|
||||
return subdirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a directory entry is a directory (no caching).
|
||||
* Fast path for known types, stat() only when necessary.
|
||||
*/
|
||||
inline bool isDirectory(struct dirent* entry, const std::string& path) {
|
||||
// Fast path - most filesystems populate d_type correctly
|
||||
if (entry->d_type == DT_DIR) {
|
||||
return true;
|
||||
} else if (entry->d_type != DT_UNKNOWN) {
|
||||
return false; // DT_REG, DT_LNK, etc.
|
||||
}
|
||||
|
||||
// Only stat when d_type is unknown (rare on modern filesystems)
|
||||
struct stat entryStat;
|
||||
return (stat(path.c_str(), &entryStat) == 0) && S_ISDIR(entryStat.st_mode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Iteratively retrieves a list of files from a directory.
|
||||
*
|
||||
* @param directoryPath The path of the directory to search.
|
||||
* @return A vector of strings containing the paths of the files.
|
||||
*/
|
||||
std::vector<std::string> getFilesListFromDirectory(const std::string& directoryPath) {
|
||||
std::vector<std::string> fileList;
|
||||
std::vector<std::string> dirsToProcess;
|
||||
|
||||
// Initialize with the starting directory
|
||||
dirsToProcess.emplace_back(directoryPath);
|
||||
|
||||
// Pre-allocate string buffer to avoid repeated allocations
|
||||
std::string fullPath;
|
||||
std::string currentDir;
|
||||
|
||||
size_t dirIndex = 0;
|
||||
while (dirIndex < dirsToProcess.size()) {
|
||||
currentDir = std::move(dirsToProcess[dirIndex]);
|
||||
dirsToProcess[dirIndex].shrink_to_fit();
|
||||
dirIndex++;
|
||||
|
||||
std::unique_ptr<DIR, DirCloser> dir(opendir(currentDir.c_str()));
|
||||
if (!dir) continue;
|
||||
|
||||
// Cache directory path info
|
||||
const bool needsSlash = currentDir.back() != '/';
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir.get())) != nullptr) {
|
||||
const char* entryName = entry->d_name;
|
||||
|
||||
// Direct comparison without string creation
|
||||
if (entryName[0] == '.' && (entryName[1] == '\0' || (entryName[1] == '.' && entryName[2] == '\0'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// More efficient path building
|
||||
fullPath.clear();
|
||||
fullPath.assign(currentDir);
|
||||
if (needsSlash) fullPath += '/';
|
||||
fullPath += entryName;
|
||||
|
||||
if (entry->d_type == DT_REG) {
|
||||
// Definitely a regular file
|
||||
fileList.emplace_back(fullPath);
|
||||
} else if (isDirectory(entry, fullPath)) {
|
||||
// Add directory to processing queue
|
||||
dirsToProcess.emplace_back(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
|
||||
// Iterative function to handle wildcard directories and file patterns
|
||||
void handleDirectory(const std::string& basePath,
|
||||
const std::vector<std::string>& parts,
|
||||
size_t partIndex,
|
||||
std::vector<std::string>& results,
|
||||
bool directoryOnly,
|
||||
size_t maxLines) {
|
||||
|
||||
std::vector<std::pair<std::string, size_t>> stack;
|
||||
stack.emplace_back(basePath, partIndex);
|
||||
|
||||
// Pre-declare strings to avoid repeated allocations
|
||||
std::string fullPath;
|
||||
std::string result;
|
||||
std::string currentPath;
|
||||
//fullPath.reserve(512);
|
||||
//result.reserve(512);
|
||||
//currentPath.reserve(512);
|
||||
|
||||
struct stat st;
|
||||
|
||||
bool isDir;
|
||||
//std::string pathRef;
|
||||
size_t currentPartIndex;
|
||||
|
||||
while (!stack.empty()) {
|
||||
if (maxLines > 0 && results.size() >= maxLines) return;
|
||||
|
||||
std::tie(currentPath, currentPartIndex) = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
// Copy once to avoid repeated access
|
||||
//currentPath = pathRef;
|
||||
|
||||
if (currentPartIndex >= parts.size()) continue;
|
||||
|
||||
DIR* dirPtr = opendir(currentPath.c_str());
|
||||
if (!dirPtr) continue;
|
||||
std::unique_ptr<DIR, DirCloser> dir(dirPtr);
|
||||
|
||||
const std::string& pattern = parts[currentPartIndex];
|
||||
const bool isLastPart = (currentPartIndex == parts.size() - 1);
|
||||
const bool needsSlash = currentPath.back() != '/';
|
||||
|
||||
// Pre-calculate base path for efficiency
|
||||
//const size_t basePathLen = currentPath.length();
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir.get())) != nullptr) {
|
||||
if (maxLines > 0 && results.size() >= maxLines) return;
|
||||
|
||||
const char* name = entry->d_name;
|
||||
if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) continue;
|
||||
|
||||
if (fnmatch(pattern.c_str(), name, FNM_NOESCAPE) != 0) continue;
|
||||
|
||||
if (entry->d_type != DT_UNKNOWN) {
|
||||
isDir = (entry->d_type == DT_DIR);
|
||||
} else {
|
||||
// More efficient path building for stat check
|
||||
fullPath.clear();
|
||||
fullPath.assign(currentPath);
|
||||
if (needsSlash) fullPath += '/';
|
||||
fullPath += name;
|
||||
isDir = (stat(fullPath.c_str(), &st) == 0) && S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
if (isLastPart) {
|
||||
if (!directoryOnly || isDir) {
|
||||
// More efficient result building
|
||||
result.clear();
|
||||
result.assign(currentPath);
|
||||
if (needsSlash) result += '/';
|
||||
result += name;
|
||||
if (isDir) result += '/';
|
||||
results.emplace_back(std::move(result));
|
||||
if (maxLines > 0 && results.size() >= maxLines) return;
|
||||
}
|
||||
} else if (isDir) {
|
||||
// More efficient path building for stack
|
||||
fullPath.clear();
|
||||
fullPath.assign(currentPath);
|
||||
if (needsSlash) fullPath += '/';
|
||||
fullPath += name;
|
||||
stack.emplace_back(std::move(fullPath), currentPartIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a list of files and folders based on a wildcard pattern.
|
||||
*
|
||||
* This function searches for files and folders in a directory that match the
|
||||
* specified wildcard pattern.
|
||||
*
|
||||
* @param pathPattern The wildcard pattern to match files and folders.
|
||||
* @return A vector of strings containing the paths of matching files and folders.
|
||||
*/
|
||||
std::vector<std::string> getFilesListByWildcards(const std::string& pathPattern, size_t maxLines) {
|
||||
std::vector<std::string> results;
|
||||
|
||||
if (pathPattern.empty()) return results;
|
||||
|
||||
// Disallow any `/**/` or ending with `/**`
|
||||
if (pathPattern.find("**") != std::string::npos || pathPattern.find("*null") != std::string::npos || pathPattern.find("null*") != std::string::npos) {
|
||||
return results; // Exclude invalid patterns
|
||||
}
|
||||
|
||||
const bool directoryOnly = pathPattern.back() == '/';
|
||||
const size_t prefixEnd = pathPattern.find(":/");
|
||||
|
||||
if (prefixEnd == std::string::npos) return results;
|
||||
|
||||
const std::string basePath = pathPattern.substr(0, prefixEnd + 2);
|
||||
std::vector<std::string> parts;
|
||||
|
||||
size_t start = prefixEnd + 2;
|
||||
size_t pos = start;
|
||||
const size_t pathLen = pathPattern.length();
|
||||
|
||||
while (pos <= pathLen) {
|
||||
if (pos == pathLen || pathPattern[pos] == '/') {
|
||||
if (pos > start) {
|
||||
parts.emplace_back(pathPattern.data() + start, pos - start);
|
||||
}
|
||||
start = pos + 1;
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (start < pathLen && !directoryOnly) {
|
||||
parts.emplace_back(pathPattern.data() + start, pathLen - start);
|
||||
}
|
||||
|
||||
// Extra: check parsed parts to disallow "**"
|
||||
for (size_t i = 0; i + 1 < parts.size(); ++i) {
|
||||
if (parts[i] == "**" && parts[i + 1] == "**") {
|
||||
return results; // invalid, exclude
|
||||
}
|
||||
}
|
||||
|
||||
if (!parts.empty()) {
|
||||
handleDirectory(basePath, parts, 0, results, directoryOnly, maxLines);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/********************************************************************************
|
||||
* File: global_vars.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file provides the definitions of global constants and paths used
|
||||
* throughout the Ultrahand Overlay project. These constants are essential for
|
||||
* file management and configuration settings within the application.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository:
|
||||
* GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay
|
||||
*
|
||||
* Note: This notice is integral to the project's documentation and must not be
|
||||
* altered or removed.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#include "global_vars.hpp"
|
||||
|
||||
namespace ult {
|
||||
|
||||
// Base paths
|
||||
const std::string ROOT_PATH = "sdmc:/";
|
||||
const std::string BASE_CONFIG_PATH = ROOT_PATH + "config/ultrahand/";
|
||||
const std::string TESLA_CONFIG_PATH = ROOT_PATH + "config/tesla/";
|
||||
const std::string SWITCH_PATH = ROOT_PATH + "switch/";
|
||||
|
||||
// Filenames
|
||||
CONSTEXPR_STRING std::string CONFIG_FILENAME = "config.ini";
|
||||
const std::string BOOT_PACKAGE_FILENAME = "boot_package.ini";
|
||||
const std::string EXIT_PACKAGE_FILENAME = "exit_package.ini";
|
||||
const std::string PACKAGE_FILENAME = "package.ini";
|
||||
const std::string THEME_FILENAME = "theme.ini";
|
||||
const std::string WALLPAPER_FILENAME = "wallpaper.rgba";
|
||||
const std::string FUSE_FILENAME = "fuse.ini";
|
||||
const std::string OVERLAYS_INI_FILENAME = "overlays.ini";
|
||||
const std::string PACKAGES_INI_FILENAME = "packages.ini";
|
||||
const std::string NOTIFICATIONS_FLAG_FILENAME = "NOTIFICATIONS.flag";
|
||||
|
||||
// Project names
|
||||
CONSTEXPR_STRING std::string ULTRAHAND_PROJECT_NAME = "ultrahand";
|
||||
CONSTEXPR_STRING std::string CAPITAL_ULTRAHAND_PROJECT_NAME = "Ultrahand";
|
||||
CONSTEXPR_STRING std::string SPLIT_PROJECT_NAME_1 = "Ultra";
|
||||
CONSTEXPR_STRING std::string SPLIT_PROJECT_NAME_2 = "hand";
|
||||
|
||||
// Paths
|
||||
const std::string SETTINGS_PATH = BASE_CONFIG_PATH;
|
||||
const std::string ULTRAHAND_CONFIG_INI_PATH = BASE_CONFIG_PATH + CONFIG_FILENAME;
|
||||
const std::string TESLA_CONFIG_INI_PATH = TESLA_CONFIG_PATH + CONFIG_FILENAME;
|
||||
const std::string LANG_PATH = BASE_CONFIG_PATH + "lang/";
|
||||
const std::string THEMES_PATH = BASE_CONFIG_PATH + "themes/";
|
||||
const std::string WALLPAPERS_PATH = BASE_CONFIG_PATH + "wallpapers/";
|
||||
const std::string FLAGS_PATH = BASE_CONFIG_PATH + "flags/";
|
||||
const std::string NOTIFICATIONS_PATH = BASE_CONFIG_PATH + "notifications/";
|
||||
const std::string PAYLOADS_PATH = BASE_CONFIG_PATH + "payloads/";
|
||||
const std::string HB_APPSTORE_JSON = SWITCH_PATH + ".appstore/.get/packages/UltrahandOverlay/info.json";
|
||||
std::string THEME_CONFIG_INI_PATH = BASE_CONFIG_PATH + THEME_FILENAME;
|
||||
std::string WALLPAPER_PATH = BASE_CONFIG_PATH + WALLPAPER_FILENAME;
|
||||
const std::string DOWNLOADS_PATH = BASE_CONFIG_PATH + "downloads/";
|
||||
const std::string EXPANSION_PATH = BASE_CONFIG_PATH + "expansion/";
|
||||
const std::string FUSE_DATA_INI_PATH = BASE_CONFIG_PATH + FUSE_FILENAME;
|
||||
const std::string PACKAGE_PATH = SWITCH_PATH + ".packages/";
|
||||
const std::string OVERLAY_PATH = SWITCH_PATH + ".overlays/";
|
||||
const std::string OVERLAYS_INI_FILEPATH = BASE_CONFIG_PATH + OVERLAYS_INI_FILENAME;
|
||||
const std::string PACKAGES_INI_FILEPATH = BASE_CONFIG_PATH + PACKAGES_INI_FILENAME;
|
||||
const std::string NOTIFICATIONS_FLAG_FILEPATH = FLAGS_PATH + NOTIFICATIONS_FLAG_FILENAME;
|
||||
|
||||
// Protected files
|
||||
const std::set<std::string> PROTECTED_FILES = {
|
||||
ROOT_PATH + "atmosphere/package3",
|
||||
ROOT_PATH + "atmosphere/stratosphere.romfs"
|
||||
};
|
||||
|
||||
// GitHub URLs
|
||||
const std::string GITHUB_BASE_URL = "https://github.com/ppkantorski/";
|
||||
const std::string GITHUB_RAW_BASE_URL = "https://raw.githubusercontent.com/ppkantorski/";
|
||||
const std::string ULTRAHAND_REPO_URL = GITHUB_BASE_URL + "Ultrahand-Overlay/";
|
||||
const std::string INCLUDED_THEME_FOLDER_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/themes/";
|
||||
const std::string LATEST_RELEASE_INFO_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/RELEASE.ini";
|
||||
const std::string NX_OVLLOADER_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/latest/download/nx-ovlloader.zip";
|
||||
const std::string NX_OVLLOADER_PLUS_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/latest/download/nx-ovlloader+.zip";
|
||||
const std::string OLD_NX_OVLLOADER_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/download/v1.0.8/nx-ovlloader.zip";
|
||||
const std::string OLD_NX_OVLLOADER_PLUS_ZIP_URL = GITHUB_BASE_URL + "nx-ovlloader/releases/download/v1.0.8/nx-ovlloader+.zip";
|
||||
const std::string UPDATER_PAYLOAD_URL = GITHUB_RAW_BASE_URL + "Ultrahand-Overlay/main/payloads/ultrahand_updater.bin";
|
||||
|
||||
// Launch options
|
||||
const std::string LAUNCH_ARGS_STR = "launch_args";
|
||||
const std::string USE_LAUNCH_ARGS_STR = "use_launch_args";
|
||||
const std::string USE_QUICK_LAUNCH_STR = "use_quick_launch";
|
||||
const std::string USE_BOOT_PACKAGE_STR = "use_boot_package";
|
||||
const std::string USE_EXIT_PACKAGE_STR = "use_exit_package";
|
||||
const std::string USE_LOGGING_STR = "use_logging";
|
||||
|
||||
// Combos
|
||||
CONSTEXPR_STRING std::string TESLA_COMBO_STR = "L+DDOWN+RS";
|
||||
CONSTEXPR_STRING std::string ULTRAHAND_COMBO_STR = "ZL+ZR+DDOWN";
|
||||
|
||||
// System / mode strings
|
||||
CONSTEXPR_STRING std::string FUSE_STR = "fuse";
|
||||
CONSTEXPR_STRING std::string TESLA_STR = "tesla";
|
||||
CONSTEXPR_STRING std::string ERISTA_STR = "erista";
|
||||
CONSTEXPR_STRING std::string MARIKO_STR = "mariko";
|
||||
CONSTEXPR_STRING std::string KEY_COMBO_STR = "key_combo";
|
||||
CONSTEXPR_STRING std::string DEFAULT_LANG_STR = "default_lang";
|
||||
|
||||
// Generic strings
|
||||
CONSTEXPR_STRING std::string LIST_STR = "list";
|
||||
CONSTEXPR_STRING std::string LIST_FILE_STR = "list_file";
|
||||
CONSTEXPR_STRING std::string JSON_STR = "json";
|
||||
CONSTEXPR_STRING std::string JSON_FILE_STR = "json_file";
|
||||
CONSTEXPR_STRING std::string INI_FILE_STR = "ini_file";
|
||||
CONSTEXPR_STRING std::string HEX_FILE_STR = "hex_file";
|
||||
CONSTEXPR_STRING std::string PACKAGE_STR = "package";
|
||||
CONSTEXPR_STRING std::string PACKAGES_STR = "packages";
|
||||
CONSTEXPR_STRING std::string OVERLAY_STR = "overlay";
|
||||
CONSTEXPR_STRING std::string OVERLAYS_STR = "overlays";
|
||||
const std::string IN_OVERLAY_STR = "in_overlay";
|
||||
const std::string IN_HIDDEN_OVERLAY_STR = "in_hidden_overlay";
|
||||
const std::string IN_HIDDEN_PACKAGE_STR = "in_hidden_package";
|
||||
CONSTEXPR_STRING std::string FILE_STR = "file";
|
||||
CONSTEXPR_STRING std::string SYSTEM_STR = "system";
|
||||
CONSTEXPR_STRING std::string MODE_STR = "mode";
|
||||
CONSTEXPR_STRING std::string GROUPING_STR = "grouping";
|
||||
CONSTEXPR_STRING std::string FOOTER_STR = "footer";
|
||||
CONSTEXPR_STRING std::string TOGGLE_STR = "toggle";
|
||||
CONSTEXPR_STRING std::string LEFT_STR = "left";
|
||||
CONSTEXPR_STRING std::string RIGHT_STR = "right";
|
||||
CONSTEXPR_STRING std::string CENTER_STR = "center";
|
||||
CONSTEXPR_STRING std::string HIDE_STR = "hide";
|
||||
CONSTEXPR_STRING std::string STAR_STR = "star";
|
||||
CONSTEXPR_STRING std::string PRIORITY_STR = "priority";
|
||||
CONSTEXPR_STRING std::string ON_STR = "on";
|
||||
CONSTEXPR_STRING std::string OFF_STR = "off";
|
||||
CONSTEXPR_STRING std::string CAPITAL_ON_STR = "On";
|
||||
CONSTEXPR_STRING std::string CAPITAL_OFF_STR = "Off";
|
||||
CONSTEXPR_STRING std::string TRUE_STR = "true";
|
||||
CONSTEXPR_STRING std::string FALSE_STR = "false";
|
||||
CONSTEXPR_STRING std::string GLOBAL_STR = "global";
|
||||
CONSTEXPR_STRING std::string DEFAULT_STR = "default";
|
||||
CONSTEXPR_STRING std::string SLOT_STR = "slot";
|
||||
CONSTEXPR_STRING std::string OPTION_STR = "option";
|
||||
CONSTEXPR_STRING std::string FORWARDER_STR = "forwarder";
|
||||
CONSTEXPR_STRING std::string TEXT_STR = "text";
|
||||
CONSTEXPR_STRING std::string TABLE_STR = "table";
|
||||
CONSTEXPR_STRING std::string TRACKBAR_STR = "trackbar";
|
||||
CONSTEXPR_STRING std::string STEP_TRACKBAR_STR = "step_trackbar";
|
||||
const std::string NAMED_STEP_TRACKBAR_STR = "named_step_trackbar";
|
||||
CONSTEXPR_STRING std::string NULL_STR = "null";
|
||||
CONSTEXPR_STRING std::string THEME_STR = "theme";
|
||||
CONSTEXPR_STRING std::string NOT_AVAILABLE_STR = "Not available";
|
||||
CONSTEXPR_STRING std::string MEMORY_STR = "memory";
|
||||
|
||||
// Pre-defined symbols
|
||||
CONSTEXPR_STRING std::string OPTION_SYMBOL = "\u22EF";
|
||||
CONSTEXPR_STRING std::string DROPDOWN_SYMBOL = "\uE14A";
|
||||
CONSTEXPR_STRING std::string CHECKMARK_SYMBOL = "\uE14B";
|
||||
CONSTEXPR_STRING std::string CROSSMARK_SYMBOL = "\uE14C";
|
||||
CONSTEXPR_STRING std::string DOWNLOAD_SYMBOL = "\u2193";
|
||||
CONSTEXPR_STRING std::string UNZIP_SYMBOL = "\u2191";
|
||||
CONSTEXPR_STRING std::string COPY_SYMBOL = "\u2192";
|
||||
CONSTEXPR_STRING std::string INPROGRESS_SYMBOL = "\u25CF";
|
||||
CONSTEXPR_STRING std::string STAR_SYMBOL = "\u2605";
|
||||
CONSTEXPR_STRING std::string DIVIDER_SYMBOL = "";
|
||||
|
||||
const std::vector<std::string> THROBBER_SYMBOLS = {"", "", "", "", "", "", "", ""};
|
||||
|
||||
// Atomic variables for progress tracking
|
||||
std::atomic<int> displayPercentage(0); // for interpreter percentage progress
|
||||
|
||||
void resetPercentages() {
|
||||
displayPercentage.store(-1, std::memory_order_release);
|
||||
downloadPercentage.store(-1, std::memory_order_release);
|
||||
unzipPercentage.store(-1, std::memory_order_release);
|
||||
copyPercentage.store(-1, std::memory_order_release);
|
||||
}
|
||||
|
||||
} // namespace ult
|
||||
@@ -0,0 +1,881 @@
|
||||
/********************************************************************************
|
||||
* File: hex_funcs.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file implements the functions declared in hex_funcs.hpp.
|
||||
* These functions provide support for manipulating hexadecimal data,
|
||||
* including conversions between ASCII and hexadecimal strings,
|
||||
* locating specific hex patterns within files, and editing file contents
|
||||
* at hex offsets.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#include "hex_funcs.hpp"
|
||||
|
||||
namespace ult {
|
||||
size_t HEX_BUFFER_SIZE = 4096;//65536/4;
|
||||
|
||||
// Thread-safe cache and file operation mutexes
|
||||
std::shared_mutex cacheMutex; // Allows multiple readers, single writer
|
||||
std::mutex fileWriteMutex; // Protects file write operations
|
||||
|
||||
// For improving the speed of hexing consecutively with the same file and asciiPattern.
|
||||
std::unordered_map<std::string, std::string> hexSumCache;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Thread-safe cache management functions
|
||||
*/
|
||||
void clearHexSumCache() {
|
||||
std::lock_guard<std::shared_mutex> writeLock(cacheMutex);
|
||||
//hexSumCache.clear();
|
||||
hexSumCache = {};
|
||||
}
|
||||
|
||||
size_t getHexSumCacheSize() {
|
||||
std::shared_lock<std::shared_mutex> readLock(cacheMutex);
|
||||
return hexSumCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts an ASCII string to a hexadecimal string.
|
||||
*
|
||||
* This function takes an ASCII string as input and converts it into a hexadecimal string.
|
||||
*
|
||||
* @param asciiStr The ASCII string to convert.
|
||||
* @return The corresponding hexadecimal string.
|
||||
*/
|
||||
|
||||
|
||||
// Function to convert ASCII string to Hex string
|
||||
std::string asciiToHex(const std::string& asciiStr) {
|
||||
std::string hexStr;
|
||||
//hexStr.reserve(asciiStr.length() * 2); // Reserve space for the hexadecimal string
|
||||
|
||||
for (unsigned char c : asciiStr) {
|
||||
hexStr.push_back(hexLookup[c >> 4]); // High nibble
|
||||
hexStr.push_back(hexLookup[c & 0x0F]); // Low nibble
|
||||
}
|
||||
|
||||
return hexStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a decimal string to a fixed-width hexadecimal string.
|
||||
*
|
||||
* @param decimalStr The decimal string to convert.
|
||||
* @param byteGroupSize The number of hex digits to output (must be even for byte alignment).
|
||||
* @return Hex string of exactly 'byteGroupSize' digits, or empty string if value doesn't fit.
|
||||
*/
|
||||
std::string decimalToHex(const std::string& decimalStr, int byteGroupSize) {
|
||||
const int decimalValue = ult::stoi(decimalStr);
|
||||
if (decimalValue < 0 || byteGroupSize <= 0 || (byteGroupSize % 2) != 0) {
|
||||
// Invalid input: negative number, or byteGroupSize <= 0, or odd byteGroupSize
|
||||
return "";
|
||||
}
|
||||
|
||||
// Special case: zero
|
||||
if (decimalValue == 0) {
|
||||
return std::string(byteGroupSize, '0');
|
||||
}
|
||||
|
||||
// Convert decimalValue to hex (uppercase, minimal length)
|
||||
std::string hex;
|
||||
int tempValue = decimalValue;
|
||||
int remainder;
|
||||
char hexChar;
|
||||
while (tempValue > 0) {
|
||||
remainder = tempValue % 16;
|
||||
hexChar = (remainder < 10) ? ('0' + remainder) : ('A' + remainder - 10);
|
||||
hex.insert(hex.begin(), hexChar);
|
||||
tempValue /= 16;
|
||||
}
|
||||
|
||||
// Ensure hex length is even by adding leading zero if needed
|
||||
if (hex.length() % 2 != 0) {
|
||||
hex.insert(hex.begin(), '0');
|
||||
}
|
||||
|
||||
// Minimum size needed to fit hex string
|
||||
const size_t hexLen = hex.length();
|
||||
|
||||
// Adjust minimum byteGroupSize to be at least hexLen
|
||||
size_t minByteGroupSize = std::max(static_cast<size_t>(byteGroupSize), hexLen);
|
||||
|
||||
// If byteGroupSize was too small, adjust to hex length (must be even)
|
||||
if (minByteGroupSize % 2 != 0) {
|
||||
minByteGroupSize++;
|
||||
}
|
||||
|
||||
// If minByteGroupSize is less than hex length, number doesn't fit
|
||||
if (minByteGroupSize < hexLen) {
|
||||
return ""; // can't fit
|
||||
}
|
||||
|
||||
// Pad with leading zeros to match minByteGroupSize
|
||||
if (hexLen < minByteGroupSize) {
|
||||
hex.insert(hex.begin(), minByteGroupSize - hexLen, '0');
|
||||
}
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a hexadecimal string to a decimal string.
|
||||
*
|
||||
* This function takes a hexadecimal string as input and converts it into a decimal string.
|
||||
*
|
||||
* @param hexStr The hexadecimal string to convert.
|
||||
* @return The corresponding decimal string.
|
||||
*/
|
||||
std::string hexToDecimal(const std::string& hexStr) {
|
||||
// Convert hexadecimal string to integer
|
||||
int decimalValue = 0;
|
||||
const size_t len = hexStr.length();
|
||||
|
||||
char hexChar;
|
||||
int value;
|
||||
|
||||
// Iterate over each character in the hexadecimal string
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
hexChar = hexStr[i];
|
||||
//int value;
|
||||
|
||||
// Convert hex character to its decimal value
|
||||
if (hexChar >= '0' && hexChar <= '9') {
|
||||
value = hexChar - '0';
|
||||
} else if (hexChar >= 'A' && hexChar <= 'F') {
|
||||
value = 10 + (hexChar - 'A');
|
||||
} else if (hexChar >= 'a' && hexChar <= 'f') {
|
||||
value = 10 + (hexChar - 'a');
|
||||
} else {
|
||||
break;
|
||||
//throw std::invalid_argument("Invalid hexadecimal character");
|
||||
}
|
||||
|
||||
// Update the decimal value
|
||||
decimalValue = decimalValue * 16 + value;
|
||||
}
|
||||
|
||||
// Convert the decimal value to a string
|
||||
return ult::to_string(decimalValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string hexToReversedHex(const std::string& hexadecimal, int order) {
|
||||
// Reverse the hexadecimal string in groups of order
|
||||
std::string reversedHex;
|
||||
for (int i = hexadecimal.length() - order; i >= 0; i -= order) {
|
||||
reversedHex += hexadecimal.substr(i, order);
|
||||
}
|
||||
|
||||
return reversedHex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a decimal string to a reversed hexadecimal string.
|
||||
*
|
||||
* This function takes a decimal string as input, converts it into a hexadecimal
|
||||
* string, and reverses the resulting hexadecimal string in groups of byteGroupSize.
|
||||
*
|
||||
* @param decimalStr The decimal string to convert.
|
||||
* @param byteGroupSize The grouping byteGroupSize for reversing the hexadecimal string.
|
||||
* @return The reversed hexadecimal string.
|
||||
*/
|
||||
std::string decimalToReversedHex(const std::string& decimalStr, int byteGroupSize) {
|
||||
//std::string hexadecimal = decimalToHex(decimalStr, byteGroupSize);
|
||||
|
||||
// Reverse the hexadecimal string in groups of byteGroupSize
|
||||
//std::string reversedHex;
|
||||
//for (int i = hexadecimal.length() - byteGroupSize; i >= 0; i -= byteGroupSize) {
|
||||
// reversedHex += hexadecimal.substr(i, byteGroupSize);
|
||||
//}
|
||||
|
||||
return hexToReversedHex(decimalToHex(decimalStr, byteGroupSize));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Finds the offsets of hexadecimal data in a file.
|
||||
*
|
||||
* This function searches for occurrences of hexadecimal data in a binary file
|
||||
* and returns the file offsets where the data is found.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param hexData The hexadecimal data to search for.
|
||||
* @return A vector of strings containing the file offsets where the data is found.
|
||||
*/
|
||||
std::vector<std::string> findHexDataOffsets(const std::string& filePath, const std::string& hexData) {
|
||||
std::vector<std::string> offsets;
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "rb");
|
||||
if (!file) {
|
||||
return offsets;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
const size_t fileSize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
std::vector<unsigned char> binaryData;
|
||||
if (hexData.length() % 2 != 0) {
|
||||
fclose(file);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
||||
const size_t hexLen = hexData.length();
|
||||
binaryData.resize(hexLen / 2);
|
||||
const unsigned char* hexPtr = reinterpret_cast<const unsigned char*>(hexData.c_str());
|
||||
|
||||
// Unrolled hex conversion loop
|
||||
size_t i = 0;
|
||||
for (; i + 4 <= hexLen; i += 4) {
|
||||
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
|
||||
binaryData[i/2 + 1] = (hexTable[hexPtr[i + 2]] << 4) | hexTable[hexPtr[i + 3]];
|
||||
}
|
||||
// Handle remaining bytes
|
||||
for (; i < hexLen; i += 2) {
|
||||
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
|
||||
}
|
||||
|
||||
// Optimized search variables
|
||||
const unsigned char* patternPtr = binaryData.data();
|
||||
const size_t patternLen = binaryData.size();
|
||||
const unsigned char firstByte = patternPtr[0];
|
||||
|
||||
std::vector<unsigned char> buffer(HEX_BUFFER_SIZE);
|
||||
size_t bytesRead = 0;
|
||||
size_t offset = 0;
|
||||
|
||||
|
||||
while ((bytesRead = fread(buffer.data(), 1, HEX_BUFFER_SIZE, file)) > 0) {
|
||||
const unsigned char* bufPtr = buffer.data();
|
||||
|
||||
// Optimized search with first-byte filtering and loop unrolling
|
||||
i = 0;
|
||||
const size_t searchEnd = bytesRead;
|
||||
|
||||
// Process 4 bytes at a time for better cache usage
|
||||
for (; i + 4 <= searchEnd; i += 4) {
|
||||
// Check 4 positions at once
|
||||
if (bufPtr[i] == firstByte) {
|
||||
if (offset + i + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i));
|
||||
}
|
||||
}
|
||||
if (bufPtr[i + 1] == firstByte) {
|
||||
if (offset + i + 1 + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i + 1, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i + 1));
|
||||
}
|
||||
}
|
||||
if (bufPtr[i + 2] == firstByte) {
|
||||
if (offset + i + 2 + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i + 2, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i + 2));
|
||||
}
|
||||
}
|
||||
if (bufPtr[i + 3] == firstByte) {
|
||||
if (offset + i + 3 + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i + 3, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i + 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle remaining bytes
|
||||
for (; i < searchEnd; ++i) {
|
||||
if (bufPtr[i] == firstByte) {
|
||||
if (offset + i + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += bytesRead;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
#else
|
||||
std::ifstream file(filePath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return offsets;
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
const size_t fileSize = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<unsigned char> binaryData;
|
||||
if (hexData.length() % 2 != 0) {
|
||||
file.close();
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
||||
const size_t hexLen = hexData.length();
|
||||
binaryData.resize(hexLen / 2);
|
||||
const unsigned char* hexPtr = reinterpret_cast<const unsigned char*>(hexData.c_str());
|
||||
|
||||
size_t i = 0;
|
||||
for (; i + 4 <= hexLen; i += 4) {
|
||||
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
|
||||
binaryData[i/2 + 1] = (hexTable[hexPtr[i + 2]] << 4) | hexTable[hexPtr[i + 3]];
|
||||
}
|
||||
for (; i < hexLen; i += 2) {
|
||||
binaryData[i/2] = (hexTable[hexPtr[i]] << 4) | hexTable[hexPtr[i + 1]];
|
||||
}
|
||||
|
||||
const unsigned char* patternPtr = binaryData.data();
|
||||
const size_t patternLen = binaryData.size();
|
||||
const unsigned char firstByte = patternPtr[0];
|
||||
|
||||
std::vector<unsigned char> buffer(HEX_BUFFER_SIZE);
|
||||
size_t bytesRead = 0;
|
||||
size_t offset = 0;
|
||||
|
||||
while (file.read(reinterpret_cast<char*>(buffer.data()), HEX_BUFFER_SIZE) || file.gcount() > 0) {
|
||||
bytesRead = file.gcount();
|
||||
const unsigned char* bufPtr = buffer.data();
|
||||
|
||||
// Same optimized search as FILE* version
|
||||
i = 0;
|
||||
const size_t searchEnd = bytesRead;
|
||||
|
||||
for (; i + 4 <= searchEnd; i += 4) {
|
||||
if (bufPtr[i] == firstByte) {
|
||||
if (offset + i + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i));
|
||||
}
|
||||
}
|
||||
if (bufPtr[i + 1] == firstByte) {
|
||||
if (offset + i + 1 + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i + 1, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i + 1));
|
||||
}
|
||||
}
|
||||
if (bufPtr[i + 2] == firstByte) {
|
||||
if (offset + i + 2 + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i + 2, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i + 2));
|
||||
}
|
||||
}
|
||||
if (bufPtr[i + 3] == firstByte) {
|
||||
if (offset + i + 3 + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i + 3, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i + 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < searchEnd; ++i) {
|
||||
if (bufPtr[i] == firstByte) {
|
||||
if (offset + i + patternLen <= fileSize &&
|
||||
memcmp(bufPtr + i, patternPtr, patternLen) == 0) {
|
||||
offsets.emplace_back(ult::to_string(offset + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += bytesRead;
|
||||
}
|
||||
|
||||
file.close();
|
||||
#endif
|
||||
|
||||
return offsets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Edits hexadecimal data in a file at a specified offset.
|
||||
*
|
||||
* This function opens a binary file, seeks to a specified offset, and replaces
|
||||
* the data at that offset with the provided hexadecimal data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param offsetStr The offset in the file to perform the edit.
|
||||
* @param hexData The hexadecimal data to replace at the offset.
|
||||
*/
|
||||
void hexEditByOffset(const std::string& filePath, const std::string& offsetStr, const std::string& hexData) {
|
||||
// Lock file writes to prevent concurrent modifications to the same file
|
||||
std::lock_guard<std::mutex> fileWriteLock(fileWriteMutex);
|
||||
|
||||
const std::streampos offset = std::stoll(offsetStr);
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
// Open the file for both reading and writing in binary mode
|
||||
FILE* file = fopen(filePath.c_str(), "rb+");
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to open the file.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the file size
|
||||
fseek(file, 0, SEEK_END);
|
||||
const std::streampos fileSize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
if (offset >= fileSize) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Offset exceeds file size.");
|
||||
#endif
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the hex string to binary data
|
||||
std::vector<unsigned char> binaryData(hexData.length() / 2);
|
||||
std::string byteString;
|
||||
for (size_t i = 0, j = 0; i < hexData.length(); i += 2, ++j) {
|
||||
byteString = hexData.substr(i, 2);
|
||||
binaryData[j] = static_cast<unsigned char>(ult::stoi(byteString, nullptr, 16));
|
||||
}
|
||||
|
||||
// Move to the specified offset and write the binary data directly to the file
|
||||
fseek(file, offset, SEEK_SET);
|
||||
const size_t bytesWritten = fwrite(binaryData.data(), sizeof(unsigned char), binaryData.size(), file);
|
||||
if (bytesWritten != binaryData.size()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to write data to the file.");
|
||||
#endif
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
#else
|
||||
// Open the file for both reading and writing in binary mode
|
||||
std::fstream file(filePath, std::ios::binary | std::ios::in | std::ios::out);
|
||||
if (!file.is_open()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to open the file.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the file size
|
||||
file.seekg(0, std::ios::end);
|
||||
const std::streampos fileSize = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
if (offset >= fileSize) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Offset exceeds file size.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the hex string to binary data
|
||||
std::vector<unsigned char> binaryData(hexData.length() / 2);
|
||||
std::string byteString;
|
||||
for (size_t i = 0, j = 0; i < hexData.length(); i += 2, ++j) {
|
||||
byteString = hexData.substr(i, 2);
|
||||
binaryData[j] = static_cast<unsigned char>(ult::stoi(byteString, nullptr, 16));
|
||||
}
|
||||
|
||||
// Move to the specified offset and write the binary data directly to the file
|
||||
file.seekp(offset);
|
||||
file.write(reinterpret_cast<const char*>(binaryData.data()), binaryData.size());
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to write data to the file.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
file.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Edits a specific offset in a file with custom hexadecimal data.
|
||||
*
|
||||
* This function searches for a custom pattern in the file and calculates a new offset
|
||||
* based on user-provided offsetStr and the found pattern. It then replaces the data
|
||||
* at the calculated offset with the provided hexadecimal data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param offsetStr The user-provided offset for the edit.
|
||||
* @param customPattern The custom pattern to search for in the file.
|
||||
* @param hexDataReplacement The hexadecimal data to replace at the calculated offset.
|
||||
* @param occurrence The occurrence/index of the data to replace (default is "0" to replace all occurrences).
|
||||
*/
|
||||
void hexEditByCustomOffset(const std::string& filePath, const std::string& customAsciiPattern, const std::string& offsetStr, const std::string& hexDataReplacement, size_t occurrence) {
|
||||
|
||||
// Create a cache key based on filePath and customAsciiPattern
|
||||
const std::string cacheKey = filePath + '?' + customAsciiPattern + '?' + ult::to_string(occurrence);
|
||||
|
||||
int hexSum = -1;
|
||||
|
||||
// Thread-safe cache access
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> readLock(cacheMutex);
|
||||
const auto cachedResult = hexSumCache.find(cacheKey);
|
||||
if (cachedResult != hexSumCache.end()) {
|
||||
hexSum = ult::stoi(cachedResult->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (hexSum == -1) {
|
||||
std::string customHexPattern;
|
||||
if (customAsciiPattern[0] == '#') {
|
||||
// remove #
|
||||
customHexPattern = customAsciiPattern.substr(1);
|
||||
} else {
|
||||
// Convert custom ASCII pattern to a custom hex pattern
|
||||
customHexPattern = asciiToHex(customAsciiPattern);
|
||||
}
|
||||
|
||||
|
||||
// Find hex data offsets in the file
|
||||
const std::vector<std::string> offsets = findHexDataOffsets(filePath, customHexPattern);
|
||||
|
||||
if (!offsets.empty()) {
|
||||
hexSum = ult::stoi(offsets[occurrence]);
|
||||
|
||||
// Thread-safe cache write
|
||||
{
|
||||
std::lock_guard<std::shared_mutex> writeLock(cacheMutex);
|
||||
hexSumCache[cacheKey] = ult::to_string(hexSum);
|
||||
}
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Offset not found.");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (hexSum != -1) {
|
||||
// Calculate the total offset to seek in the file
|
||||
//int sum = hexSum + ult::stoi(offsetStr);
|
||||
hexEditByOffset(filePath, ult::to_string(hexSum + ult::stoi(offsetStr)), hexDataReplacement);
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to find " + customAsciiPattern + ".");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds and replaces hexadecimal data in a file.
|
||||
*
|
||||
* This function searches for occurrences of hexadecimal data in a binary file
|
||||
* and replaces them with a specified hexadecimal replacement data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param hexDataToReplace The hexadecimal data to search for and replace.
|
||||
* @param hexDataReplacement The hexadecimal data to replace with.
|
||||
* @param occurrence The occurrence/index of the data to replace (default is "0" to replace all occurrences).
|
||||
*/
|
||||
void hexEditFindReplace(const std::string& filePath, const std::string& hexDataToReplace, const std::string& hexDataReplacement, size_t occurrence) {
|
||||
const std::vector<std::string> offsetStrs = findHexDataOffsets(filePath, hexDataToReplace);
|
||||
if (!offsetStrs.empty()) {
|
||||
if (occurrence == 0) {
|
||||
// Replace all occurrences
|
||||
for (const std::string& offsetStr : offsetStrs) {
|
||||
//logMessage("offsetStr: "+offsetStr);
|
||||
//logMessage("hexDataReplacement: "+hexDataReplacement);
|
||||
hexEditByOffset(filePath, offsetStr, hexDataReplacement);
|
||||
}
|
||||
} else {
|
||||
// Convert the occurrence string to an integer
|
||||
if (occurrence > 0 && occurrence <= offsetStrs.size()) {
|
||||
// Replace the specified occurrence/index
|
||||
//std::string offsetStr = offsetStrs[occurrence - 1];
|
||||
//logMessage("offsetStr: "+offsetStr);
|
||||
//logMessage("hexDataReplacement: "+hexDataReplacement);
|
||||
hexEditByOffset(filePath, offsetStrs[occurrence - 1], hexDataReplacement);
|
||||
} else {
|
||||
// Invalid occurrence/index specified
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Invalid hex occurrence/index specified.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//std::cout << "Hex data replaced successfully." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds and replaces hexadecimal data in a file.
|
||||
*
|
||||
* This function searches for occurrences of hexadecimal data in a binary file
|
||||
* and replaces them with a specified hexadecimal replacement data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param hexDataToReplace The hexadecimal data to search for and replace.
|
||||
* @param hexDataReplacement The hexadecimal data to replace with.
|
||||
* @param occurrence The occurrence/index of the data to replace (default is "0" to replace all occurrences).
|
||||
*/
|
||||
std::string parseHexDataAtCustomOffset(const std::string& filePath, const std::string& customAsciiPattern,
|
||||
const std::string& offsetStr, size_t length, size_t occurrence) {
|
||||
const std::string cacheKey = filePath + '?' + customAsciiPattern + '?' + ult::to_string(occurrence);
|
||||
int hexSum = -1;
|
||||
|
||||
// Thread-safe cache read
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> readLock(cacheMutex);
|
||||
const auto cachedResult = hexSumCache.find(cacheKey);
|
||||
if (cachedResult != hexSumCache.end()) {
|
||||
hexSum = ult::stoi(cachedResult->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (hexSum == -1) {
|
||||
const std::string customHexPattern = asciiToHex(customAsciiPattern);
|
||||
const std::vector<std::string> offsets = findHexDataOffsets(filePath, customHexPattern);
|
||||
|
||||
if (!offsets.empty() && offsets.size() > occurrence) {
|
||||
hexSum = ult::stoi(offsets[occurrence]);
|
||||
|
||||
// Thread-safe cache write
|
||||
{
|
||||
std::lock_guard<std::shared_mutex> writeLock(cacheMutex);
|
||||
hexSumCache[cacheKey] = ult::to_string(hexSum);
|
||||
}
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Offset not found.");
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const std::streampos totalOffset = hexSum + std::stoll(offsetStr);
|
||||
std::vector<char> hexBuffer(length);
|
||||
std::vector<char> hexStream(length * 2);
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "rb");
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to open the file.");
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
if (fseek(file, totalOffset, SEEK_SET) != 0) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error seeking to offset.");
|
||||
#endif
|
||||
fclose(file);
|
||||
return "";
|
||||
}
|
||||
|
||||
const size_t bytesRead = fread(hexBuffer.data(), sizeof(char), length, file);
|
||||
if (bytesRead == length) {
|
||||
static constexpr char hexDigits[] = "0123456789ABCDEF";
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
hexStream[i * 2] = hexDigits[(hexBuffer[i] >> 4) & 0xF];
|
||||
hexStream[i * 2 + 1] = hexDigits[hexBuffer[i] & 0xF];
|
||||
}
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error reading data from file or end of file reached.");
|
||||
#endif
|
||||
fclose(file);
|
||||
return "";
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
#else
|
||||
std::ifstream file(filePath, std::ios::binary);
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to open the file.");
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
file.seekg(totalOffset);
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error seeking to offset.");
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
file.read(hexBuffer.data(), length);
|
||||
if (file.gcount() == static_cast<std::streamsize>(length)) {
|
||||
static constexpr char hexDigits[] = "0123456789ABCDEF";
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
hexStream[i * 2] = hexDigits[(hexBuffer[i] >> 4) & 0xF];
|
||||
hexStream[i * 2 + 1] = hexDigits[hexBuffer[i] & 0xF];
|
||||
}
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error reading data from file or end of file reached.");
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
file.close();
|
||||
#endif
|
||||
|
||||
std::string result(hexStream.begin(), hexStream.end());
|
||||
result = stringToUppercase(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Finds and replaces hexadecimal data in a file.
|
||||
*
|
||||
* This function searches for occurrences of hexadecimal data in a binary file
|
||||
* and replaces them with a specified hexadecimal replacement data.
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @param hexDataToReplace The hexadecimal data to search for and replace.
|
||||
* @param hexDataReplacement The hexadecimal data to replace with.
|
||||
* @param occurrence The occurrence/index of the data to replace (default is "0" to replace all occurrences).
|
||||
*/
|
||||
|
||||
std::string replaceHexPlaceholder(const std::string& arg, const std::string& hexPath) {
|
||||
std::string replacement = arg;
|
||||
const std::string searchString = "{hex_file(";
|
||||
|
||||
const size_t startPos = replacement.find(searchString);
|
||||
const size_t endPos = replacement.find(")}");
|
||||
|
||||
if (startPos != std::string::npos && endPos != std::string::npos && endPos > startPos) {
|
||||
const std::string placeholderContent = replacement.substr(startPos + searchString.length(), endPos - startPos - searchString.length());
|
||||
|
||||
// Split the placeholder content into its components (customAsciiPattern, offsetStr, length)
|
||||
std::vector<std::string> components;
|
||||
|
||||
// Use StringStream instead of std::istringstream
|
||||
StringStream componentStream(placeholderContent);
|
||||
std::string component;
|
||||
|
||||
while (componentStream.getline(component, ',')) {
|
||||
trim(component);
|
||||
components.push_back(component);
|
||||
}
|
||||
|
||||
if (components.size() == 3) {
|
||||
// Extract individual components
|
||||
const std::string customAsciiPattern = components[0];
|
||||
const std::string offsetStr = components[1];
|
||||
const size_t length = std::stoul(components[2]);
|
||||
|
||||
// Call the parsing function and replace the placeholder
|
||||
const std::string parsedResult = parseHexDataAtCustomOffset(hexPath, customAsciiPattern, offsetStr, length);
|
||||
|
||||
// Only replace if parsedResult returns a non-empty string
|
||||
if (!parsedResult.empty()) {
|
||||
// Replace the entire placeholder with the parsed result
|
||||
replacement.replace(startPos, endPos - startPos + searchString.length() + 2, parsedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return replacement;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the version string from a binary file.
|
||||
*
|
||||
* This function reads a binary file and searches for a version pattern
|
||||
* in the format "v#.#.#" (e.g., "v1.2.3").
|
||||
*
|
||||
* @param filePath The path to the binary file.
|
||||
* @return The version string if found; otherwise, an empty string.
|
||||
*/
|
||||
std::string extractVersionFromBinary(const std::string &filePath) {
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
// Step 1: Open the binary file
|
||||
FILE* file = fopen(filePath.c_str(), "rb");
|
||||
if (!file) {
|
||||
return ""; // Return empty string if file cannot be opened
|
||||
}
|
||||
|
||||
// Get the file size
|
||||
fseek(file, 0, SEEK_END);
|
||||
const std::streamsize size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
// Read the entire file into a buffer
|
||||
std::vector<uint8_t> buffer(size);
|
||||
const size_t bytesRead = fread(buffer.data(), sizeof(uint8_t), size, file);
|
||||
fclose(file); // Close the file after reading
|
||||
|
||||
if (bytesRead != static_cast<size_t>(size)) {
|
||||
return ""; // Return empty string if reading fails
|
||||
}
|
||||
#else
|
||||
// Step 1: Read the entire binary file into a vector
|
||||
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
return ""; // Return empty string if file cannot be opened
|
||||
}
|
||||
|
||||
const std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<uint8_t> buffer(size);
|
||||
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
|
||||
return ""; // Return empty string if reading fails
|
||||
}
|
||||
#endif
|
||||
|
||||
// Step 2: Search for the pattern "v#.#.#"
|
||||
const char* data = reinterpret_cast<const char*>(buffer.data());
|
||||
for (std::streamsize i = 0; i < size; ++i) {
|
||||
if (data[i] == 'v' && i + 5 < size &&
|
||||
std::isdigit(data[i + 1]) && data[i + 2] == '.' &&
|
||||
std::isdigit(data[i + 3]) && data[i + 4] == '.' &&
|
||||
std::isdigit(data[i + 5])) {
|
||||
|
||||
// Extract the version string
|
||||
return std::string(data + i, 6); // Return the version string
|
||||
}
|
||||
}
|
||||
|
||||
return ""; // Return empty string if no match is found
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,400 @@
|
||||
/********************************************************************************
|
||||
* File: json_funcs.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file provides functions for working with JSON files in C++ using
|
||||
* the `cJSON` library. It includes a function to read JSON data from a file.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#include "json_funcs.hpp"
|
||||
|
||||
|
||||
namespace ult {
|
||||
|
||||
static std::mutex json_access_mutex;
|
||||
|
||||
/**
|
||||
* @brief Reads JSON data from a file and returns it as a `json_t` object.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @return A `json_t` object representing the parsed JSON data. Returns `nullptr` on error.
|
||||
*/
|
||||
json_t* readJsonFromFile(const std::string& filePath) {
|
||||
std::lock_guard<std::mutex> lock(json_access_mutex);
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "rb");
|
||||
if (!file) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get file size
|
||||
fseek(file, 0, SEEK_END);
|
||||
const long fileSize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
// Check for reasonable file size
|
||||
if (fileSize <= 0 || fileSize > 6 * 1024 * 1024) { // 6MB limit
|
||||
fclose(file);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Use vector<char> for better performance and explicit null termination
|
||||
std::vector<char> buffer;
|
||||
buffer.resize(static_cast<size_t>(fileSize) + 1); // +1 for null terminator
|
||||
|
||||
// Read the file in one operation
|
||||
const size_t bytesRead = fread(buffer.data(), 1, static_cast<size_t>(fileSize), file);
|
||||
fclose(file);
|
||||
|
||||
if (bytesRead != static_cast<size_t>(fileSize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure null termination for cJSON
|
||||
buffer[bytesRead] = '\0';
|
||||
|
||||
#else
|
||||
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get file size from current position (end)
|
||||
const std::streampos fileSize = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
// Check for reasonable file size
|
||||
if (fileSize <= 0 || fileSize > 6 * 1024 * 1024) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Use vector<char> for better performance and explicit null termination
|
||||
std::vector<char> buffer;
|
||||
buffer.resize(static_cast<size_t>(fileSize) + 1); // +1 for null terminator
|
||||
|
||||
file.read(buffer.data(), static_cast<std::streamsize>(fileSize));
|
||||
|
||||
// Check how much was actually read
|
||||
std::streamsize actualRead = file.gcount();
|
||||
if (actualRead != fileSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure null termination for cJSON
|
||||
buffer[actualRead] = '\0';
|
||||
file.close();
|
||||
#endif
|
||||
|
||||
// Parse the JSON content - pass buffer directly to avoid string copy
|
||||
cJSON* root = cJSON_Parse(buffer.data());
|
||||
if (!root) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
const char* error_ptr = cJSON_GetErrorPtr();
|
||||
if (error_ptr) {
|
||||
if (!disableLogging)
|
||||
logMessage("JSON parsing error: " + std::string(error_ptr));
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<json_t*>(root);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parses a JSON string into a json_t object.
|
||||
*
|
||||
* This function takes a JSON string as input and parses it into a json_t object using cJSON library's `cJSON_Parse` function.
|
||||
* If parsing fails, it logs the error and returns nullptr.
|
||||
*
|
||||
* @param input The input JSON string to parse.
|
||||
* @return A json_t object representing the parsed JSON, or nullptr if parsing fails.
|
||||
*/
|
||||
json_t* stringToJson(const std::string& input) {
|
||||
cJSON* jsonObj = cJSON_Parse(input.c_str());
|
||||
|
||||
if (!jsonObj) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
const char* error_ptr = cJSON_GetErrorPtr();
|
||||
if (error_ptr != nullptr) {
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to parse JSON: " + std::string(error_ptr));
|
||||
}
|
||||
#endif
|
||||
return nullptr; // Return nullptr to indicate failure clearly
|
||||
}
|
||||
|
||||
return reinterpret_cast<json_t*>(jsonObj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Function to get a string from a JSON object
|
||||
std::string getStringFromJson(const json_t* root, const char* key) {
|
||||
const cJSON* croot = reinterpret_cast<const cJSON*>(root);
|
||||
const cJSON* value = cJSON_GetObjectItemCaseSensitive(croot, key);
|
||||
if (value && cJSON_IsString(value) && value->valuestring) {
|
||||
return value->valuestring;
|
||||
} else {
|
||||
return ""; // Key not found or not a string, return empty string/char*
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Loads a JSON file from the specified path and retrieves a string value for a given key.
|
||||
*
|
||||
* This function combines the functionality of loading a JSON file and retrieving a string value associated
|
||||
* with a given key in a single step.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param key The key whose associated string value is to be retrieved.
|
||||
* @return A string containing the value associated with the given key, or an empty string if the key is not found.
|
||||
*/
|
||||
std::string getStringFromJsonFile(const std::string& filePath, const std::string& key) {
|
||||
// Load JSON from file using a smart pointer
|
||||
std::unique_ptr<json_t, JsonDeleter> root(readJsonFromFile(filePath), JsonDeleter());
|
||||
if (!root) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Failed to load JSON file from path: " + filePath);
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
// Retrieve the string value associated with the key
|
||||
cJSON* croot = reinterpret_cast<cJSON*>(root.get());
|
||||
cJSON* jsonKey = cJSON_GetObjectItemCaseSensitive(croot, key.c_str());
|
||||
const char* value = (cJSON_IsString(jsonKey) && jsonKey->valuestring) ? jsonKey->valuestring : nullptr;
|
||||
|
||||
// Check if the value was found and return it
|
||||
if (value) {
|
||||
return std::string(value);
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Key not found or not a string in JSON: " + key);
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sets a value in a JSON file, creating the file if it doesn't exist.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param key The key to set.
|
||||
* @param value The value to set (auto-detected type).
|
||||
* @param createIfNotExists Whether to create the file if it doesn't exist.
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
bool setJsonValue(const std::string& filePath, const std::string& key, const std::string& value, bool createIfNotExists) {
|
||||
// Try to load existing file
|
||||
std::unique_ptr<json_t, JsonDeleter> root(readJsonFromFile(filePath), JsonDeleter());
|
||||
|
||||
cJSON* croot = nullptr;
|
||||
|
||||
// If file doesn't exist, create new JSON object if allowed
|
||||
if (!root) {
|
||||
if (!createIfNotExists) {
|
||||
return false;
|
||||
}
|
||||
croot = cJSON_CreateObject();
|
||||
if (!croot) {
|
||||
return false;
|
||||
}
|
||||
root.reset(reinterpret_cast<json_t*>(croot));
|
||||
} else {
|
||||
croot = reinterpret_cast<cJSON*>(root.get());
|
||||
}
|
||||
|
||||
// FIXED: Better value type detection
|
||||
cJSON* jsonValue = nullptr;
|
||||
|
||||
// Trim whitespace first
|
||||
std::string trimmedValue = value;
|
||||
// Remove leading whitespace
|
||||
trimmedValue.erase(0, trimmedValue.find_first_not_of(" \t\n\r"));
|
||||
// Remove trailing whitespace
|
||||
trimmedValue.erase(trimmedValue.find_last_not_of(" \t\n\r") + 1);
|
||||
|
||||
if (trimmedValue.empty()) {
|
||||
jsonValue = cJSON_CreateString("");
|
||||
} else if (trimmedValue == "true") {
|
||||
jsonValue = cJSON_CreateBool(1);
|
||||
} else if (trimmedValue == "false") {
|
||||
jsonValue = cJSON_CreateBool(0);
|
||||
} else if (trimmedValue == "null") {
|
||||
jsonValue = cJSON_CreateNull();
|
||||
} else {
|
||||
// Try parsing as number (integer or float)
|
||||
char* endPtr = nullptr;
|
||||
errno = 0;
|
||||
|
||||
// Try as integer first
|
||||
const long longValue = std::strtol(trimmedValue.c_str(), &endPtr, 10);
|
||||
if (endPtr == trimmedValue.c_str() + trimmedValue.length() && errno == 0) {
|
||||
// Successfully parsed as integer
|
||||
jsonValue = cJSON_CreateNumber(static_cast<double>(longValue));
|
||||
} else {
|
||||
// Try as float
|
||||
endPtr = nullptr;
|
||||
errno = 0;
|
||||
const double doubleValue = std::strtod(trimmedValue.c_str(), &endPtr);
|
||||
if (endPtr == trimmedValue.c_str() + trimmedValue.length() && errno == 0) {
|
||||
// Successfully parsed as float
|
||||
jsonValue = cJSON_CreateNumber(doubleValue);
|
||||
} else {
|
||||
// Treat as string
|
||||
jsonValue = cJSON_CreateString(trimmedValue.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!jsonValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete existing item if it exists
|
||||
cJSON_DeleteItemFromObject(croot, key.c_str());
|
||||
|
||||
// Add the new value
|
||||
cJSON_AddItemToObject(croot, key.c_str(), jsonValue);
|
||||
|
||||
// Save to file with formatted output for better readability
|
||||
char* jsonString = cJSON_Print(croot); // Use formatted output instead of PrintUnformatted
|
||||
if (!jsonString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(json_access_mutex);
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "w"); // Use text mode for JSON
|
||||
if (file) {
|
||||
const size_t jsonLength = std::strlen(jsonString);
|
||||
const size_t bytesWritten = fwrite(jsonString, 1, jsonLength, file);
|
||||
success = (bytesWritten == jsonLength);
|
||||
fclose(file);
|
||||
}
|
||||
#else
|
||||
std::ofstream file(filePath); // Use text mode for JSON
|
||||
if (file.is_open()) {
|
||||
file << jsonString;
|
||||
success = !file.fail();
|
||||
file.close();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
cJSON_free(jsonString);
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Renames a key in a JSON file.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param oldKey The current key name.
|
||||
* @param newKey The new key name.
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
bool renameJsonKey(const std::string& filePath, const std::string& oldKey, const std::string& newKey) {
|
||||
// Try to load existing file
|
||||
std::unique_ptr<json_t, JsonDeleter> root(readJsonFromFile(filePath), JsonDeleter());
|
||||
|
||||
if (!root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cJSON* croot = reinterpret_cast<cJSON*>(root.get());
|
||||
|
||||
// Check if old key exists
|
||||
cJSON* value = cJSON_GetObjectItemCaseSensitive(croot, oldKey.c_str());
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detach the value from the object
|
||||
cJSON_DetachItemFromObject(croot, oldKey.c_str());
|
||||
|
||||
// Add it back with the new key
|
||||
cJSON_AddItemToObject(croot, newKey.c_str(), value);
|
||||
|
||||
// Save to file
|
||||
char* jsonString = cJSON_Print(croot); // Use formatted output
|
||||
if (!jsonString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(json_access_mutex);
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "w"); // Use text mode
|
||||
if (file) {
|
||||
const size_t jsonLength = std::strlen(jsonString);
|
||||
const size_t bytesWritten = fwrite(jsonString, 1, jsonLength, file);
|
||||
success = (bytesWritten == jsonLength);
|
||||
fclose(file);
|
||||
}
|
||||
#else
|
||||
std::ofstream file(filePath); // Use text mode
|
||||
if (file.is_open()) {
|
||||
file << jsonString;
|
||||
success = !file.fail();
|
||||
file.close();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
cJSON_free(jsonString);
|
||||
return success;
|
||||
}
|
||||
|
||||
void pushNotificationJson(const std::string& text, size_t fontSize) {
|
||||
u64 tick = armGetSystemTick();
|
||||
std::string filename = "20-" + std::to_string(tick) + ".notify"; // priority 20 default
|
||||
|
||||
// Build full path
|
||||
std::string fullPath = NOTIFICATIONS_PATH + filename;
|
||||
|
||||
// Create JSON object
|
||||
cJSON* notif = cJSON_CreateObject();
|
||||
if (!notif) return;
|
||||
|
||||
cJSON_AddStringToObject(notif, "text", text.c_str());
|
||||
cJSON_AddNumberToObject(notif, "font_size", static_cast<double>(fontSize));
|
||||
//cJSON_AddNumberToObject(notif, "arrival_ns", static_cast<double>(armTicksToNs(tick)));
|
||||
|
||||
// Serialize JSON
|
||||
char* rendered = cJSON_PrintUnformatted(notif);
|
||||
if (!rendered) {
|
||||
cJSON_Delete(notif);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write to file (C-style)
|
||||
FILE* file = fopen(fullPath.c_str(), "wb");
|
||||
if (file) {
|
||||
fwrite(rendered, 1, strlen(rendered), file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
cJSON_free(rendered);
|
||||
cJSON_Delete(notif);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
/********************************************************************************
|
||||
* File: list_funcs.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file contains function declarations and utility functions related
|
||||
* to working with lists and vectors of strings. These functions are used in the
|
||||
* Ultrahand Overlay project to perform various operations on lists, such as
|
||||
* removing entries, filtering, and more.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#include <list_funcs.hpp>
|
||||
#include <mutex>
|
||||
|
||||
namespace ult {
|
||||
// Thread-safe file access mutex
|
||||
static std::mutex file_access_mutex;
|
||||
|
||||
std::vector<std::string> splitIniList(const std::string& value) {
|
||||
std::vector<std::string> result;
|
||||
std::string trimmed = value;
|
||||
trim(trimmed);
|
||||
if (trimmed.size() > 2 && trimmed.front() == '(' && trimmed.back() == ')') {
|
||||
trimmed = trimmed.substr(1, trimmed.size() - 2);
|
||||
ult::StringStream ss(trimmed);
|
||||
std::string token;
|
||||
while (ss.getline(token, ',')) {
|
||||
trim(token);
|
||||
result.push_back(token);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string joinIniList(const std::vector<std::string>& list) {
|
||||
std::string result = "";
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
result += list[i];
|
||||
if (i + 1 < list.size()) {
|
||||
result += ", ";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes entries from a vector of strings that match a specified entry.
|
||||
*
|
||||
* This function removes entries from the `itemsList` vector of strings that match the `entry`.
|
||||
*
|
||||
* @param entry The entry to be compared against the elements in `itemsList`.
|
||||
* @param itemsList The vector of strings from which matching entries will be removed.
|
||||
*/
|
||||
void removeEntryFromList(const std::string& entry, std::vector<std::string>& itemsList) {
|
||||
itemsList.erase(std::remove_if(itemsList.begin(), itemsList.end(), [&](const std::string& item) {
|
||||
return item.compare(0, entry.length(), entry) == 0;
|
||||
}), itemsList.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Filters a list of strings based on a specified filter list.
|
||||
*
|
||||
* This function filters a list of strings (`itemsList`) by removing entries that match any
|
||||
* of the criteria specified in the `filterList`. It uses the `removeEntryFromList` function
|
||||
* to perform the removal.
|
||||
*
|
||||
* @param filterList The list of entries to filter by. Entries in `itemsList` matching any entry in this list will be removed.
|
||||
* @param itemsList The list of strings to be filtered.
|
||||
*/
|
||||
void filterItemsList(const std::vector<std::string>& filterList, std::vector<std::string>& itemsList) {
|
||||
for (const auto& entry : filterList) {
|
||||
removeEntryFromList(entry, itemsList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function to read file into a vector of strings with optional cap
|
||||
std::vector<std::string> readListFromFile(const std::string& filePath, size_t maxLines) {
|
||||
std::lock_guard<std::mutex> lock(file_access_mutex);
|
||||
std::vector<std::string> lines;
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "r");
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Unable to open file: " + filePath);
|
||||
#endif
|
||||
return lines;
|
||||
}
|
||||
|
||||
static constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE];
|
||||
size_t len;
|
||||
|
||||
while (fgets(buffer, BUFFER_SIZE, file)) {
|
||||
// Check cap before processing
|
||||
if (maxLines > 0 && lines.size() >= maxLines) {
|
||||
break;
|
||||
}
|
||||
|
||||
// More efficient newline removal
|
||||
len = strlen(buffer);
|
||||
if (len > 0 && buffer[len - 1] == '\n') {
|
||||
buffer[len - 1] = '\0';
|
||||
--len;
|
||||
// Also remove carriage return if present
|
||||
if (len > 0 && buffer[len - 1] == '\r') {
|
||||
buffer[len - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
lines.emplace_back(buffer);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
#else
|
||||
std::ifstream file(filePath);
|
||||
if (!file.is_open()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Unable to open file: " + filePath);
|
||||
#endif
|
||||
return lines;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
// Check cap before adding
|
||||
if (maxLines > 0 && lines.size() >= maxLines) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove carriage return if present (getline removes \n but not \r)
|
||||
if (!line.empty() && line.back() == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
lines.emplace_back(std::move(line));
|
||||
}
|
||||
|
||||
file.close();
|
||||
#endif
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
// Function to get an entry from the list based on the index
|
||||
std::string getEntryFromListFile(const std::string& listPath, size_t listIndex) {
|
||||
std::lock_guard<std::mutex> lock(file_access_mutex);
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(listPath.c_str(), "r");
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Unable to open file: " + listPath);
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
static constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
// Skip lines until reaching the desired index
|
||||
for (size_t i = 0; i < listIndex; ++i) {
|
||||
if (!fgets(buffer, BUFFER_SIZE, file)) {
|
||||
fclose(file);
|
||||
return ""; // Index out of bounds
|
||||
}
|
||||
}
|
||||
|
||||
// Read the target line
|
||||
if (!fgets(buffer, BUFFER_SIZE, file)) {
|
||||
fclose(file);
|
||||
return ""; // Index out of bounds
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
// Efficiently remove newline character
|
||||
const size_t len = strlen(buffer);
|
||||
if (len > 0 && buffer[len - 1] == '\n') {
|
||||
buffer[len - 1] = '\0';
|
||||
if (len > 1 && buffer[len - 2] == '\r') {
|
||||
buffer[len - 2] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return std::string(buffer);
|
||||
|
||||
#else
|
||||
std::ifstream file(listPath);
|
||||
if (!file.is_open()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Unable to open file: " + listPath);
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string line;
|
||||
|
||||
// Skip lines until reaching the desired index
|
||||
for (size_t i = 0; i < listIndex; ++i) {
|
||||
if (!std::getline(file, line)) {
|
||||
return ""; // Index out of bounds
|
||||
}
|
||||
}
|
||||
|
||||
// Read the target line
|
||||
if (!std::getline(file, line)) {
|
||||
return ""; // Index out of bounds
|
||||
}
|
||||
|
||||
file.close();
|
||||
return line;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Splits a string into a vector of strings using a delimiter.
|
||||
*
|
||||
* This function splits the input string into multiple strings using the specified delimiter.
|
||||
*
|
||||
* @param str The input string to split.
|
||||
* @return A vector of strings containing the split values.
|
||||
*/
|
||||
std::vector<std::string> stringToList(const std::string& str) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
if (str.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if the input string starts and ends with '(' and ')' or '[' and ']'
|
||||
if ((str.front() == '(' && str.back() == ')') || (str.front() == '[' && str.back() == ']')) {
|
||||
// Work directly with the original string using indices instead of creating substring
|
||||
size_t start = 1; // Skip opening bracket/paren
|
||||
size_t end = 1;
|
||||
const size_t values_end = str.size() - 1; // Skip closing bracket/paren
|
||||
|
||||
// Pre-declare item string to avoid repeated allocations
|
||||
std::string item;
|
||||
|
||||
// Iterate through the string manually to split by commas
|
||||
while ((end = str.find(',', start)) != std::string::npos && end < values_end) {
|
||||
// Extract item directly from original string without creating substring
|
||||
item.assign(str, start, end - start);
|
||||
|
||||
// Trim leading and trailing spaces
|
||||
trim(item);
|
||||
|
||||
// Remove quotes from each token if necessary
|
||||
removeQuotes(item);
|
||||
|
||||
result.push_back(std::move(item));
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
// Handle the last item after the last comma
|
||||
if (start < values_end) {
|
||||
item.assign(str, start, values_end - start);
|
||||
trim(item);
|
||||
removeQuotes(item);
|
||||
result.push_back(std::move(item));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to read file into a set of strings
|
||||
std::unordered_set<std::string> readSetFromFile(const std::string& filePath) {
|
||||
std::lock_guard<std::mutex> lock(file_access_mutex);
|
||||
std::unordered_set<std::string> lines;
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "r");
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Unable to open file: " + filePath);
|
||||
#endif
|
||||
return lines;
|
||||
}
|
||||
|
||||
static constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE];
|
||||
size_t len;
|
||||
while (fgets(buffer, BUFFER_SIZE, file)) {
|
||||
// Remove trailing newline character if it exists
|
||||
len = strlen(buffer);
|
||||
if (len > 0 && buffer[len - 1] == '\n') {
|
||||
buffer[len - 1] = '\0';
|
||||
}
|
||||
lines.insert(buffer);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
#else
|
||||
std::ifstream file(filePath);
|
||||
if (!file.is_open()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Unable to open file: " + filePath);
|
||||
#endif
|
||||
return lines;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
lines.insert(std::move(line));
|
||||
}
|
||||
|
||||
file.close();
|
||||
#endif
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
// Function to write a set to a file
|
||||
void writeSetToFile(const std::unordered_set<std::string>& fileSet, const std::string& filePath) {
|
||||
std::lock_guard<std::mutex> lock(file_access_mutex);
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "w");
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Failed to open file: " + filePath);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& entry : fileSet) {
|
||||
fprintf(file, "%s\n", entry.c_str());
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
#else
|
||||
std::ofstream file(filePath);
|
||||
if (!file.is_open()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Failed to open file: " + filePath);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& entry : fileSet) {
|
||||
file << entry << '\n';
|
||||
}
|
||||
|
||||
file.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Function to compare two file lists and save duplicates to an output file
|
||||
void compareFilesLists(const std::string& txtFilePath1, const std::string& txtFilePath2, const std::string& outputTxtFilePath) {
|
||||
// Read files into sets
|
||||
std::unordered_set<std::string> fileSet1 = readSetFromFile(txtFilePath1);
|
||||
std::unordered_set<std::string> fileSet2 = readSetFromFile(txtFilePath2);
|
||||
std::unordered_set<std::string> duplicateFiles;
|
||||
|
||||
// Find intersection (common elements) between the two sets
|
||||
for (const auto& entry : fileSet1) {
|
||||
if (fileSet2.count(entry)) {
|
||||
duplicateFiles.insert(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the duplicates to the output file
|
||||
writeSetToFile(duplicateFiles, outputTxtFilePath);
|
||||
}
|
||||
|
||||
// Helper function to read a text file and process each line with a callback
|
||||
void processFileLines(const std::string& filePath, const std::function<void(const std::string&)>& callback) {
|
||||
std::lock_guard<std::mutex> lock(file_access_mutex);
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* file = fopen(filePath.c_str(), "r");
|
||||
if (!file) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Unable to open file: " + filePath);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// OPTIMIZATION 1: Larger buffer for better I/O performance
|
||||
static constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
while (fgets(buffer, BUFFER_SIZE, file)) {
|
||||
// OPTIMIZATION 2: Find newline directly instead of strlen()
|
||||
char* newlinePos = strchr(buffer, '\n');
|
||||
if (newlinePos) {
|
||||
*newlinePos = '\0'; // Remove newline in-place
|
||||
}
|
||||
|
||||
// OPTIMIZATION 3: Pass buffer directly - no string construction overhead
|
||||
callback(std::string(buffer));
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
#else
|
||||
// OPTIMIZATION 4: Use faster I/O for fstream version
|
||||
std::ifstream file(filePath);
|
||||
if (!file.is_open()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
logMessage("Unable to open file: " + filePath);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// OPTIMIZATION 5: Reserve string capacity to avoid reallocations
|
||||
std::string line;
|
||||
line.reserve(256); // Reasonable default for most lines
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
callback(line);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void compareWildcardFilesLists(
|
||||
const std::string& wildcardPatternFilePath,
|
||||
const std::string& txtFilePath,
|
||||
const std::string& outputTxtFilePath
|
||||
) {
|
||||
// STEP 1: Read target file into fast lookup set (only once)
|
||||
const std::unordered_set<std::string> targetLines = readSetFromFile(txtFilePath);
|
||||
std::unordered_set<std::string> duplicates;
|
||||
|
||||
// STEP 2: Get wildcard files
|
||||
std::vector<std::string> wildcardFiles = getFilesListByWildcards(wildcardPatternFilePath);
|
||||
|
||||
// STEP 3: Process each wildcard file line-by-line (minimum memory)
|
||||
for (auto& filePath : wildcardFiles) {
|
||||
if (filePath == txtFilePath) {
|
||||
filePath = "";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process line-by-line without loading entire file into memory
|
||||
processFileLines(filePath, [&](const std::string& line) {
|
||||
// O(1) lookup + O(1) insert if duplicate found
|
||||
if (targetLines.count(line)) {
|
||||
duplicates.insert(line);
|
||||
}
|
||||
});
|
||||
filePath = "";
|
||||
}
|
||||
|
||||
// STEP 4: Write results
|
||||
writeSetToFile(duplicates, outputTxtFilePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,736 @@
|
||||
/********************************************************************************
|
||||
* File: mod_funcs.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file provides the implementations of functions declared in
|
||||
* mod_funcs.hpp. These functions handle the conversion of `.pchtxt` mod files
|
||||
* into `.ips` binary patches used by the Ultrahand Overlay project. This includes
|
||||
* parsing, validating, and encoding patch data into the IPS format.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#include <mod_funcs.hpp>
|
||||
|
||||
namespace ult {
|
||||
|
||||
//const std::string CHEAT_HEADER = "// auto generated by pchtxt2cheat\n\n";
|
||||
const std::string CHEAT_TYPE = "04000000";
|
||||
const std::string CHEAT_EXT = ".txt";
|
||||
const std::string CHEAT_ENCODING = "ascii";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Checks if a cheat already exists in the cheat file.
|
||||
* @param cheatFilePath The path to the cheat file.
|
||||
* @param newCheat The new cheat to check.
|
||||
* @return True if the cheat exists, otherwise false.
|
||||
*/
|
||||
bool cheatExists(const std::string& cheatFilePath, const std::string& newCheat) {
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* cheatFile = fopen(cheatFilePath.c_str(), "r"); // Open the cheat file in read mode
|
||||
if (!cheatFile) {
|
||||
return false; // Return false if the file cannot be opened
|
||||
}
|
||||
|
||||
//size_t len;
|
||||
char buffer[1024]; // Buffer to store each line
|
||||
while (fgets(buffer, sizeof(buffer), cheatFile)) {
|
||||
// Remove newline character, if present
|
||||
const size_t len = strlen(buffer);
|
||||
if (len > 0 && buffer[len - 1] == '\n') {
|
||||
buffer[len - 1] = '\0';
|
||||
}
|
||||
if (newCheat == buffer) {
|
||||
fclose(cheatFile); // Close the file before returning
|
||||
return true; // Cheat exists
|
||||
}
|
||||
}
|
||||
|
||||
fclose(cheatFile); // Close the file after processing
|
||||
return false; // Cheat does not exist
|
||||
#else
|
||||
std::ifstream cheatFile(cheatFilePath);
|
||||
if (!cheatFile) {
|
||||
return false; // Return false if the file cannot be opened
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(cheatFile, line)) {
|
||||
if (line == newCheat) {
|
||||
return true; // Cheat exists
|
||||
}
|
||||
}
|
||||
return false; // Cheat does not exist
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Appends a new cheat to the cheat file.
|
||||
* @param cheatFilePath The path to the cheat file.
|
||||
* @param newCheat The new cheat to append.
|
||||
*/
|
||||
void appendCheatToFile(const std::string& cheatFilePath, const std::string& newCheat) {
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* cheatFile = fopen(cheatFilePath.c_str(), "a"); // Open the cheat file in append mode
|
||||
if (!cheatFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to open cheat file for appending: " + cheatFilePath);
|
||||
#endif
|
||||
return; // Handle the error accordingly
|
||||
}
|
||||
|
||||
fprintf(cheatFile, "%s\n", newCheat.c_str()); // Write the new cheat followed by a newline
|
||||
fclose(cheatFile); // Close the file
|
||||
#else
|
||||
std::ofstream cheatFile(cheatFilePath, std::ios::app);
|
||||
if (!cheatFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Failed to open cheat file for appending: " + cheatFilePath);
|
||||
#endif
|
||||
return; // Handle the error accordingly
|
||||
}
|
||||
|
||||
cheatFile << newCheat << std::endl; // Write the new cheat
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the cheat name from the given file path.
|
||||
* @param filePath The full file path.
|
||||
* @return The extracted cheat name.
|
||||
*/
|
||||
std::string extractCheatName(const std::string &filePath) {
|
||||
const size_t lastSlash = filePath.find_last_of("/\\");
|
||||
if (lastSlash == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
const size_t secondLastSlash = filePath.find_last_of("/\\", lastSlash - 1);
|
||||
if (secondLastSlash == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
const std::string lastDir = filePath.substr(secondLastSlash + 1, lastSlash - secondLastSlash - 1);
|
||||
std::string fileName = filePath.substr(lastSlash + 1);
|
||||
const size_t dotPos = fileName.find_last_of('.');
|
||||
if (dotPos != std::string::npos) {
|
||||
fileName = fileName.substr(0, dotPos);
|
||||
}
|
||||
|
||||
// If lastDir contains " - ", extract the part after " - "
|
||||
const size_t dashPos = lastDir.find(" - ");
|
||||
std::string cheatName = lastDir;
|
||||
if (dashPos != std::string::npos) {
|
||||
cheatName = lastDir.substr(dashPos + 3);
|
||||
}
|
||||
|
||||
return cheatName + " " + fileName;
|
||||
}
|
||||
|
||||
// Helper function to determine if a string is a valid title ID
|
||||
bool isValidTitleID(const std::string &str) {
|
||||
if (str.length() != 16) return false;
|
||||
for (char c : str) {
|
||||
if (!std::isxdigit(c)) return false; // Check if each character is a hexadecimal digit
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to find the title ID in the text, avoiding the @nsobid- line
|
||||
std::string findTitleID(const std::string &text) {
|
||||
const size_t nsobidPos = text.find("@nsobid-");
|
||||
const size_t startPos = (nsobidPos != std::string::npos) ? nsobidPos + 40 + 8 : 0; // Skip past @nsobid- and its value
|
||||
|
||||
std::string potentialID;
|
||||
for (size_t i = startPos; i <= text.length() - 16; ++i) {
|
||||
potentialID = text.substr(i, 16);
|
||||
if (isValidTitleID(potentialID)) {
|
||||
return potentialID;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a .pchtxt file to a cheat file.
|
||||
* @param pchtxtPath The file path to the .pchtxt file.
|
||||
* @param cheatName The name of the cheat.
|
||||
* @param outCheatPath The file path for the output cheat file.
|
||||
* @return True if the conversion was successful, false otherwise.
|
||||
*/
|
||||
bool pchtxt2cheat(const std::string &pchtxtPath, std::string cheatName, std::string outCheatPath) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Starting pchtxt2cheat with pchtxtPath: " + pchtxtPath);
|
||||
#endif
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* pchtxtFile = fopen(pchtxtPath.c_str(), "r");
|
||||
if (!pchtxtFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to open file " + pchtxtPath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the entire file into a string
|
||||
std::string pchtxt;
|
||||
char buffer[4096];
|
||||
while (fgets(buffer, sizeof(buffer), pchtxtFile)) {
|
||||
pchtxt += buffer;
|
||||
}
|
||||
fclose(pchtxtFile);
|
||||
#else
|
||||
std::ifstream pchtxtFile(pchtxtPath);
|
||||
if (!pchtxtFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to open file " + pchtxtPath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string pchtxt((std::istreambuf_iterator<char>(pchtxtFile)), std::istreambuf_iterator<char>());
|
||||
#endif
|
||||
|
||||
const size_t nsobidPos = pchtxt.find("@nsobid-");
|
||||
if (nsobidPos == std::string::npos) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Could not find bid in pchtxt file, the file is likely invalid.");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string bid = pchtxt.substr(nsobidPos + 8, 40);
|
||||
const std::string bidShort = bid.substr(0, 16);
|
||||
|
||||
const std::string tid = findTitleID(pchtxt);
|
||||
if (tid.empty()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Could not find TID in pchtxt file, the file is likely invalid.");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string cheatFilePath;
|
||||
|
||||
if (outCheatPath.empty()) {
|
||||
const std::string folderPath = "sdmc:/atmosphere/contents/" + tid + "/cheats/";
|
||||
createDirectory(folderPath);
|
||||
cheatFilePath = folderPath + bidShort + CHEAT_EXT;
|
||||
} else {
|
||||
cheatFilePath = outCheatPath;
|
||||
}
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* existingCheatFile = fopen(cheatFilePath.c_str(), "r");
|
||||
bool cheatNameExists = false;
|
||||
if (existingCheatFile) {
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), existingCheatFile)) {
|
||||
if (std::string(line) == "[" + cheatName + "]\n") {
|
||||
cheatNameExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(existingCheatFile);
|
||||
}
|
||||
#else
|
||||
std::ifstream existingCheatFile(cheatFilePath);
|
||||
bool cheatNameExists = false;
|
||||
if (existingCheatFile) {
|
||||
std::string line;
|
||||
while (std::getline(existingCheatFile, line)) {
|
||||
if (line == "[" + cheatName + "]") {
|
||||
cheatNameExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
existingCheatFile.close();
|
||||
#endif
|
||||
|
||||
// Open output cheat file
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
FILE* outCheatFile = fopen(cheatFilePath.c_str(), "a");
|
||||
if (!outCheatFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to create cheat file " + cheatFilePath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
std::ofstream outCheatFile(cheatFilePath, std::ios::app);
|
||||
if (!outCheatFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to create cheat file " + cheatFilePath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!cheatNameExists) {
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
fprintf(outCheatFile, "[%s]\n", cheatName.c_str());
|
||||
#else
|
||||
outCheatFile << "[" << cheatName << "]\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
bool enabled = true;
|
||||
int validCheatsProcessed = 0; // ADDED: Track number of valid cheats processed
|
||||
StringStream iss(pchtxt); // Use your custom StringStream
|
||||
std::string line;
|
||||
|
||||
std::string addrStr, valStr;
|
||||
size_t spacePos;
|
||||
int codeOffset;
|
||||
std::string cheatLine;
|
||||
char offsetBuffer[9];
|
||||
|
||||
// Use your custom getline method instead of std::getline
|
||||
while (iss.getline(line, '\n')) { // Custom getline with newline as the delimiter
|
||||
|
||||
// strip inline C++-style comments
|
||||
const auto slashPos = line.find("//");
|
||||
if (slashPos != std::string::npos)
|
||||
line = line.substr(0, slashPos);
|
||||
|
||||
// strip inline hash comments (but leave full-line # for headers)
|
||||
const auto hashPos = line.find('#');
|
||||
if (hashPos != std::string::npos && hashPos > 0)
|
||||
line = line.substr(0, hashPos);
|
||||
|
||||
trim(line);
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
|
||||
if (line.find("@flag offset_shift ") == 0) {
|
||||
const std::string offsetStr = line.substr(19);
|
||||
offset = (offsetStr.find("0x") == 0 ? std::strtol(offsetStr.c_str(), nullptr, 16) : std::strtol(offsetStr.c_str(), nullptr, 10)) - 0x100;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.find("@enabled") == 0) {
|
||||
enabled = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.find("@disabled") == 0) {
|
||||
enabled = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.find("@stop") == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
spacePos = line.find(' ');
|
||||
if (spacePos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
addrStr = line.substr(0, spacePos);
|
||||
valStr = line.substr(spacePos + 1);
|
||||
|
||||
if (addrStr.find_first_not_of("0123456789abcdefABCDEF") != std::string::npos || valStr.find_first_not_of("0123456789abcdefABCDEF") != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
codeOffset = std::strtol(valStr.c_str(), nullptr, 16) + offset;
|
||||
snprintf(offsetBuffer, sizeof(offsetBuffer), "%08X", codeOffset);
|
||||
cheatLine = CHEAT_TYPE + " " + addrStr + " " + hexToReversedHex(offsetBuffer);
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
// Check if cheat already exists
|
||||
FILE* checkFile = fopen(cheatFilePath.c_str(), "r");
|
||||
bool exists = false;
|
||||
if (checkFile) {
|
||||
char checkLine[256];
|
||||
while (fgets(checkLine, sizeof(checkLine), checkFile)) {
|
||||
if (std::string(checkLine) == cheatLine + "\n") {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(checkFile);
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
fprintf(outCheatFile, "%s\n", cheatLine.c_str());
|
||||
validCheatsProcessed++; // ADDED: Increment counter for new cheats
|
||||
}
|
||||
#else
|
||||
if (!cheatExists(cheatFilePath, cheatLine)) {
|
||||
outCheatFile << cheatLine << "\n";
|
||||
validCheatsProcessed++; // ADDED: Increment counter for new cheats
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
fclose(outCheatFile);
|
||||
#else
|
||||
outCheatFile.close();
|
||||
#endif
|
||||
|
||||
// ADDED: Check if any valid cheats were processed
|
||||
if (validCheatsProcessed == 0) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Warning: No valid patch data found to convert to cheats in " + pchtxtPath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Corrected helper function to convert values to big-endian format
|
||||
uint32_t toBigEndian(uint32_t value) {
|
||||
return ((value & 0x000000FF) << 24) |
|
||||
((value & 0x0000FF00) << 8) |
|
||||
((value & 0x00FF0000) >> 8) |
|
||||
((value & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
uint16_t toBigEndian(uint16_t value) {
|
||||
return ((value & 0x00FF) << 8) |
|
||||
((value & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
// Helper function to convert a vector of bytes to a hex string for logging
|
||||
|
||||
std::string hexToString(const std::vector<uint8_t>& bytes) {
|
||||
StringStream oss; // Use your custom StringStream
|
||||
oss.hex(); // Enable hex mode for the stream
|
||||
|
||||
for (uint8_t byte : bytes) {
|
||||
if (byte < 0x10) {
|
||||
oss << "0"; // Append leading zero for single-digit hex values
|
||||
}
|
||||
oss << static_cast<int>(byte); // Convert byte to int and then append it
|
||||
}
|
||||
|
||||
return oss.str(); // Return the final hex string
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a .pchtxt file to an IPS file using fstream.
|
||||
*
|
||||
* This function reads the contents of a .pchtxt file, extracts the address-value pairs,
|
||||
* and generates an IPS file with the provided output folder.
|
||||
*
|
||||
* @param pchtxtPath The file path to the .pchtxt file.
|
||||
* @param outputFolder The folder path for the output IPS file.
|
||||
* @return True if the conversion was successful, false otherwise.
|
||||
*/
|
||||
bool pchtxt2ips(const std::string& pchtxtPath, const std::string& outputFolder) {
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
// Use FILE* for reading
|
||||
FILE* pchtxtFile = fopen(pchtxtPath.c_str(), "r");
|
||||
if (!pchtxtFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to open file " + pchtxtPath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::pair<uint32_t, std::vector<uint8_t>>> patches;
|
||||
char lineBuffer[512]; // Fixed: Use char buffer for fgets
|
||||
std::string line;
|
||||
uint32_t lineNum = 0;
|
||||
std::string nsobid;
|
||||
int offset = 0; // Default offset
|
||||
bool enabled = true;
|
||||
|
||||
uint32_t address;
|
||||
uint8_t byte;
|
||||
std::vector<uint8_t> valueBytes;
|
||||
std::string offsetStr;
|
||||
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), pchtxtFile) != nullptr) {
|
||||
line = lineBuffer; // Convert to string
|
||||
++lineNum;
|
||||
if (line.empty() || line.front() == '@' || !enabled) {
|
||||
if (line.find("@nsobid-") == 0) {
|
||||
nsobid = line.substr(8);
|
||||
}
|
||||
if (line.find("@flag offset_shift ") == 0) {
|
||||
offsetStr = line.substr(19);
|
||||
offset = (offsetStr.find("0x") == 0 ? std::strtol(offsetStr.c_str(), nullptr, 16) : std::strtol(offsetStr.c_str(), nullptr, 10));
|
||||
}
|
||||
if (line.find("@enabled") == 0) {
|
||||
enabled = true;
|
||||
continue;
|
||||
}
|
||||
if (line.find("@disabled") == 0) {
|
||||
enabled = false;
|
||||
continue;
|
||||
}
|
||||
if (line.find("@stop") == 0) {
|
||||
break;
|
||||
}
|
||||
continue; // Skip empty lines and lines starting with '@'
|
||||
}
|
||||
|
||||
StringStream iss(line);
|
||||
std::string addressStr, valueStr;
|
||||
|
||||
if (!(iss >> addressStr >> valueStr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char* endPtr;
|
||||
address = std::strtoul(addressStr.c_str(), &endPtr, 16) + offset; // Adjust address by offset
|
||||
if (*endPtr != '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < valueStr.length(); i += 2) {
|
||||
byte = ult::stoi(valueStr.substr(i, 2), nullptr, 16);
|
||||
valueBytes.push_back(byte);
|
||||
}
|
||||
|
||||
if (valueBytes.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
patches.push_back(std::make_pair(address, valueBytes));
|
||||
valueBytes.clear();
|
||||
}
|
||||
|
||||
fclose(pchtxtFile);
|
||||
|
||||
#else
|
||||
// Use fstream for reading
|
||||
std::ifstream pchtxtFile(pchtxtPath);
|
||||
if (!pchtxtFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to open file " + pchtxtPath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::pair<uint32_t, std::vector<uint8_t>>> patches;
|
||||
std::string line;
|
||||
uint32_t lineNum = 0;
|
||||
std::string nsobid;
|
||||
int offset = 0; // Default offset
|
||||
|
||||
uint32_t address;
|
||||
uint8_t byte;
|
||||
std::vector<uint8_t> valueBytes;
|
||||
std::string offsetStr;
|
||||
|
||||
while (std::getline(pchtxtFile, line)) {
|
||||
++lineNum;
|
||||
if (line.empty() || line.front() == '@') {
|
||||
if (line.find("@nsobid-") == 0) {
|
||||
nsobid = line.substr(8);
|
||||
}
|
||||
if (line.find("@flag offset_shift ") == 0) {
|
||||
offsetStr = line.substr(19);
|
||||
offset = (offsetStr.find("0x") == 0 ? std::strtol(offsetStr.c_str(), nullptr, 16) : std::strtol(offsetStr.c_str(), nullptr, 10));
|
||||
}
|
||||
if (line.find("@stop") == 0) {
|
||||
break;
|
||||
}
|
||||
continue; // Skip empty lines and lines starting with '@'
|
||||
}
|
||||
|
||||
StringStream iss(line);
|
||||
std::string addressStr, valueStr;
|
||||
|
||||
if (!(iss >> addressStr >> valueStr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char* endPtr;
|
||||
address = std::strtoul(addressStr.c_str(), &endPtr, 16) + offset; // Adjust address by offset
|
||||
if (*endPtr != '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < valueStr.length(); i += 2) {
|
||||
byte = ult::stoi(valueStr.substr(i, 2), nullptr, 16);
|
||||
valueBytes.push_back(byte);
|
||||
}
|
||||
|
||||
if (valueBytes.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
patches.push_back(std::make_pair(address, valueBytes));
|
||||
valueBytes.clear();
|
||||
}
|
||||
|
||||
pchtxtFile.close();
|
||||
#endif
|
||||
|
||||
// CHECK: Return false if no patches were found
|
||||
if (patches.empty()) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Warning: No valid patches found in " + pchtxtPath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nsobid.empty()) {
|
||||
nsobid = pchtxtPath.substr(pchtxtPath.find_last_of("/\\") + 1);
|
||||
nsobid = nsobid.substr(0, nsobid.find_last_of("."));
|
||||
}
|
||||
|
||||
// Trim any newline characters from nsobid
|
||||
trim(nsobid);
|
||||
trimNewline(nsobid);
|
||||
|
||||
const std::string ipsFileName = nsobid + ".ips";
|
||||
const std::string ipsFilePath = outputFolder + ipsFileName;
|
||||
|
||||
#if !USING_FSTREAM_DIRECTIVE
|
||||
// Use FILE* for writing
|
||||
FILE* ipsFile = fopen(ipsFilePath.c_str(), "wb");
|
||||
if (!ipsFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to create IPS file " + ipsFilePath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
fwrite(IPS32_HEAD_MAGIC, sizeof(char), std::strlen(IPS32_HEAD_MAGIC), ipsFile);
|
||||
|
||||
uint16_t valueLength;
|
||||
uint32_t bigEndianAddress;
|
||||
|
||||
for (const auto& patch : patches) {
|
||||
bigEndianAddress = toBigEndian(patch.first); // Convert address to big-endian
|
||||
fwrite(&bigEndianAddress, sizeof(bigEndianAddress), 1, ipsFile); // Write address
|
||||
|
||||
valueLength = toBigEndian(static_cast<uint16_t>(patch.second.size())); // Convert length to big-endian
|
||||
fwrite(&valueLength, sizeof(valueLength), 1, ipsFile); // Write length of value
|
||||
|
||||
fwrite(patch.second.data(), sizeof(uint8_t), patch.second.size(), ipsFile); // Write value
|
||||
}
|
||||
|
||||
fwrite(IPS32_FOOT_MAGIC, sizeof(char), std::strlen(IPS32_FOOT_MAGIC), ipsFile);
|
||||
fclose(ipsFile);
|
||||
|
||||
|
||||
FILE* _pchtxtFile = fopen(pchtxtPath.c_str(), "r");
|
||||
if (!_pchtxtFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to open file " + pchtxtPath);
|
||||
#endif
|
||||
return false; // Changed: Return false if we can't read the file for title ID
|
||||
}
|
||||
|
||||
// Read the entire file into a string
|
||||
std::string pchtxt;
|
||||
char buffer[4096];
|
||||
while (fgets(buffer, sizeof(buffer), _pchtxtFile)) {
|
||||
pchtxt += buffer;
|
||||
}
|
||||
fclose(_pchtxtFile);
|
||||
|
||||
|
||||
const std::string tid = findTitleID(pchtxt);
|
||||
if (!tid.empty()) {
|
||||
const std::string tidFilePath = outputFolder + tid;
|
||||
FILE* tidFile = fopen(tidFilePath.c_str(), "w");
|
||||
if (tidFile) {
|
||||
fclose(tidFile); // Creates an empty file
|
||||
}
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Warning: Could not find Title ID in " + pchtxtPath);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
// Use fstream for writing
|
||||
std::ofstream ipsFile(ipsFilePath, std::ios::binary);
|
||||
if (!ipsFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to create IPS file " + ipsFilePath);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
ipsFile.write(IPS32_HEAD_MAGIC, std::strlen(IPS32_HEAD_MAGIC));
|
||||
|
||||
uint16_t valueLength;
|
||||
uint32_t bigEndianAddress;
|
||||
for (const auto& patch : patches) {
|
||||
bigEndianAddress = toBigEndian(patch.first); // Convert address to big-endian
|
||||
ipsFile.write(reinterpret_cast<const char*>(&bigEndianAddress), sizeof(bigEndianAddress)); // Write address
|
||||
|
||||
valueLength = toBigEndian(static_cast<uint16_t>(patch.second.size())); // Convert length to big-endian
|
||||
ipsFile.write(reinterpret_cast<const char*>(&valueLength), sizeof(valueLength)); // Write length of value
|
||||
|
||||
ipsFile.write(reinterpret_cast<const char*>(patch.second.data()), patch.second.size()); // Write value
|
||||
}
|
||||
|
||||
ipsFile.write(IPS32_FOOT_MAGIC, std::strlen(IPS32_FOOT_MAGIC));
|
||||
ipsFile.close();
|
||||
|
||||
|
||||
std::ifstream _pchtxtFile(pchtxtPath);
|
||||
if (!_pchtxtFile) {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Error: Unable to open file " + pchtxtPath);
|
||||
#endif
|
||||
return false; // Changed: Return false if we can't read the file for title ID
|
||||
}
|
||||
|
||||
std::string pchtxt((std::istreambuf_iterator<char>(_pchtxtFile)), std::istreambuf_iterator<char>());
|
||||
|
||||
const std::string tid = findTitleID(pchtxt);
|
||||
if (!tid.empty()) {
|
||||
const std::string tidFilePath = outputFolder + tid;
|
||||
std::ofstream tidFile(tidFilePath);
|
||||
tidFile.close(); // Creates an empty file
|
||||
} else {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
if (!disableLogging)
|
||||
logMessage("Warning: Could not find Title ID in " + pchtxtPath);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,609 @@
|
||||
/********************************************************************************
|
||||
* File: string_funcs.cpp
|
||||
* Author: ppkantorski
|
||||
* Description:
|
||||
* This source file provides implementations for the string manipulation
|
||||
* functions declared in string_funcs.hpp. These utility functions support
|
||||
* operations such as trimming whitespace, removing quotes, normalizing
|
||||
* slashes, and performing other string cleanup tasks used throughout
|
||||
* the Ultrahand Overlay project.
|
||||
*
|
||||
* For the latest updates and contributions, visit the project's GitHub repository.
|
||||
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
|
||||
*
|
||||
* Note: Please be aware that this notice cannot be altered or removed. It is a part
|
||||
* of the project's documentation and must remain intact.
|
||||
*
|
||||
* Licensed under both GPLv2 and CC-BY-4.0
|
||||
* Copyright (c) 2024 ppkantorski
|
||||
********************************************************************************/
|
||||
|
||||
#include "string_funcs.hpp"
|
||||
|
||||
namespace ult {
|
||||
|
||||
|
||||
// Custom string conversion methods in place of std::
|
||||
std::string to_string(int value) {
|
||||
char buffer[12]; // Sufficient for 32-bit int
|
||||
snprintf(buffer, sizeof(buffer), "%d", value);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
int stoi(const std::string& str, std::size_t* pos, int base) {
|
||||
char* end;
|
||||
const long result = std::strtol(str.c_str(), &end, base);
|
||||
|
||||
if (pos) {
|
||||
*pos = end - str.c_str(); // Set the position to the last character processed
|
||||
}
|
||||
|
||||
// Handle out-of-range or conversion issues here if needed
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
|
||||
float stof(const std::string& str) {
|
||||
return strtof(str.c_str(), nullptr);
|
||||
}
|
||||
|
||||
|
||||
// Mimics std::getline() with a delimiter
|
||||
bool StringStream::getline(std::string& output, char delimiter) {
|
||||
if (position >= data.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t nextPos = data.find(delimiter, position);
|
||||
|
||||
if (nextPos != std::string::npos) {
|
||||
output.assign(data, position, nextPos - position); // No temporary string creation
|
||||
position = nextPos + 1;
|
||||
} else {
|
||||
output.assign(data, position, data.size() - position); // No temporary string creation
|
||||
position = data.size();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mimics operator >> to split by whitespace
|
||||
StringStream& StringStream::operator>>(std::string& output) {
|
||||
// Skip leading whitespace
|
||||
while (position < data.size() && std::isspace(data[position])) {
|
||||
++position;
|
||||
}
|
||||
|
||||
if (position >= data.size()) {
|
||||
output.clear();
|
||||
validState = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t nextPos = position;
|
||||
while (nextPos < data.size() && !std::isspace(data[nextPos])) {
|
||||
++nextPos;
|
||||
}
|
||||
|
||||
output.assign(data, position, nextPos - position); // Replace substr() with assign()
|
||||
position = nextPos;
|
||||
|
||||
validState = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Overload << operator for std::string
|
||||
StringStream& StringStream::operator<<(const std::string& input) {
|
||||
data += input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Overload << operator for const char*
|
||||
StringStream& StringStream::operator<<(const char* input) {
|
||||
data += input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Overload << operator for char
|
||||
StringStream& StringStream::operator<<(char input) {
|
||||
data += input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Overload << operator for int (handles hex mode)
|
||||
StringStream& StringStream::operator<<(int input) {
|
||||
if (hexMode) {
|
||||
char buffer[20]; // Buffer large enough for hex conversion
|
||||
sprintf(buffer, "%x", input); // Convert integer to hex string
|
||||
data += buffer;
|
||||
} else {
|
||||
data += ult::to_string(input);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Define the new overload for long long
|
||||
StringStream& StringStream::operator<<(long long input) {
|
||||
data += std::to_string(input);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Return the current buffer content
|
||||
std::string StringStream::str() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Trims leading and trailing whitespaces from a string.
|
||||
*
|
||||
* This function removes leading and trailing whitespaces, tabs, newlines, carriage returns, form feeds,
|
||||
* and vertical tabs from the input string.
|
||||
*
|
||||
* @param str The input string to trim.
|
||||
* @return The trimmed string.
|
||||
*/
|
||||
void trim(std::string& str) {
|
||||
const size_t first = str.find_first_not_of(" \t\n\r\f\v");
|
||||
if (first == std::string::npos) {
|
||||
str.clear(); // Fix: clear all-whitespace strings
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t last = str.find_last_not_of(" \t\n\r\f\v");
|
||||
|
||||
// True in-place modification - no temporary string creation
|
||||
if (last + 1 < str.length()) {
|
||||
str.erase(last + 1); // Remove trailing whitespace
|
||||
}
|
||||
if (first > 0) {
|
||||
str.erase(0, first); // Remove leading whitespace
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function to trim newline characters from the end of a string
|
||||
void trimNewline(std::string& str) {
|
||||
const size_t end = str.find_last_not_of("\n");
|
||||
if (end == std::string::npos) {
|
||||
str.clear(); // If the string consists entirely of newlines, clear it
|
||||
} else {
|
||||
str.erase(end + 1); // Remove all characters after the last non-newline character
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes all white spaces from a string.
|
||||
*
|
||||
* This function removes all white spaces, including spaces, tabs, newlines, carriage returns, form feeds,
|
||||
* and vertical tabs from the input string.
|
||||
*
|
||||
* @param str The input string to remove white spaces from.
|
||||
* @return The string with white spaces removed.
|
||||
*/
|
||||
std::string removeWhiteSpaces(const std::string& str) {
|
||||
std::string result;
|
||||
result.reserve(str.size()); // Reserve space for the result to avoid reallocations
|
||||
|
||||
std::remove_copy_if(str.begin(), str.end(), std::back_inserter(result), [](unsigned char c) {
|
||||
return std::isspace(c);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes quotes from a string.
|
||||
*
|
||||
* This function removes single and double quotes from the beginning and end of the input string.
|
||||
*
|
||||
* @param str The input string to remove quotes from.
|
||||
* @return The string with quotes removed.
|
||||
*/
|
||||
void removeQuotes(std::string& str) {
|
||||
if (str.size() >= 2) {
|
||||
const char front = str[0];
|
||||
const char back = str[str.size() - 1];
|
||||
if ((front == '\'' && back == '\'') || (front == '"' && back == '"')) {
|
||||
str.erase(0, 1);
|
||||
str.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Replaces multiple consecutive slashes with a single slash in a string.
|
||||
*
|
||||
* This function replaces sequences of two or more consecutive slashes with a single slash in the input string.
|
||||
*
|
||||
* @param input The input string to process.
|
||||
* @return The string with multiple slashes replaced.
|
||||
*/
|
||||
std::string replaceMultipleSlashes(const std::string& input) {
|
||||
std::string output;
|
||||
output.reserve(input.size()); // Reserve space for the output string
|
||||
|
||||
bool previousSlash = false;
|
||||
for (char c : input) {
|
||||
if (c == '/') {
|
||||
if (!previousSlash) {
|
||||
output.push_back(c);
|
||||
}
|
||||
previousSlash = true;
|
||||
} else {
|
||||
output.push_back(c);
|
||||
previousSlash = false;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Preprocesses a path string by replacing multiple slashes and adding "sdmc:" prefix.
|
||||
*
|
||||
* This function preprocesses a path string by removing multiple consecutive slashes,
|
||||
* resolving relative path components (. and ..), adding the "sdmc:" prefix if not present,
|
||||
* and modifying the input string in place.
|
||||
*
|
||||
* @param path The input path string to preprocess, passed by reference.
|
||||
* @param packagePath The base package path to resolve relative paths against.
|
||||
*/
|
||||
void preprocessPath(std::string& path, const std::string& packagePath) {
|
||||
removeQuotes(path);
|
||||
|
||||
// In-place multiple slash removal - no temporary string creation
|
||||
if (!path.empty()) {
|
||||
size_t writePos = 0;
|
||||
bool previousSlash = false;
|
||||
|
||||
for (size_t i = 0; i < path.length(); ++i) {
|
||||
if (path[i] == '/') {
|
||||
if (!previousSlash) {
|
||||
path[writePos++] = path[i];
|
||||
}
|
||||
previousSlash = true;
|
||||
} else {
|
||||
path[writePos++] = path[i];
|
||||
previousSlash = false;
|
||||
}
|
||||
}
|
||||
path.resize(writePos);
|
||||
}
|
||||
|
||||
// First handle "./" replacement if present
|
||||
if (!packagePath.empty() && path.length() >= 2 && path[0] == '.' && path[1] == '/') {
|
||||
// Handle "./" - replace with packagePath
|
||||
path.replace(0, 2, packagePath);
|
||||
}
|
||||
|
||||
size_t dotDotPos, lastSlash;
|
||||
std::string beforeDotDot;
|
||||
|
||||
// Then handle any "../" sequences that may exist anywhere in the path
|
||||
while (!path.empty()) {
|
||||
dotDotPos = path.find("../");
|
||||
if (dotDotPos == std::string::npos) {
|
||||
break; // No more "../" sequences found
|
||||
}
|
||||
|
||||
// Found "../" sequence at dotDotPos
|
||||
beforeDotDot = path.substr(0, dotDotPos);
|
||||
|
||||
// Remove trailing slash from the part before "../"
|
||||
if (!beforeDotDot.empty() && beforeDotDot.back() == '/') {
|
||||
beforeDotDot.pop_back();
|
||||
}
|
||||
|
||||
// Go up one level
|
||||
lastSlash = beforeDotDot.find_last_of('/');
|
||||
if (lastSlash != std::string::npos) {
|
||||
beforeDotDot = beforeDotDot.substr(0, lastSlash + 1);
|
||||
} else {
|
||||
// No slash found, we're at root level
|
||||
beforeDotDot = "/";
|
||||
}
|
||||
|
||||
// Replace the path up to and including this "../" with the resolved path
|
||||
path = beforeDotDot + path.substr(dotDotPos + 3);
|
||||
}
|
||||
|
||||
// Direct character comparison instead of substr() for sdmc: prefix
|
||||
if (path.length() < 5 ||
|
||||
path[0] != 's' || path[1] != 'd' || path[2] != 'm' || path[3] != 'c' || path[4] != ':') {
|
||||
path.insert(0, "sdmc:");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Preprocesses a URL string by adding "https://" prefix.
|
||||
*
|
||||
* This function preprocesses a URL string by adding the "https://" prefix if not already present.
|
||||
*
|
||||
* @param path The input URL string to preprocess, passed by reference and modified in-place.
|
||||
*/
|
||||
void preprocessUrl(std::string& path) {
|
||||
removeQuotes(path);
|
||||
if ((path.compare(0, 7, "http://") == 0) || (path.compare(0, 8, "https://") == 0)) {
|
||||
return; // No need to modify the string if it already has a prefix
|
||||
} else {
|
||||
path = "https://" + path; // Prepend "https://"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Drops the file extension from a filename.
|
||||
*
|
||||
* This function removes the file extension (characters after the last dot) from the input filename string.
|
||||
*
|
||||
* @param filename The input filename from which to drop the extension, passed by reference and modified in-place.
|
||||
*/
|
||||
void dropExtension(std::string& filename) {
|
||||
const size_t lastDotPos = filename.find_last_of('.'); // Single char instead of string
|
||||
if (lastDotPos != std::string::npos) {
|
||||
filename.resize(lastDotPos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a string starts with a given prefix.
|
||||
*
|
||||
* This function checks if the input string starts with the specified prefix.
|
||||
*
|
||||
* @param str The input string to check.
|
||||
* @param prefix The prefix to check for.
|
||||
* @return True if the string starts with the prefix, false otherwise.
|
||||
*/
|
||||
bool startsWith(const std::string& str, const std::string& prefix) {
|
||||
return str.compare(0, prefix.length(), prefix) == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Helper function to check if a string is a valid integer
|
||||
bool isValidNumber(const std::string& str) {
|
||||
if (str.empty() || ((str[0] != '-') && !std::isdigit(str[0])) || (str[0] == '-' && str.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 1; i < str.size(); ++i) {
|
||||
if (!std::isdigit(str[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string returnOrNull(const std::string& value) {
|
||||
return value.empty() ? NULL_STR : value;
|
||||
}
|
||||
|
||||
|
||||
// Function to slice a string from start to end index
|
||||
std::string sliceString(const std::string& str, size_t start, size_t end) {
|
||||
if (start < 0) start = 0;
|
||||
if (end > static_cast<size_t>(str.length())) end = str.length();
|
||||
if (start > end) start = end;
|
||||
return str.substr(start, end - start);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//std::string addQuotesIfNeeded(const std::string& str) {
|
||||
// if (str.find(' ') != std::string::npos) {
|
||||
// return "\"" + str + "\"";
|
||||
// }
|
||||
// return str;
|
||||
//}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a string to lowercase.
|
||||
*
|
||||
* This function takes a string as input and returns a lowercase version of that string.
|
||||
*
|
||||
* @param str The input string to convert to lowercase.
|
||||
* @return The lowercase version of the input string.
|
||||
*/
|
||||
|
||||
std::string stringToLowercase(const std::string& str) {
|
||||
std::string result = str;
|
||||
//std::transform(result.begin(), result.end(), result.begin(),
|
||||
// [](unsigned char c) { return std::tolower(c); });
|
||||
//return result;
|
||||
for (char& c : result) {
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
c += 32;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a string to uppercase.
|
||||
*
|
||||
* This function takes a string as input and returns an uppercase version of that string.
|
||||
*
|
||||
* @param str The input string to convert to uppercase.
|
||||
* @return The uppercase version of the input string.
|
||||
*/
|
||||
|
||||
std::string stringToUppercase(const std::string& str) {
|
||||
std::string result = str;
|
||||
for (char& c : result) {
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
c -= 32;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Formats a priority string to a desired width.
|
||||
*
|
||||
* This function takes a priority string and formats it to a specified desired width by padding with '0's if it's shorter
|
||||
* or truncating with '9's if it's longer.
|
||||
*
|
||||
* @param priority The input priority string to format.
|
||||
* @param desiredWidth The desired width of the formatted string (default is 4).
|
||||
* @return A formatted priority string.
|
||||
*/
|
||||
std::string formatPriorityString(const std::string& priority, int desiredWidth) {
|
||||
const int priorityLength = priority.length();
|
||||
|
||||
if (priorityLength > desiredWidth) {
|
||||
// FASTEST: Single allocation with direct fill
|
||||
return std::string(desiredWidth, '9');
|
||||
} else {
|
||||
// FASTEST: Single allocation + direct memory copy
|
||||
std::string result(desiredWidth, '0'); // Pre-fill with zeros
|
||||
memcpy(&result[desiredWidth - priorityLength], priority.data(), priorityLength);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes the part of the string after the first occurrence of '?' character.
|
||||
*
|
||||
* This function takes a string and removes the portion of the string that appears after
|
||||
* the first '?' character, if found. If no '?' character is present, the string remains unchanged.
|
||||
*
|
||||
* @param input The input string from which to remove the tag, passed by reference and modified in-place.
|
||||
*/
|
||||
void removeTag(std::string &input) {
|
||||
const size_t pos = input.find('?');
|
||||
if (pos != std::string::npos) {
|
||||
input.resize(pos); // Modify the string in-place to remove everything after the '?'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string getFirstLongEntry(const std::string& input, size_t minLength) {
|
||||
StringStream iss(input); // Use custom StringStream
|
||||
std::string word;
|
||||
|
||||
// Split the input string based on spaces and get the first word
|
||||
if (iss >> word) {
|
||||
// Check if the first word's length is greater than the specified length
|
||||
if (word.length() > minLength) {
|
||||
return word;
|
||||
}
|
||||
}
|
||||
|
||||
// Return an empty string if the first word is not longer than minLength
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
// This will take a string like "v1.3.5-abasdfasdfa" and output "1.3.5". string could also look like "test-1.3.5-1" or "v1.3.5" and we will only want "1.3.5"
|
||||
std::string cleanVersionLabel(const std::string& input) {
|
||||
std::string result;
|
||||
result.reserve(input.size());
|
||||
|
||||
size_t start = 0;
|
||||
|
||||
// Find the start of the version number (first digit)
|
||||
while (start < input.size() && !std::isdigit(input[start])) {
|
||||
start++;
|
||||
}
|
||||
|
||||
if (start == input.size()) {
|
||||
return ""; // No digits found
|
||||
}
|
||||
|
||||
|
||||
// Extract version number with dots and plus signs
|
||||
for (size_t i = start; i < input.size(); ++i) {
|
||||
const char c = input[i];
|
||||
if (std::isdigit(c) || c == '.' || c == '+') {
|
||||
result += c;
|
||||
} else {
|
||||
break; // Stop at first character that's not digit, dot, or plus
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string extractTitle(const std::string& input) {
|
||||
const size_t spacePos = input.find(' '); // Find the position of the first space
|
||||
|
||||
if (spacePos != std::string::npos) {
|
||||
// Extract the substring before the first space
|
||||
return input.substr(0, spacePos);
|
||||
} else {
|
||||
// If no space is found, return the original string
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> splitString(const std::string& str, const std::string& delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
// OPTIMIZATION: Pre-allocate space to avoid reallocations
|
||||
tokens.reserve(str.length() / (delimiter.length() + 1) + 1);
|
||||
|
||||
size_t start = 0;
|
||||
size_t end = str.find(delimiter);
|
||||
|
||||
while (end != std::string::npos) {
|
||||
// OPTIMIZATION: Direct construction instead of substr() - no temporary string
|
||||
tokens.emplace_back(str, start, end - start);
|
||||
start = end + delimiter.length();
|
||||
end = str.find(delimiter, start);
|
||||
}
|
||||
|
||||
// OPTIMIZATION: Direct construction for last token
|
||||
tokens.emplace_back(str, start);
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
// Function to split a string by a delimiter and return a specific index
|
||||
std::string splitStringAtIndex(const std::string& str, const std::string& delimiter, size_t index) {
|
||||
const std::vector<std::string> tokens = splitString(str, delimiter);
|
||||
|
||||
if (index < tokens.size()) {
|
||||
return tokens[index];
|
||||
} else {
|
||||
return ""; // Return empty string if index is out of bounds
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string customAlign(int number) {
|
||||
const std::string numStr = ult::to_string(number);
|
||||
const int paddingSpaces = (4 - numStr.length()) * 2;
|
||||
|
||||
// FASTEST: Single allocation + direct memory operations
|
||||
std::string result(paddingSpaces + numStr.length(), ' ');
|
||||
memcpy(&result[paddingSpaces], numStr.data(), numStr.length());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//#if IS_LAUNCHER_DIRECTIVE
|
||||
//std::string inputExists(const std::string& input) {
|
||||
// std::string e;
|
||||
// for (char c : input) {
|
||||
// e += (c + 5);
|
||||
// }
|
||||
// return e;
|
||||
//}
|
||||
//#endif
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user