├── .clang-format ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── LICENSE.thirdparty ├── Makefile ├── icon.jpg ├── readme.md ├── romfs ├── btnADark.png ├── btnALight.png ├── btnBDark.png ├── btnBLight.png ├── btnXDark.png ├── btnXLight.png ├── btnYDark.png ├── btnYLight.png ├── gba.png ├── logoSmall.png ├── splashBlack.png └── splashWhite.png └── source ├── GBACheats.h ├── cheats.cpp ├── gba.cpp ├── gba.h ├── globals.h ├── memory.cpp ├── memory.h ├── neon.h ├── neon_memcpy.S ├── port.h ├── sound.cpp ├── sound.h ├── sound_blargg.h ├── switch ├── clickable.cpp ├── clickable.h ├── colors.h ├── draw.cpp ├── draw.h ├── gbaover.h ├── hbkbd.cpp ├── hbkbd.h ├── iclickable.h ├── image.cpp ├── image.h ├── ini │ ├── ini.c │ └── ini.h ├── localtime.c ├── localtime.h ├── main.cpp ├── stb_image.c ├── stb_image.h ├── theme.cpp ├── theme.h ├── types.h ├── ui.cpp ├── ui.h ├── util.cpp ├── util.h ├── zoom.cpp └── zoom.h ├── system.cpp ├── system.h ├── thread.cpp ├── thread.h └── types.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: true 6 | AlignConsecutiveAssignments: false 7 | AlignEscapedNewlinesLeft: true 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: true 18 | AlwaysBreakTemplateDeclarations: true 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | BreakBeforeBinaryOperators: None 22 | BreakBeforeBraces: Attach 23 | BreakBeforeTernaryOperators: true 24 | BreakConstructorInitializersBeforeComma: false 25 | ColumnLimit: 140 26 | CommentPragmas: '^ IWYU pragma:' 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 28 | ConstructorInitializerIndentWidth: 4 29 | ContinuationIndentWidth: 4 30 | Cpp11BracedListStyle: true 31 | DerivePointerAlignment: true 32 | DisableFormat: false 33 | ExperimentalAutoDetectBinPacking: false 34 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, vec_foreach ] 35 | IndentCaseLabels: true 36 | IndentWidth: 8 37 | IndentWrappedFunctionNames: false 38 | KeepEmptyLinesAtTheStartOfBlocks: false 39 | MacroBlockBegin: '' 40 | MacroBlockEnd: '' 41 | MaxEmptyLinesToKeep: 1 42 | NamespaceIndentation: None 43 | ObjCBlockIndentWidth: 2 44 | ObjCSpaceAfterProperty: false 45 | ObjCSpaceBeforeProtocolList: false 46 | PenaltyBreakBeforeFirstCallParameter: 1 47 | PenaltyBreakComment: 300 48 | PenaltyBreakFirstLessLess: 120 49 | PenaltyBreakString: 1000 50 | PenaltyExcessCharacter: 1000000 51 | PenaltyReturnTypeOnItsOwnLine: 200 52 | PointerAlignment: Left 53 | SpaceAfterCStyleCast: false 54 | SpaceBeforeAssignmentOperators: true 55 | SpaceBeforeParens: ControlStatements 56 | SpaceInEmptyParentheses: false 57 | SpacesBeforeTrailingComments: 2 58 | SpacesInAngles: false 59 | SpacesInContainerLiterals: true 60 | SpacesInCStyleCastParentheses: false 61 | SpacesInParentheses: false 62 | SpacesInSquareBrackets: false 63 | Standard: Auto 64 | TabWidth: 8 65 | UseTab: Always 66 | ... 67 | 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.o 3 | *.elf 4 | EBOOT.BIN 5 | /libretro/obj/ 6 | 7 | /libretro/msvc/msvc-2010/Debug/ 8 | /libretro/msvc/msvc-2010/Release/ 9 | *.suo 10 | *.sdf 11 | *.bc 12 | *.js 13 | *.dll 14 | *.dylib 15 | package.json 16 | .npmignore 17 | *.zip 18 | 19 | *.nacp 20 | *.nro 21 | *.nso 22 | *.pfs0 23 | 24 | build -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Verwendet IntelliSense zum Ermitteln möglicher Attribute. 3 | // Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen. 4 | // Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "enter program name, for example ${workspaceFolder}/a.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": true, 17 | "MIMode": "gdb", 18 | "miDebuggerPath": "/path/to/gdb", 19 | "setupCommands": [ 20 | { 21 | "description": "Enable pretty-printing for gdb", 22 | "text": "-enable-pretty-printing", 23 | "ignoreFailures": true 24 | } 25 | ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.h": "c", 4 | "*.inc": "z80-asm", 5 | "*.asm": "z80-asm", 6 | "*.cfg": "ini", 7 | "*.shr": "glsl", 8 | "vector": "cpp", 9 | "xstring": "cpp", 10 | "xutility": "cpp", 11 | "initializer_list": "cpp", 12 | "type_traits": "cpp", 13 | "iosfwd": "cpp", 14 | "cmath": "cpp", 15 | "locale": "cpp" 16 | }, 17 | 18 | "clang.completion.enable": true, 19 | "clang.diagnostic.enable": true, 20 | "clang.cxxflags": [ 21 | "-IC:\\devkitPro\\libnx\\include", 22 | "-IC:\\devkitPro\\portlibs\\switch\\include", 23 | "-IC:\\devkitPro\\portlibs\\switch\\include\\freetype2" 24 | ] 25 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | 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 Library General Public License instead.) You can apply it to your programs, too. 13 | 14 | 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. 15 | 16 | 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. 17 | 18 | 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. 19 | 20 | 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. 21 | 22 | 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. 23 | 24 | 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. 25 | 26 | The precise terms and conditions for copying, distribution and modification follow. 27 | 28 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 29 | 30 | 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". 31 | 32 | 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. 33 | 34 | 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. 35 | 36 | 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. 37 | 38 | 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: 39 | 40 | a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. 41 | 42 | 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. 43 | 44 | 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.) 45 | 46 | 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. 47 | 48 | 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. 49 | 50 | 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. 51 | 52 | 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: 53 | 54 | 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, 55 | 56 | 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, 57 | 58 | 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.) 59 | 60 | 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. 61 | 62 | 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. 63 | 64 | 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. 65 | 66 | 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. 67 | 68 | 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. 69 | 70 | 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. 71 | 72 | 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. 73 | 74 | 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. 75 | 76 | This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 77 | 78 | 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. 79 | 80 | 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. 81 | 82 | 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. 83 | 84 | 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. 85 | 86 | NO WARRANTY 87 | 88 | 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. 89 | 90 | 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. 91 | 92 | END OF TERMS AND CONDITIONS 93 | 94 | How to Apply These Terms to Your New Programs 95 | 96 | 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. 97 | 98 | 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. 99 | 100 | One line to give the program's name and a brief idea of what it does. 101 | Copyright (C) 102 | 103 | 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. 104 | 105 | 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. 106 | 107 | 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 108 | 109 | Also add information on how to contact you by electronic and paper mail. 110 | 111 | If the program is interactive, make it output a short notice like this when it starts in an interactive mode: 112 | 113 | 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. 114 | 115 | 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. 116 | 117 | 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: 118 | 119 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. 120 | 121 | signature of Ty Coon, 1 April 1989 122 | Ty Coon, President of Vice 123 | 124 | 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 Library General Public License instead of this License. 125 | -------------------------------------------------------------------------------- /LICENSE.thirdparty: -------------------------------------------------------------------------------- 1 | 3DSGBA: 2 | Copyright (C) 2014 Steveice10 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | ini: 11 | Copyright (c) 2016 rxi 12 | 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy of 15 | this software and associated documentation files (the "Software"), to deal in 16 | the Software without restriction, including without limitation the rights to 17 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 18 | of the Software, and to permit persons to whom the Software is furnished to do 19 | so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | 32 | SDL_gfx: 33 | Copyright (C) 2001-2012 Andreas Schiffler 34 | 35 | This software is provided 'as-is', without any express or implied 36 | warranty. In no event will the authors be held liable for any damages 37 | arising from the use of this software. 38 | 39 | Permission is granted to anyone to use this software for any purpose, 40 | including commercial applications, and to alter it and redistribute it 41 | freely, subject to the following restrictions: 42 | 43 | 1. The origin of this software must not be misrepresented; you must not 44 | claim that you wrote the original software. If you use this software 45 | in a product, an acknowledgment in the product documentation would be 46 | appreciated but is not required. 47 | 48 | 2. Altered source versions must be plainly marked as such, and must not be 49 | misrepresented as being the original software. 50 | 51 | 3. This notice may not be removed or altered from any source 52 | distribution. 53 | 54 | Andreas Schiffler -- aschiffler at ferzkopp dot net 55 | 56 | Ne10: 57 | /* 58 | * Copyright 2012-16 ARM Limited and Contributors. 59 | * All rights reserved. 60 | * 61 | * Redistribution and use in source and binary forms, with or without 62 | * modification, are permitted provided that the following conditions are met: 63 | * * Redistributions of source code must retain the above copyright 64 | * notice, this list of conditions and the following disclaimer. 65 | * * Redistributions in binary form must reproduce the above copyright 66 | * notice, this list of conditions and the following disclaimer in the 67 | * documentation and/or other materials provided with the distribution. 68 | * * Neither the name of ARM Limited nor the 69 | * names of its contributors may be used to endorse or promote products 70 | * derived from this software without specific prior written permission. 71 | * 72 | * THIS SOFTWARE IS PROVIDED BY ARM LIMITED AND CONTRIBUTORS "AS IS" AND 73 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 74 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 75 | * DISCLAIMED. IN NO EVENT SHALL ARM LIMITED AND CONTRIBUTORS BE LIABLE FOR ANY 76 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 77 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 78 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 79 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 80 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 81 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 82 | */ 83 | 84 | /* 85 | * See: http://opensource.org/licenses/BSD-3-Clause for template 86 | */ 87 | 88 | stb_image.h: 89 | ------------------------------------------------------------------------------ 90 | This software is available under 2 licenses -- choose whichever you prefer. 91 | ------------------------------------------------------------------------------ 92 | ALTERNATIVE A - MIT License 93 | Copyright (c) 2017 Sean Barrett 94 | Permission is hereby granted, free of charge, to any person obtaining a copy of 95 | this software and associated documentation files (the "Software"), to deal in 96 | the Software without restriction, including without limitation the rights to 97 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 98 | of the Software, and to permit persons to whom the Software is furnished to do 99 | so, subject to the following conditions: 100 | The above copyright notice and this permission notice shall be included in all 101 | copies or substantial portions of the Software. 102 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 103 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 104 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 105 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 106 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 107 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 108 | SOFTWARE. 109 | ------------------------------------------------------------------------------ 110 | ALTERNATIVE B - Public Domain (www.unlicense.org) 111 | This is free and unencumbered software released into the public domain. 112 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 113 | software, either in source code form or as a compiled binary, for any purpose, 114 | commercial or non-commercial, and by any means. 115 | In jurisdictions that recognize copyright laws, the author or authors of this 116 | software dedicate any and all copyright interest in the software to the public 117 | domain. We make this dedication for the benefit of the public at large and to 118 | the detriment of our heirs and successors. We intend this dedication to be an 119 | overt act of relinquishment in perpetuity of all present and future rights to 120 | this software under copyright law. 121 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 122 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 123 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 124 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 125 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 126 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 127 | ------------------------------------------------------------------------------ 128 | 129 | hx-hbmenu: 130 | /* 131 | * Copyright 2017-2018 nx-hbmenu Authors 132 | * 133 | * Permission to use, copy, modify, and/or distribute this software for any purpose 134 | * with or without fee is hereby granted, provided that the above copyright notice 135 | * and this permission notice appear in all copies. 136 | * 137 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 138 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 139 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 140 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 141 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 142 | * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 143 | */ 144 | 145 | Checkpoint: 146 | GNU GPL 3.0 Copyright (C) 2017-2018 Bernardo Giordano 147 | The GPL is rather long, so I don't include it here. 148 | See here: 149 | https://github.com/BernardoGiordano/Checkpoint/blob/master/LICENSE 150 | or ask Richard Stallman for a copy -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". 19 | # ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) 20 | # 21 | # NO_ICON: if set to anything, do not use icon. 22 | # NO_NACP: if set to anything, no .nacp file is generated. 23 | # APP_TITLE is the name of the app stored in the .nacp file (Optional) 24 | # APP_AUTHOR is the author of the app stored in the .nacp file (Optional) 25 | # APP_VERSION is the version of the app stored in the .nacp file (Optional) 26 | # APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) 27 | # ICON is the filename of the icon (.jpg), relative to the project folder. 28 | # If not set, it attempts to use one of the following (in this order): 29 | # - .jpg 30 | # - icon.jpg 31 | # - /default_icon.jpg 32 | #--------------------------------------------------------------------------------- 33 | TARGET := $(notdir $(CURDIR)) 34 | BUILD := build 35 | SOURCES := source source/switch/ini source/switch 36 | DATA := data 37 | INCLUDES := include 38 | EXEFS_SRC := exefs_src 39 | APP_TITLE := VBA Next 40 | APP_AUTHOR := RSDuck and Contributors 41 | APP_VERSION := 0.6 42 | ROMFS := romfs 43 | 44 | #--------------------------------------------------------------------------------- 45 | # options for code generation 46 | #--------------------------------------------------------------------------------- 47 | ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE 48 | 49 | CFLAGS := -g -Wall -Ofast -ffunction-sections \ 50 | $(ARCH) $(DEFINES) \ 51 | `freetype-config --cflags` 52 | 53 | CFLAGS += $(INCLUDE) -D__SWITCH__ -DTILED_RENDERING -DBRANCHLESS_GBA_GFX \ 54 | -DUSE_FRAME_SKIP #-DNXLINK_STDIO#-DTHREADED_RENDERER 55 | 56 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 57 | 58 | ASFLAGS := -g $(ARCH) 59 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 60 | 61 | LIBS := `freetype-config --libs` 62 | 63 | #--------------------------------------------------------------------------------- 64 | # list of directories containing libraries, this must be the top level containing 65 | # include and lib 66 | #--------------------------------------------------------------------------------- 67 | LIBDIRS := $(PORTLIBS) $(LIBNX) 68 | 69 | 70 | #--------------------------------------------------------------------------------- 71 | # no real need to edit anything past this point unless you need to add additional 72 | # rules for different file extensions 73 | #--------------------------------------------------------------------------------- 74 | ifneq ($(BUILD),$(notdir $(CURDIR))) 75 | #--------------------------------------------------------------------------------- 76 | 77 | export OUTPUT := $(CURDIR)/$(TARGET) 78 | export TOPDIR := $(CURDIR) 79 | 80 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 81 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 82 | 83 | export DEPSDIR := $(CURDIR)/$(BUILD) 84 | 85 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 86 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 87 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 88 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 89 | 90 | #--------------------------------------------------------------------------------- 91 | # use CXX for linking C++ projects, CC for standard C 92 | #--------------------------------------------------------------------------------- 93 | ifeq ($(strip $(CPPFILES)),) 94 | #--------------------------------------------------------------------------------- 95 | export LD := $(CC) 96 | #--------------------------------------------------------------------------------- 97 | else 98 | #--------------------------------------------------------------------------------- 99 | export LD := $(CXX) 100 | #--------------------------------------------------------------------------------- 101 | endif 102 | #--------------------------------------------------------------------------------- 103 | 104 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 105 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 106 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 107 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 108 | 109 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 110 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 111 | -I$(CURDIR)/$(BUILD) 112 | 113 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 114 | 115 | export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) 116 | 117 | ifeq ($(strip $(ICON)),) 118 | icons := $(wildcard *.jpg) 119 | ifneq (,$(findstring $(TARGET).jpg,$(icons))) 120 | export APP_ICON := $(TOPDIR)/$(TARGET).jpg 121 | else 122 | ifneq (,$(findstring icon.jpg,$(icons))) 123 | export APP_ICON := $(TOPDIR)/icon.jpg 124 | endif 125 | endif 126 | else 127 | export APP_ICON := $(TOPDIR)/$(ICON) 128 | endif 129 | 130 | ifeq ($(strip $(NO_ICON)),) 131 | export NROFLAGS += --icon=$(APP_ICON) 132 | endif 133 | 134 | ifeq ($(strip $(NO_NACP)),) 135 | export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp 136 | endif 137 | 138 | ifneq ($(APP_TITLEID),) 139 | export NACPFLAGS += --titleid=$(APP_TITLEID) 140 | endif 141 | 142 | ifneq ($(ROMFS),) 143 | export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) 144 | endif 145 | 146 | .PHONY: $(BUILD) clean all 147 | 148 | #--------------------------------------------------------------------------------- 149 | all: $(BUILD) 150 | 151 | $(BUILD): 152 | @[ -d $@ ] || mkdir -p $@ 153 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 154 | 155 | #--------------------------------------------------------------------------------- 156 | clean: 157 | @echo clean ... 158 | @rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf 159 | 160 | 161 | #--------------------------------------------------------------------------------- 162 | else 163 | .PHONY: all 164 | 165 | DEPENDS := $(OFILES:.o=.d) 166 | 167 | #--------------------------------------------------------------------------------- 168 | # main targets 169 | #--------------------------------------------------------------------------------- 170 | all : $(OUTPUT).pfs0 $(OUTPUT).nro 171 | 172 | $(OUTPUT).pfs0 : $(OUTPUT).nso 173 | 174 | $(OUTPUT).nso : $(OUTPUT).elf 175 | 176 | ifeq ($(strip $(NO_NACP)),) 177 | $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp 178 | else 179 | $(OUTPUT).nro : $(OUTPUT).elf 180 | endif 181 | 182 | $(OUTPUT).elf : $(OFILES) 183 | 184 | $(OFILES_SRC) : $(HFILES_BIN) 185 | 186 | #--------------------------------------------------------------------------------- 187 | # you need a rule like this for each extension you use as binary data 188 | #--------------------------------------------------------------------------------- 189 | %.bin.o : %.bin 190 | #--------------------------------------------------------------------------------- 191 | @echo $(notdir $<) 192 | @$(bin2o) 193 | 194 | -include $(DEPENDS) 195 | 196 | #--------------------------------------------------------------------------------------- 197 | endif 198 | #--------------------------------------------------------------------------------------- 199 | -------------------------------------------------------------------------------- /icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/icon.jpg -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # VBA Next/VBA-M for Switch 2 | 3 | **Please stop using this, you have now far better options available. I recommend switching to the execellent and more accurate [mGBA](http://mgba.io/) which is available both in standalone form, as well as via [Retroarch](https://www.libretro.com/). But if you want to continue using vba-next, consider using the offical Retroarch core, which is reguarly updated and features more options than this frontend.** 4 | 5 | 6 | 7 | A VBA-M port for Nintendo Switch. It's based of the [libretro port](https://github.com/libretro/vba-next)(the actual emulator) and 8 | [3DSGBA](https://github.com/shinyquagsire23/3DSGBA)(the GUI, although heavily refactored). 9 | 10 | After porting 3DSGBA(which often crashed probably because of a huge amount of memory leaks), I tried porting mGBA which ran not so well. That's why I decided to experiment with a lighter less accurate emulator, which lead to this port. 11 | 12 | Needs [libnx](https://github.com/switchbrew/libnx) and [devkitPro](http://devkitpro.org/) to build. Just run `make`. 13 | 14 | ## Features 15 | 16 | - Quite high compability(haven't tried many games) 17 | - Save games and save states 18 | - Frameskip 19 | 20 | ## Problems 21 | 22 | - In rare occasions the audio has problems 23 | - Video and Input not frame accurate(see Speed hack) 24 | 25 | ## Speed hack 26 | 27 | NOTE: some things don't apply anymore or were inaccurate observations 28 | 29 | Before implementing this "speed hack" the emulator had regular slowdowns. Although I found out about these things by myself, these things might be common knowledge to emulator devs. These were the things which apparently slowed the emulator down: 30 | - The thread/core it's running on 31 | - The VSync 32 | 33 | The first problem was solved by running the whole emulator in a second thread with a very high priority pinned to a core not used by the system. 34 | 35 | Omitting the wait for vertical synchronisation lead naturally to artefacts. So I decided on only running the emulator inside the second thread, locked using sleep thread to 60 Hz. At the same time the main thread is locked by the vsync and only receives the framebuffer while sending the input. I left the audio in the emulator thread. 36 | 37 | This leads of course to the problem that both threads, although locked to 60 Hz, may not run in sync, so the input or the graphics of a frame may be skipped or out of sync sometimes. 38 | 39 | ## Credits 40 | 41 | - [jakibaki](https://github.com/jakibaki) and [dene-](https://github.com/dene-) who helped massively with their Pull Requests 42 | - [BernardoGiordano](https://github.com/BernardoGiordano) for the great keyboard in [Checkpoint](https://github.com/BernardoGiordano/Checkpoint). 43 | - VBA-M and [libretro devs](https://github.com/libretro/vba-next/graphs/contributors) 44 | - shinyquagsire23 and Steveice10 for [3DSGBA](https://github.com/shinyquagsire23/3DSGBA) 45 | - [libnx devs](https://github.com/switchbrew/libnx/graphs/contributors) 46 | -------------------------------------------------------------------------------- /romfs/btnADark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/btnADark.png -------------------------------------------------------------------------------- /romfs/btnALight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/btnALight.png -------------------------------------------------------------------------------- /romfs/btnBDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/btnBDark.png -------------------------------------------------------------------------------- /romfs/btnBLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/btnBLight.png -------------------------------------------------------------------------------- /romfs/btnXDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/btnXDark.png -------------------------------------------------------------------------------- /romfs/btnXLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/btnXLight.png -------------------------------------------------------------------------------- /romfs/btnYDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/btnYDark.png -------------------------------------------------------------------------------- /romfs/btnYLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/btnYLight.png -------------------------------------------------------------------------------- /romfs/gba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/gba.png -------------------------------------------------------------------------------- /romfs/logoSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/logoSmall.png -------------------------------------------------------------------------------- /romfs/splashBlack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/splashBlack.png -------------------------------------------------------------------------------- /romfs/splashWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RSDuck/vba-next-switch/c50aebf01b40dc463ebb6e922a814347fc2e926d/romfs/splashWhite.png -------------------------------------------------------------------------------- /source/GBACheats.h: -------------------------------------------------------------------------------- 1 | #ifndef GBA_CHEATS_H 2 | #define GBA_CHEATS_H 3 | 4 | struct CheatsData { 5 | int code; 6 | int size; 7 | int status; 8 | bool enabled; 9 | bool v3; 10 | uint32_t rawaddress; 11 | uint32_t address; 12 | uint32_t value; 13 | uint32_t oldValue; 14 | char codestring[20]; 15 | char desc[128]; 16 | }; 17 | 18 | void cheatsAdd(const char *codeStr, const char *desc, uint32_t rawaddress, uint32_t address, uint32_t value, int code, int size); 19 | void cheatsAddCheatCode(const char *code, const char *desc); 20 | void cheatsAddGSACode(const char *code, const char *desc, bool v3); 21 | void cheatsAddCBACode(const char *code, const char *desc); 22 | bool cheatsImportGSACodeFile(const char *name, int game, bool v3); 23 | void cheatsDelete(int number, bool restore); 24 | void cheatsDeleteAll(bool restore); 25 | void cheatsEnable(int number); 26 | void cheatsDisable(int number); 27 | void cheatsSaveCheatList(const char *file); 28 | bool cheatsLoadCheatList(const char *file); 29 | void cheatsWriteMemory(uint32_t address, uint32_t value); 30 | void cheatsWriteHalfWord(uint32_t address, uint16_t value); 31 | void cheatsWriteByte(uint32_t address, uint8_t value); 32 | int cheatsCheckKeys(uint32_t keys, uint32_t extended); 33 | void cheatListInit(); 34 | void cheatListDeinit(); 35 | void cheatsReadHumanReadable(const char *file); 36 | void cheatsWriteHumanReadable(const char *file); 37 | 38 | extern int cheatsNumber; 39 | #define CHEAT_LIST_SIZE 100 40 | #define CHEAT_LIST_ENTRY_SIZE 100 41 | extern CheatsData cheatsList[CHEAT_LIST_SIZE-1]; 42 | extern char* cheatsStringList[CHEAT_LIST_SIZE]; 43 | 44 | #endif // CHEATS_H 45 | -------------------------------------------------------------------------------- /source/cheats.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "switch/ui.h" 9 | 10 | #include "GBACheats.h" 11 | 12 | void cheatsReadHumanReadable(const char *file) { 13 | std::ifstream infile(file); 14 | std::string line; 15 | std::string description = ""; 16 | int curLine = 1; 17 | while (std::getline(infile, line)) { 18 | 19 | if(line.length() == 0) 20 | continue; 21 | 22 | if(line[0] == '#') { 23 | description = line.substr(1); 24 | continue; 25 | } 26 | 27 | std::istringstream iss(line); 28 | std::string cheat; 29 | 30 | bool enabled = true; 31 | 32 | if (!(iss >> cheat)) { 33 | uiStatusMsg("Failed to parse cheat at line " + curLine); 34 | continue; 35 | } 36 | 37 | if(cheat.length() < 12) { 38 | std::string secPart; 39 | iss >> secPart; 40 | cheat.append(secPart); 41 | } 42 | 43 | std::transform(cheat.begin(), cheat.end(),cheat.begin(), ::toupper); 44 | 45 | if(cheat.length() == 16) { 46 | // Gameshark v3 47 | cheatsAddGSACode(cheat.c_str(), description.c_str(), true); 48 | } else if(cheat.length() == 12) { 49 | // Gameshark v1/2 50 | cheat.insert(8, "0000"); 51 | cheatsAddGSACode(cheat.c_str(), description.c_str(), false); 52 | } else { 53 | uiStatusMsg("Failed to parse cheat at line " + curLine); 54 | } 55 | 56 | iss >> std::boolalpha >> enabled; 57 | 58 | if(!enabled) 59 | cheatsDisable(cheatsNumber-1); 60 | 61 | curLine++; 62 | } 63 | } 64 | 65 | void cheatsWriteHumanReadable(const char *file) { 66 | std::string description = ""; 67 | std::ofstream outfile(file); 68 | outfile << std::boolalpha; 69 | 70 | for(int i = 0; i < cheatsNumber; i++) { 71 | std::string localDesc = cheatsList[i].desc; 72 | if(localDesc != description) { 73 | description = localDesc; 74 | outfile << "#" << description << std::endl; 75 | } 76 | 77 | std::string codestring = cheatsList[i].codestring; 78 | std::string cheatFirstPart = codestring.substr(0,8); 79 | std::string cheatSecondPart; 80 | if(cheatsList[i].v3) 81 | cheatSecondPart = codestring.substr(8); 82 | else 83 | cheatSecondPart = codestring.substr(12); 84 | outfile << cheatFirstPart << " " << cheatSecondPart << " " << cheatsList[i].enabled << std::endl; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /source/gba.h: -------------------------------------------------------------------------------- 1 | #ifndef GBA_H 2 | #define GBA_H 3 | 4 | #include 5 | #include 6 | #include "GBACheats.h" 7 | #include "thread.h" 8 | 9 | #define BITS_16 0 10 | #define BITS_32 1 11 | 12 | #define SAVE_GAME_VERSION_1 1 13 | #define SAVE_GAME_VERSION_2 2 14 | #define SAVE_GAME_VERSION_3 3 15 | #define SAVE_GAME_VERSION_4 4 16 | #define SAVE_GAME_VERSION_5 5 17 | #define SAVE_GAME_VERSION_6 6 18 | #define SAVE_GAME_VERSION_7 7 19 | #define SAVE_GAME_VERSION_8 8 20 | #define SAVE_GAME_VERSION_9 9 21 | #define SAVE_GAME_VERSION_10 10 22 | #define SAVE_GAME_VERSION SAVE_GAME_VERSION_10 23 | 24 | #define R13_IRQ 18 25 | #define R14_IRQ 19 26 | #define SPSR_IRQ 20 27 | #define R13_USR 26 28 | #define R14_USR 27 29 | #define R13_SVC 28 30 | #define R14_SVC 29 31 | #define SPSR_SVC 30 32 | #define R13_ABT 31 33 | #define R14_ABT 32 34 | #define SPSR_ABT 33 35 | #define R13_UND 34 36 | #define R14_UND 35 37 | #define SPSR_UND 36 38 | #define R8_FIQ 37 39 | #define R9_FIQ 38 40 | #define R10_FIQ 39 41 | #define R11_FIQ 40 42 | #define R12_FIQ 41 43 | #define R13_FIQ 42 44 | #define R14_FIQ 43 45 | #define SPSR_FIQ 44 46 | 47 | typedef struct { 48 | uint8_t *address; 49 | uint32_t mask; 50 | } memoryMap; 51 | 52 | typedef union { 53 | struct { 54 | #ifdef MSB_FIRST 55 | uint8_t B3; 56 | uint8_t B2; 57 | uint8_t B1; 58 | uint8_t B0; 59 | #else 60 | uint8_t B0; 61 | uint8_t B1; 62 | uint8_t B2; 63 | uint8_t B3; 64 | #endif 65 | } B; 66 | struct { 67 | #ifdef MSB_FIRST 68 | uint16_t W1; 69 | uint16_t W0; 70 | #else 71 | uint16_t W0; 72 | uint16_t W1; 73 | #endif 74 | } W; 75 | #ifdef MSB_FIRST 76 | volatile uint32_t I; 77 | #else 78 | uint32_t I; 79 | #endif 80 | } reg_pair; 81 | 82 | typedef struct 83 | { 84 | reg_pair reg[45]; 85 | bool busPrefetch; 86 | bool busPrefetchEnable; 87 | uint32_t busPrefetchCount; 88 | uint32_t armNextPC; 89 | } bus_t; 90 | 91 | typedef struct 92 | { 93 | int layerEnable; 94 | int layerEnableDelay; 95 | int lcdTicks; 96 | } graphics_t; 97 | 98 | extern uint64_t joy; 99 | 100 | extern void (*cpuSaveGameFunc)(uint32_t,uint8_t); 101 | 102 | extern bool CPUWriteBatteryFile(const char *); 103 | extern bool CPUReadBatteryFile(const char *); 104 | extern bool CPUReadState(const uint8_t * data, unsigned size); 105 | extern unsigned CPUWriteState(uint8_t* data, unsigned size); 106 | extern int CPULoadRom(const char *); 107 | extern int CPULoadRomData(const char *data, int size); 108 | extern void doMirroring(bool); 109 | extern void CPUUpdateRegister(uint32_t, uint16_t); 110 | extern void CPUInit(const char *,bool); 111 | extern void CPUCleanUp (void); 112 | extern void CPUReset (void); 113 | extern void CPULoop(void); 114 | extern void UpdateJoypad(void); 115 | extern void CPUCheckDMA(int,int); 116 | #if USE_FRAME_SKIP 117 | extern void SetFrameskip(int); 118 | #endif 119 | 120 | #if THREADED_RENDERER 121 | extern void ThreadedRendererStart(); 122 | extern void ThreadedRendererStop(); 123 | #endif 124 | 125 | #if USE_MOTION_SENSOR 126 | 127 | #define HARDWARE_SENSOR_NONE 0 128 | #define HARDWARE_SENSOR_TILT 0x1 129 | #define HARDWARE_SENSOR_GYRO 0x2 130 | 131 | typedef struct { 132 | int sensor; 133 | 134 | int tilt_x; 135 | int tilt_y; 136 | 137 | uint16_t pinState; 138 | uint16_t direction; 139 | uint16_t gyroSample; 140 | bool readWrite; 141 | bool gyroEdge; 142 | } hardware_t; 143 | 144 | extern hardware_t hardware; 145 | 146 | #endif 147 | 148 | #endif // GBA_H 149 | -------------------------------------------------------------------------------- /source/globals.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBALS_H 2 | #define GLOBALS_H 3 | 4 | #include "types.h" 5 | #include 6 | 7 | //performance boost tweaks. 8 | #if USE_TWEAKS 9 | #define USE_TWEAK_SPEEDHACK 1 10 | #define USE_TWEAK_MEMFUNC 1 11 | #endif 12 | 13 | #define PIX_BUFFER_SCREEN_WIDTH 256 14 | 15 | extern int saveType; 16 | extern bool useBios; 17 | extern bool skipBios; 18 | extern bool cpuIsMultiBoot; 19 | extern int cpuSaveType; 20 | extern bool mirroringEnable; 21 | extern bool enableRtc; 22 | extern bool skipSaveGameBattery; // skip battery data when reading save states 23 | 24 | extern int cpuDmaCount; 25 | 26 | extern uint8_t *rom; 27 | extern uint8_t *bios; 28 | extern uint8_t *vram; 29 | extern uint16_t *pix; 30 | extern uint8_t *oam; 31 | extern uint8_t *ioMem; 32 | extern uint8_t *internalRAM; 33 | extern uint8_t *workRAM; 34 | extern uint8_t *paletteRAM; 35 | 36 | #endif // GLOBALS_H 37 | -------------------------------------------------------------------------------- /source/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef GBA_MEMORY_H 2 | #define GBA_MEMORY_H 3 | 4 | #define FLASH_128K_SZ 0x20000 5 | 6 | #define EEPROM_IDLE 0 7 | #define EEPROM_READADDRESS 1 8 | #define EEPROM_READDATA 2 9 | #define EEPROM_READDATA2 3 10 | #define EEPROM_WRITEDATA 4 11 | 12 | #define BAT_UNUSED_FRAMES_BEFORE_SAVE 60 13 | 14 | enum { 15 | IMAGE_UNKNOWN, 16 | IMAGE_GBA 17 | }; 18 | 19 | /* save game */ 20 | typedef struct { 21 | void *address; 22 | int size; 23 | } variable_desc; 24 | 25 | extern int autosaveCountdown; 26 | extern u32 rtcOffset; 27 | 28 | extern int flashSize; 29 | 30 | extern bool eepromInUse; 31 | extern int eepromSize; 32 | 33 | extern uint8_t *flashSaveMemory; 34 | extern u8 *eepromData; 35 | 36 | extern void eepromReadGameMem(const uint8_t *&data, int version); 37 | extern void eepromSaveGameMem(uint8_t *&data); 38 | 39 | extern int eepromRead(void); 40 | extern void eepromWrite(u8 value); 41 | extern void eepromInit(void); 42 | extern void eepromReset(void); 43 | 44 | extern u8 sramRead(u32 address); 45 | extern void sramWrite(u32 address, u8 byte); 46 | extern void sramDelayedWrite(u32 address, u8 byte); 47 | extern void flashSaveGameMem(uint8_t *& data); 48 | extern void flashReadGameMem(const uint8_t *& data, int version); 49 | 50 | extern uint8_t flashRead(uint32_t address); 51 | extern void flashWrite(uint32_t address, uint8_t byte); 52 | extern void flashDelayedWrite(uint32_t address, uint8_t byte); 53 | extern void flashSaveDecide(uint32_t address, uint8_t byte); 54 | extern void flashReset(void); 55 | extern void flashSetSize(int size); 56 | extern void flashInit(void); 57 | 58 | extern u16 rtcRead(u32 address); 59 | extern bool rtcWrite(u32 address, u16 value); 60 | extern void rtcEnable(bool); 61 | extern bool rtcIsEnabled (void); 62 | extern void rtcReset (void); 63 | extern void rtcReadGameMem(const uint8_t *& data); 64 | extern void rtcSaveGameMem(uint8_t *& data); 65 | 66 | extern u16 gyroRead(u32 address); 67 | extern bool gyroWrite(u32 address, u16 value); 68 | 69 | bool utilIsGBAImage(const char *); 70 | uint8_t *utilLoad(const char *, bool (*)(const char*), uint8_t *, int &); 71 | 72 | void utilWriteIntMem(uint8_t *& data, int); 73 | void utilWriteMem(uint8_t *& data, const void *in_data, unsigned size); 74 | void utilWriteDataMem(uint8_t *& data, variable_desc *); 75 | 76 | int utilReadIntMem(const uint8_t *& data); 77 | void utilReadMem(void *buf, const uint8_t *& data, unsigned size); 78 | void utilReadDataMem(const uint8_t *& data, variable_desc *); 79 | 80 | #endif // GBA_MEMORY_H 81 | -------------------------------------------------------------------------------- /source/neon.h: -------------------------------------------------------------------------------- 1 | #ifndef __NEON_H__ 2 | #define __NEON_H__ 3 | 4 | #if HAVE_NEON 5 | #if __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | void neon_memcpy(void*, const void*, size_t); 10 | 11 | #if __cplusplus 12 | } 13 | #endif 14 | 15 | #endif 16 | 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /source/neon_memcpy.S: -------------------------------------------------------------------------------- 1 | /* 2 | * NEON code contributed by Siarhei Siamashka . 3 | * Origin: http://sourceware.org/ml/libc-ports/2009-07/msg00003.html 4 | * 5 | * The GNU C Library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public License. 7 | * 8 | * tweaked for Android by Jim Huang 9 | */ 10 | 11 | .text 12 | .fpu neon 13 | 14 | .global neon_memcpy 15 | .type neon_memcpy, %function 16 | .align 4 17 | 18 | /* 19 | * ENABLE_UNALIGNED_MEM_ACCESSES macro can be defined to permit the use 20 | * of unaligned load/store memory accesses supported since ARMv6. This 21 | * will further improve performance, but can purely theoretically cause 22 | * problems if somebody decides to set SCTLR.A bit in the OS kernel 23 | * (to trap each unaligned memory access) or somehow mess with strongly 24 | * ordered/device memory. 25 | */ 26 | 27 | #define ENABLE_UNALIGNED_MEM_ACCESSES 1 28 | 29 | #define NEON_MAX_PREFETCH_DISTANCE 320 30 | 31 | neon_memcpy: 32 | .fnstart 33 | mov ip, r0 34 | cmp r2, #16 35 | blt 4f @ Have less than 16 bytes to copy 36 | 37 | @ First ensure 16 byte alignment for the destination buffer 38 | tst r0, #0xF 39 | beq 2f 40 | tst r0, #1 41 | ldrneb r3, [r1], #1 42 | strneb r3, [ip], #1 43 | subne r2, r2, #1 44 | tst ip, #2 45 | #ifdef ENABLE_UNALIGNED_MEM_ACCESSES 46 | ldrneh r3, [r1], #2 47 | strneh r3, [ip], #2 48 | #else 49 | ldrneb r3, [r1], #1 50 | strneb r3, [ip], #1 51 | ldrneb r3, [r1], #1 52 | strneb r3, [ip], #1 53 | #endif 54 | subne r2, r2, #2 55 | 56 | tst ip, #4 57 | beq 1f 58 | vld4.8 {d0[0], d1[0], d2[0], d3[0]}, [r1]! 59 | vst4.8 {d0[0], d1[0], d2[0], d3[0]}, [ip, :32]! 60 | sub r2, r2, #4 61 | 1: 62 | tst ip, #8 63 | beq 2f 64 | vld1.8 {d0}, [r1]! 65 | vst1.8 {d0}, [ip, :64]! 66 | sub r2, r2, #8 67 | 2: 68 | subs r2, r2, #32 69 | blt 3f 70 | mov r3, #32 71 | 72 | @ Main copy loop, 32 bytes are processed per iteration. 73 | @ ARM instructions are used for doing fine-grained prefetch, 74 | @ increasing prefetch distance progressively up to 75 | @ NEON_MAX_PREFETCH_DISTANCE at runtime 76 | 1: 77 | vld1.8 {d0-d3}, [r1]! 78 | cmp r3, #(NEON_MAX_PREFETCH_DISTANCE - 32) 79 | pld [r1, r3] 80 | addle r3, r3, #32 81 | vst1.8 {d0-d3}, [ip, :128]! 82 | sub r2, r2, #32 83 | cmp r2, r3 84 | bge 1b 85 | cmp r2, #0 86 | blt 3f 87 | 1: @ Copy the remaining part of the buffer (already prefetched) 88 | vld1.8 {d0-d3}, [r1]! 89 | subs r2, r2, #32 90 | vst1.8 {d0-d3}, [ip, :128]! 91 | bge 1b 92 | 3: @ Copy up to 31 remaining bytes 93 | tst r2, #16 94 | beq 4f 95 | vld1.8 {d0, d1}, [r1]! 96 | vst1.8 {d0, d1}, [ip, :128]! 97 | 4: 98 | @ Use ARM instructions exclusively for the final trailing part 99 | @ not fully fitting into full 16 byte aligned block in order 100 | @ to avoid "ARM store after NEON store" hazard. Also NEON 101 | @ pipeline will be (mostly) flushed by the time when the 102 | @ control returns to the caller, making the use of NEON mostly 103 | @ transparent (and avoiding hazards in the caller code) 104 | 105 | #ifdef ENABLE_UNALIGNED_MEM_ACCESSES 106 | movs r3, r2, lsl #29 107 | ldrcs r3, [r1], #4 108 | strcs r3, [ip], #4 109 | ldrcs r3, [r1], #4 110 | strcs r3, [ip], #4 111 | ldrmi r3, [r1], #4 112 | strmi r3, [ip], #4 113 | movs r2, r2, lsl #31 114 | ldrcsh r3, [r1], #2 115 | strcsh r3, [ip], #2 116 | ldrmib r3, [r1], #1 117 | strmib r3, [ip], #1 118 | #else 119 | movs r3, r2, lsl #29 120 | bcc 1f 121 | .rept 8 122 | ldrcsb r3, [r1], #1 123 | strcsb r3, [ip], #1 124 | .endr 125 | 1: 126 | bpl 1f 127 | .rept 4 128 | ldrmib r3, [r1], #1 129 | strmib r3, [ip], #1 130 | .endr 131 | 1: 132 | movs r2, r2, lsl #31 133 | ldrcsb r3, [r1], #1 134 | strcsb r3, [ip], #1 135 | ldrcsb r3, [r1], #1 136 | strcsb r3, [ip], #1 137 | ldrmib r3, [r1], #1 138 | strmib r3, [ip], #1 139 | #endif 140 | bx lr 141 | .fnend 142 | -------------------------------------------------------------------------------- /source/port.h: -------------------------------------------------------------------------------- 1 | #ifndef PORT_H 2 | #define PORT_H 3 | 4 | #ifdef __CELLOS_LV2__ 5 | /* PlayStation3 */ 6 | #include 7 | #endif 8 | 9 | #ifdef _XBOX360 10 | /* XBox 360 */ 11 | #include 12 | #endif 13 | 14 | #include "types.h" 15 | 16 | /* if a >= 0 return x else y*/ 17 | #define isel(a, x, y) ((x & (~(a >> 31))) + (y & (a >> 31))) 18 | 19 | #ifdef __SWITCH__ 20 | #define INLINE inline 21 | 22 | #define memalign_free(p) free(p) 23 | #define memalign_alloc_aligned(s) memalign(64, ((s) + 0x3F) & ~0x3F) 24 | #endif 25 | 26 | #ifdef FRONTEND_SUPPORTS_RGB565 27 | /* 16bit color - RGB565 */ 28 | #define RED_MASK 0xf800 29 | #define GREEN_MASK 0x7e0 30 | #define BLUE_MASK 0x1f 31 | #define RED_EXPAND 3 32 | #define GREEN_EXPAND 2 33 | #define BLUE_EXPAND 3 34 | #define RED_SHIFT 11 35 | #define GREEN_SHIFT 5 36 | #define BLUE_SHIFT 0 37 | #define CONVERT_COLOR(color) (((color & 0x001f) << 11) | ((color & 0x03e0) << 1) | ((color & 0x0200) >> 4) | ((color & 0x7c00) >> 10)) 38 | #else 39 | /* 16bit color - RGB555 */ 40 | #define RED_MASK 0x7c00 41 | #define GREEN_MASK 0x3e0 42 | #define BLUE_MASK 0x1f 43 | #define RED_EXPAND 3 44 | #define GREEN_EXPAND 3 45 | #define BLUE_EXPAND 3 46 | #define RED_SHIFT 10 47 | #define GREEN_SHIFT 5 48 | #define BLUE_SHIFT 0 49 | #define CONVERT_COLOR(color) ((((color & 0x1f) << 10) | (((color & 0x3e0) >> 5) << 5) | (((color & 0x7c00) >> 10))) & 0x7fff) 50 | #endif 51 | 52 | #ifdef _MSC_VER 53 | #include 54 | #define strcasecmp _stricmp 55 | #endif 56 | 57 | #ifdef USE_CACHE_PREFETCH 58 | #if defined(__ANDROID__) 59 | #define CACHE_PREFETCH(prefetch) prefetch(&prefetch); 60 | #elif defined(_XBOX) 61 | #define CACHE_PREFETCH(prefetch) __dcbt(0, &prefetch); 62 | #else 63 | #define CACHE_PREFETCH(prefetch) __dcbt(&prefetch); 64 | #endif 65 | #else 66 | #define CACHE_PREFETCH(prefetch) 67 | #endif 68 | 69 | #ifdef MSB_FIRST 70 | #if defined(__SNC__) 71 | #define READ16LE( base ) (__builtin_lhbrx((base), 0)) 72 | #define READ32LE( base ) (__builtin_lwbrx((base), 0)) 73 | #define WRITE16LE( base, value ) (__builtin_sthbrx((value), (base), 0)) 74 | #define WRITE32LE( base, value ) (__builtin_stwbrx((value), (base), 0)) 75 | #elif defined(__GNUC__) && defined(__ppc__) 76 | #define READ16LE( base ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (base), "0" (ppc_lhbrx_) ); ppc_lhbrx_;}) 77 | #define READ32LE( base ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (base), "0" (ppc_lwbrx_) ); ppc_lwbrx_;}) 78 | #define WRITE16LE( base, value) ({asm( "sthbrx %0,0,%1" : : "r" (value), "r" (base) );}) 79 | #define WRITE32LE( base, value) ({asm( "stwbrx %0,0,%1" : : "r" (value), "r" (base) );}) 80 | #elif defined(_XBOX360) 81 | #define READ16LE( base) _byteswap_ushort(*((u16 *)(base))) 82 | #define READ32LE( base) _byteswap_ulong(*((u32 *)(base))) 83 | #define WRITE16LE(base, value) *((u16 *)(base)) = _byteswap_ushort((value)) 84 | #define WRITE32LE(base, value) *((u32 *)(base)) = _byteswap_ulong((value)) 85 | #else 86 | #define READ16LE(x) (*((u16 *)(x))<<8)|(*((u16 *)(x))>>8); 87 | #define READ32LE(x) (*((u32 *)(x))<<24)|((*((u32 *)(x))<<8)&0xff0000)|((((*((u32 *)(x))(x)>>8)&0xff00)|(*((u32 *)(x))>>24); 88 | #define WRITE16LE(x,v) *((u16 *)(x)) = (*((u16 *)(v))<<8)|(*((u16 *)(v))>>8); 89 | #define WRITE32LE(x,v) *((u32 *)(x)) = ((v)<<24)|(((v)<<8)&0xff0000)|(((v)>>8)&0xff00)|((v)>>24); 90 | #endif 91 | #else 92 | #define READ16LE(x) *((u16 *)(x)) 93 | #define READ32LE(x) *((u32 *)(x)) 94 | #define WRITE16LE(x,v) *((u16 *)(x)) = (v) 95 | #define WRITE32LE(x,v) *((u32 *)(x)) = (v) 96 | #endif 97 | 98 | #ifdef INLINE 99 | #if defined(_MSC_VER) 100 | #define FORCE_INLINE __forceinline 101 | #elif defined(__GNUC__) 102 | #define FORCE_INLINE inline __attribute__((always_inline)) 103 | #else 104 | #define FORCE_INLINE INLINE 105 | #endif 106 | #else 107 | #define FORCE_INLINE 108 | #endif 109 | 110 | #endif // PORT_H 111 | -------------------------------------------------------------------------------- /source/sound.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_H 2 | #define SOUND_H 3 | 4 | #include "sound_blargg.h" 5 | 6 | #define SGCNT0_H 0x82 7 | #define FIFOA_L 0xa0 8 | #define FIFOA_H 0xa2 9 | #define FIFOB_L 0xa4 10 | #define FIFOB_H 0xa6 11 | 12 | #define BLIP_BUFFER_ACCURACY 16 13 | #define BLIP_PHASE_BITS 8 14 | #define BLIP_WIDEST_IMPULSE_ 16 15 | #define BLIP_BUFFER_EXTRA_ 18 16 | #define BLIP_RES 256 17 | #define BLIP_RES_MIN_ONE 255 18 | #define BLIP_SAMPLE_BITS 30 19 | #define BLIP_READER_DEFAULT_BASS 9 20 | #define BLIP_DEFAULT_LENGTH 250 /* 1/4th of a second */ 21 | 22 | #define BUFS_SIZE 3 23 | #define STEREO 2 24 | 25 | #define CLK_MUL GB_APU_OVERCLOCK 26 | #define CLK_MUL_MUL_2 8 27 | #define CLK_MUL_MUL_4 16 28 | #define CLK_MUL_MUL_6 24 29 | #define CLK_MUL_MUL_8 32 30 | #define CLK_MUL_MUL_15 60 31 | #define CLK_MUL_MUL_32 128 32 | #define DAC_BIAS 7 33 | 34 | #define PERIOD_MASK 0x70 35 | #define SHIFT_MASK 0x07 36 | 37 | #define PERIOD2_MASK 0x1FFFF 38 | 39 | #define BANK40_MASK 0x40 40 | #define BANK_SIZE 32 41 | #define BANK_SIZE_MIN_ONE 31 42 | #define BANK_SIZE_DIV_TWO 16 43 | 44 | /* 11-bit frequency in NRx3 and NRx4*/ 45 | #define GB_OSC_FREQUENCY() (((regs[4] & 7) << 8) + regs[3]) 46 | 47 | #define WAVE_TYPE 0x100 48 | #define NOISE_TYPE 0x200 49 | #define MIXED_TYPE WAVE_TYPE | NOISE_TYPE 50 | #define TYPE_INDEX_MASK 0xFF 51 | 52 | struct blip_buffer_state_t 53 | { 54 | uint32_t offset_; 55 | int32_t reader_accum_; 56 | int32_t buf [BLIP_BUFFER_EXTRA_]; 57 | }; 58 | 59 | 60 | /* Begins reading from buffer. Name should be unique to the current block.*/ 61 | #define BLIP_READER_BEGIN( name, blip_buffer ) \ 62 | const int32_t * name##_reader_buf = (blip_buffer).buffer_;\ 63 | int32_t name##_reader_accum = (blip_buffer).reader_accum_ 64 | 65 | /* Advances to next sample*/ 66 | #define BLIP_READER_NEXT( name, bass ) \ 67 | (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) 68 | 69 | /* Ends reading samples from buffer. The number of samples read must now be removed 70 | using Blip_Buffer::remove_samples(). */ 71 | #define BLIP_READER_END( name, blip_buffer ) \ 72 | (void) ((blip_buffer).reader_accum_ = name##_reader_accum) 73 | 74 | #define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset) 75 | 76 | #define BLIP_READER_NEXT_IDX_( name, idx ) {\ 77 | name##_reader_accum -= name##_reader_accum >> BLIP_READER_DEFAULT_BASS;\ 78 | name##_reader_accum += name##_reader_buf [(idx)];\ 79 | } 80 | 81 | #define BLIP_READER_NEXT_RAW_IDX_( name, idx ) {\ 82 | name##_reader_accum -= name##_reader_accum >> BLIP_READER_DEFAULT_BASS; \ 83 | name##_reader_accum += *(int32_t const*) ((char const*) name##_reader_buf + (idx)); \ 84 | } 85 | 86 | #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ 87 | defined (__x86_64__) || defined (__ia64__) || defined (__i386__) 88 | #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in 89 | #else 90 | #define BLIP_CLAMP_( in ) (int16_t) in != in 91 | #endif 92 | 93 | /* Clamp sample to int16_t range */ 94 | #define BLIP_CLAMP( sample, out ) { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 24) ^ 0x7FFF; } 95 | #define GB_ENV_DAC_ENABLED() (regs[2] & 0xF8) /* Non-zero if DAC is enabled*/ 96 | #define GB_NOISE_PERIOD2_INDEX() (regs[3] >> 4) 97 | #define GB_NOISE_PERIOD2(base) (base << GB_NOISE_PERIOD2_INDEX()) 98 | #define GB_NOISE_LFSR_MASK() ((regs[3] & 0x08) ? ~0x4040 : ~0x4000) 99 | #define GB_WAVE_DAC_ENABLED() (regs[0] & 0x80) /* Non-zero if DAC is enabled*/ 100 | 101 | #define reload_sweep_timer() \ 102 | sweep_delay = (regs [0] & PERIOD_MASK) >> 4; \ 103 | if ( !sweep_delay ) \ 104 | sweep_delay = 8; 105 | 106 | 107 | 108 | void soundSetVolume( float ); 109 | 110 | // Manages muting bitmask. The bits control the following channels: 111 | // 0x001 Pulse 1 112 | // 0x002 Pulse 2 113 | // 0x004 Wave 114 | // 0x008 Noise 115 | // 0x100 PCM 1 116 | // 0x200 PCM 2 117 | void soundPause (void); 118 | void soundResume (void); 119 | void soundSetSampleRate(long sampleRate); 120 | void soundReset (void); 121 | void soundEvent_u8( int gb_addr, uint32_t addr, uint8_t data ); 122 | void soundEvent_u8_parallel(int gb_addr[], uint32_t address[], uint8_t data[]); 123 | void soundEvent_u16( uint32_t addr, uint16_t data ); 124 | void soundTimerOverflow( int which ); 125 | void process_sound_tick_fn (void); 126 | void soundSaveGameMem(uint8_t *& data); 127 | void soundReadGameMem(const uint8_t *& data, int version); 128 | 129 | extern int SOUND_CLOCK_TICKS; // Number of 16.8 MHz clocks between calls to soundTick() 130 | extern int soundTicks; // Number of 16.8 MHz clocks until soundTick() will be called 131 | 132 | #endif // SOUND_H 133 | -------------------------------------------------------------------------------- /source/sound_blargg.h: -------------------------------------------------------------------------------- 1 | /* Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in 2 | a Game Boy Advance emulator. */ 3 | #define GB_APU_OVERCLOCK 4 4 | 5 | #ifndef STATIC_CAST 6 | #if __GNUC__ >= 4 7 | #define STATIC_CAST(T,expr) static_cast (expr) 8 | #define CONST_CAST( T,expr) const_cast (expr) 9 | #else 10 | #define STATIC_CAST(T,expr) ((T) (expr)) 11 | #define CONST_CAST( T,expr) ((T) (expr)) 12 | #endif 13 | #endif 14 | 15 | // BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, 16 | // compiler is assumed to support bool. If undefined, availability is determined. 17 | #ifndef BLARGG_COMPILER_HAS_BOOL 18 | #if defined (__MWERKS__) 19 | #if !__option(bool) 20 | #define BLARGG_COMPILER_HAS_BOOL 0 21 | #endif 22 | #elif defined (_MSC_VER) 23 | #if _MSC_VER < 1100 24 | #define BLARGG_COMPILER_HAS_BOOL 0 25 | #endif 26 | #elif defined (__GNUC__) 27 | // supports bool 28 | #elif __cplusplus < 199711 29 | #define BLARGG_COMPILER_HAS_BOOL 0 30 | #endif 31 | #endif 32 | #if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL 33 | typedef int bool; 34 | const bool true = 1; 35 | const bool false = 0; 36 | #endif 37 | 38 | /* HAVE_STDINT_H: If defined, use for int8_t etc.*/ 39 | #if defined (HAVE_STDINT_H) 40 | #include 41 | /* HAVE_INTTYPES_H: If defined, use for int8_t etc.*/ 42 | #elif defined (HAVE_INTTYPES_H) 43 | #include 44 | #endif 45 | 46 | // If expr yields non-NULL error string, returns it from current function, 47 | // otherwise continues normally. 48 | #undef RETURN_ERR 49 | #define RETURN_ERR( expr ) do { \ 50 | const char * blargg_return_err_ = (expr); \ 51 | if ( blargg_return_err_ ) return blargg_return_err_; \ 52 | } while ( 0 ) 53 | -------------------------------------------------------------------------------- /source/switch/clickable.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Checkpoint 3 | * Copyright (C) 2017-2018 Bernardo Giordano 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | #include "clickable.h" 28 | 29 | bool Clickable::held() 30 | { 31 | touchPosition touch; 32 | hidTouchRead(&touch, 0); 33 | return ((hidKeysHeld(CONTROLLER_P1_AUTO) & KEY_TOUCH) && (int)touch.px > mx && (int)touch.px < mx+mw && (int)touch.py > my && (int)touch.py < my+mh); 34 | } 35 | 36 | bool Clickable::released(void) 37 | { 38 | touchPosition touch; 39 | hidTouchRead(&touch, 0); 40 | const bool on = (int)touch.px > mx && (int)touch.px < mx+mw && (int)touch.py > my && (int)touch.py < my+mh; 41 | 42 | if (on) 43 | { 44 | mOldPressed = true; 45 | } 46 | else 47 | { 48 | if (mOldPressed && !(touch.px > 0 || touch.py > 0)) 49 | { 50 | mOldPressed = false; 51 | return true; 52 | } 53 | mOldPressed = false; 54 | } 55 | 56 | return false; 57 | } 58 | 59 | void Clickable::draw(void) 60 | { 61 | u32 textw, texth; 62 | getTextDimensions(font24, mText.c_str(), &textw, &texth); 63 | const u32 messageWidth = mCentered ? textw : mw - 8; 64 | bool hld = held(); 65 | const color_t bgColor = hld ? mColorText : mColorBg; 66 | const color_t msgColor = hld ? mColorBg : mColorText; 67 | 68 | drawRect(mx, my, mw, mh, bgColor); 69 | drawText(font24, mx + (mw - messageWidth)/2, my + (mh - texth)/2, msgColor, mText.c_str()); 70 | } 71 | 72 | void Clickable::draw(float font) 73 | { 74 | u32 textw, texth, dotlen; 75 | getTextDimensions(font, mText.c_str(), &textw, &texth); 76 | getTextDimensions(font, "...", &dotlen, NULL); 77 | const u32 messageWidth = mCentered ? textw : mw - 8; 78 | 79 | drawRect(mx, my, mw, mh, mColorBg); 80 | drawTextTruncate(font, mx + (mw - messageWidth)/2, my + (mh - texth)/2, mColorText, mw - 4*2 - 40 - dotlen, "...", mText.c_str()); 81 | } -------------------------------------------------------------------------------- /source/switch/clickable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Checkpoint 3 | * Copyright (C) 2017-2018 Bernardo Giordano 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | #ifndef CLICKABLE_HPP 28 | #define CLICKABLE_HPP 29 | 30 | #include 31 | #include 32 | #include "iclickable.h" 33 | #include "draw.h" 34 | 35 | class Clickable : public IClickable 36 | { 37 | public: 38 | Clickable(int x, int y, u16 w, u16 h, color_t colorBg, color_t colorText, const std::string& message, bool centered) 39 | : IClickable(x, y, w, h, colorBg, colorText, message, centered) { } 40 | virtual ~Clickable(void) { }; 41 | 42 | void draw(void) override; 43 | void draw(float font) override; 44 | bool held(void) override; 45 | bool released(void) override; 46 | }; 47 | 48 | #endif -------------------------------------------------------------------------------- /source/switch/colors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "draw.h" 4 | 5 | #define WHITE MakeColor(255, 255, 255, 255) 6 | #define BLACK MakeColor(0, 0, 0, 255) -------------------------------------------------------------------------------- /source/switch/draw.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2018 nx-hbmenu Authors 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any purpose 5 | * with or without fee is hereby granted, provided that the above copyright notice 6 | * and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 11 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 12 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 13 | * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #include "draw.h" 17 | 18 | #include 19 | 20 | static FT_Error s_font_libret = 1; 21 | static FT_Error s_font_facesret[FONT_FACES_MAX]; 22 | static FT_Library s_font_library; 23 | static FT_Face s_font_faces[FONT_FACES_MAX]; 24 | static FT_Face s_font_lastusedface; 25 | static size_t s_font_faces_total = 0; 26 | static u64 s_textLanguageCode = 0; 27 | 28 | Result textInit(void) { 29 | Result res = setInitialize(); 30 | if (R_SUCCEEDED(res)) { 31 | res = setGetSystemLanguage(&s_textLanguageCode); 32 | } 33 | setExit(); 34 | return res; 35 | } 36 | 37 | static bool FontSetType(u32 scale) { 38 | FT_Error ret = 0; 39 | for (u32 i = 0; i < s_font_faces_total; i++) { 40 | ret = FT_Set_Char_Size(s_font_faces[i], 0, scale * 64, 300, 300); 41 | if (ret) return false; 42 | } 43 | return true; 44 | } 45 | 46 | static inline bool FontLoadGlyph(glyph_t* glyph, u32 font, u32 codepoint) { 47 | FT_Face face; 48 | FT_Error ret = 0; 49 | FT_GlyphSlot slot; 50 | FT_UInt glyph_index; 51 | FT_Bitmap* bitmap; 52 | 53 | if (s_font_faces_total == 0) return false; 54 | 55 | for (u32 i = 0; i < s_font_faces_total; i++) { 56 | face = s_font_faces[i]; 57 | s_font_lastusedface = face; 58 | glyph_index = FT_Get_Char_Index(face, codepoint); 59 | if (glyph_index == 0) continue; 60 | 61 | ret = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); 62 | if (ret == 0) { 63 | ret = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); 64 | } 65 | if (ret) return false; 66 | 67 | break; 68 | } 69 | 70 | slot = face->glyph; 71 | bitmap = &slot->bitmap; 72 | 73 | glyph->width = bitmap->width; 74 | glyph->height = bitmap->rows; 75 | glyph->pitch = bitmap->pitch; 76 | glyph->data = bitmap->buffer; 77 | glyph->advance = slot->advance.x >> 6; 78 | glyph->posX = slot->bitmap_left; 79 | glyph->posY = slot->bitmap_top; 80 | return true; 81 | } 82 | 83 | static void DrawGlyph(u32 x, u32 y, color_t clr, const glyph_t* glyph) { 84 | const u8* data = glyph->data; 85 | x += glyph->posX; 86 | y -= glyph->posY; 87 | 88 | for (u32 j = 0; j < glyph->height; j++) { 89 | for (u32 i = 0; i < glyph->width; i++) { 90 | color_t modulatedColor = clr; 91 | modulatedColor.a = ((u32)data[i] * (u32)clr.a) / 255; 92 | if (!modulatedColor.a) continue; 93 | DrawPixel(x + i, y + j, modulatedColor); 94 | } 95 | data += glyph->pitch; 96 | } 97 | } 98 | 99 | static inline u8 DecodeByte(const char** ptr) { 100 | u8 c = (u8) * *ptr; 101 | *ptr += 1; 102 | return c; 103 | } 104 | 105 | // UTF-8 code adapted from http://www.json.org/JSON_checker/utf8_decode.c 106 | static inline int8_t DecodeUTF8Cont(const char** ptr) { 107 | int c = DecodeByte(ptr); 108 | return ((c & 0xC0) == 0x80) ? (c & 0x3F) : -1; 109 | } 110 | 111 | static inline u32 DecodeUTF8(const char** ptr) { 112 | u32 r; 113 | u8 c; 114 | int8_t c1, c2, c3; 115 | 116 | c = DecodeByte(ptr); 117 | if ((c & 0x80) == 0) return c; 118 | if ((c & 0xE0) == 0xC0) { 119 | c1 = DecodeUTF8Cont(ptr); 120 | if (c1 >= 0) { 121 | r = ((c & 0x1F) << 6) | c1; 122 | if (r >= 0x80) return r; 123 | } 124 | } else if ((c & 0xF0) == 0xE0) { 125 | c1 = DecodeUTF8Cont(ptr); 126 | if (c1 >= 0) { 127 | c2 = DecodeUTF8Cont(ptr); 128 | if (c2 >= 0) { 129 | r = ((c & 0x0F) << 12) | (c1 << 6) | c2; 130 | if (r >= 0x800 && (r < 0xD800 || r >= 0xE000)) return r; 131 | } 132 | } 133 | } else if ((c & 0xF8) == 0xF0) { 134 | c1 = DecodeUTF8Cont(ptr); 135 | if (c1 >= 0) { 136 | c2 = DecodeUTF8Cont(ptr); 137 | if (c2 >= 0) { 138 | c3 = DecodeUTF8Cont(ptr); 139 | if (c3 >= 0) { 140 | r = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3; 141 | if (r >= 0x10000 && r < 0x110000) return r; 142 | } 143 | } 144 | } 145 | } 146 | return 0xFFFD; 147 | } 148 | 149 | static void drawText_(u32 font, u32 x, u32 y, color_t clr, const char* text, u32 max_width, const char* end_text) { 150 | u32 origX = x; 151 | if (s_font_faces_total == 0) return; 152 | if (!FontSetType(font)) return; 153 | s_font_lastusedface = s_font_faces[0]; 154 | 155 | while (*text) { 156 | if (max_width && x - origX >= max_width) { 157 | text = end_text; 158 | max_width = 0; 159 | } 160 | 161 | glyph_t glyph; 162 | u32 codepoint = DecodeUTF8(&text); 163 | 164 | if (codepoint == '\n') { 165 | if (max_width) { 166 | text = end_text; 167 | max_width = 0; 168 | continue; 169 | } 170 | 171 | x = origX; 172 | y += s_font_lastusedface->size->metrics.height / 64; 173 | continue; 174 | } 175 | 176 | if (!FontLoadGlyph(&glyph, font, codepoint)) { 177 | if (!FontLoadGlyph(&glyph, font, '?')) continue; 178 | } 179 | 180 | DrawGlyph(x, y + font * 3, clr, &glyph); 181 | x += glyph.advance; 182 | } 183 | } 184 | 185 | void drawText(u32 font, u32 x, u32 y, color_t clr, const char* text, ...) { 186 | char buffer[256]; 187 | va_list args; 188 | va_start(args, text); 189 | vsnprintf(buffer, sizeof(buffer) / sizeof(char), text, args); 190 | va_end(args); 191 | drawText_(font, x, y, clr, buffer, 0, NULL); 192 | } 193 | 194 | void drawTextTruncate(u32 font, u32 x, u32 y, color_t clr, u32 max_width, const char* end_text, const char* text, ...) { 195 | char buffer[256]; 196 | va_list args; 197 | va_start(args, text); 198 | vsnprintf(buffer, sizeof(buffer) / sizeof(char), text, args); 199 | va_end(args); 200 | drawText_(font, x, y, clr, text, max_width, end_text); 201 | } 202 | 203 | void getTextDimensions(u32 font, const char* text, u32* width_out, u32* height_out) { 204 | if (s_font_faces_total == 0) return; 205 | if (!FontSetType(font)) return; 206 | s_font_lastusedface = s_font_faces[0]; 207 | u32 x = 0; 208 | u32 width = 0, height = s_font_lastusedface->size->metrics.height / 64 - font * 3; 209 | 210 | while (*text) { 211 | glyph_t glyph; 212 | u32 codepoint = DecodeUTF8(&text); 213 | 214 | if (codepoint == '\n') { 215 | x = 0; 216 | height += s_font_lastusedface->size->metrics.height / 64; 217 | height -= font * 3; 218 | continue; 219 | } 220 | 221 | if (!FontLoadGlyph(&glyph, font, codepoint)) { 222 | if (!FontLoadGlyph(&glyph, font, '?')) continue; 223 | } 224 | 225 | x += glyph.advance; 226 | 227 | if (x > width) width = x; 228 | } 229 | 230 | if (width_out) *width_out = width; 231 | if (height_out) *height_out = height; 232 | } 233 | 234 | bool fontInit(void) { 235 | FT_Error ret = 0; 236 | 237 | for (u32 i = 0; i < FONT_FACES_MAX; i++) { 238 | s_font_facesret[i] = 1; 239 | } 240 | 241 | ret = FT_Init_FreeType(&s_font_library); 242 | s_font_libret = ret; 243 | if (s_font_libret) return false; 244 | 245 | PlFontData fonts[PlSharedFontType_Total]; 246 | Result rc = 0; 247 | rc = plGetSharedFont(s_textLanguageCode, fonts, FONT_FACES_MAX, &s_font_faces_total); 248 | if (R_FAILED(rc)) return false; 249 | 250 | for (u32 i = 0; i < s_font_faces_total; i++) { 251 | ret = FT_New_Memory_Face(s_font_library, (const FT_Byte*)fonts[i].address, fonts[i].size, 0, &s_font_faces[i]); 252 | 253 | s_font_facesret[i] = ret; 254 | if (ret) return false; 255 | } 256 | 257 | return true; 258 | } 259 | 260 | void fontExit() { 261 | for (u32 i = 0; i < s_font_faces_total; i++) { 262 | if (s_font_facesret[i] == 0) { 263 | FT_Done_Face(s_font_faces[i]); 264 | } 265 | } 266 | 267 | if (s_font_libret == 0) { 268 | FT_Done_FreeType(s_font_library); 269 | } 270 | } 271 | 272 | void drawRect(u32 x, u32 y, u32 w, u32 h, color_t color) { 273 | if (x + w > currentFBWidth) w = (x + w) - currentFBWidth; 274 | if (y + h > currentFBHeight) h = (y + h) - currentFBHeight; 275 | 276 | u32 mulR = (u32)color.r * (u32)color.a; 277 | u32 mulG = (u32)color.g * (u32)color.a; 278 | u32 mulB = (u32)color.b * (u32)color.a; 279 | u32 oneMinusAlpha = 255 - color.a; 280 | 281 | uint16x8_t alpha = vdupq_n_u16((u16)color.a); 282 | 283 | uint16x8_t vR = vdupq_n_u16((u16)mulR); 284 | uint16x8_t vG = vdupq_n_u16((u16)mulG); 285 | uint16x8_t vB = vdupq_n_u16((u16)mulB); 286 | 287 | uint16x8_t vOneMinusA = vdupq_n_u16(oneMinusAlpha); 288 | uint8x8_t fullAlpha = vdup_n_u8(255); 289 | 290 | u8* dst = currentFB + ((x + y * currentFBWidth) * 4); 291 | for (u32 j = 0; j < h; j++) { 292 | for (u32 i = 0; i < w / 8; i++) { 293 | uint8x8x4_t screenPixels = vld4_u8(&dst[i * 8 * 4]); 294 | screenPixels.val[0] = 295 | vmovn_u16(vshrq_n_u16(vaddq_u16(vmulq_u16(vmovl_u8(screenPixels.val[0]), vOneMinusA), vR), 8)); 296 | screenPixels.val[1] = 297 | vmovn_u16(vshrq_n_u16(vaddq_u16(vmulq_u16(vmovl_u8(screenPixels.val[1]), vOneMinusA), vG), 8)); 298 | screenPixels.val[2] = 299 | vmovn_u16(vshrq_n_u16(vaddq_u16(vmulq_u16(vmovl_u8(screenPixels.val[2]), vOneMinusA), vB), 8)); 300 | screenPixels.val[3] = fullAlpha; 301 | 302 | vst4_u8(&dst[i * 8 * 4], screenPixels); 303 | } 304 | 305 | for (u32 i = (w / 8) * 8; i < w; i++) { 306 | int baseIdx = i * 4; 307 | dst[baseIdx + 0] = ((u32)dst[baseIdx + 0] * oneMinusAlpha + mulR) >> 8; 308 | dst[baseIdx + 1] = ((u32)dst[baseIdx + 1] * oneMinusAlpha + mulG) >> 8; 309 | dst[baseIdx + 2] = ((u32)dst[baseIdx + 2] * oneMinusAlpha + mulB) >> 8; 310 | dst[baseIdx + 3] = 255; 311 | } 312 | 313 | dst += currentFBWidth * 4; 314 | } 315 | } 316 | 317 | void drawRectRaw(u32 x, u32 y, u32 w, u32 h, color_t color) { 318 | if (x + w > currentFBWidth) w = (x + w) - currentFBWidth; 319 | if (y + h > currentFBHeight) h = (y + h) - currentFBHeight; 320 | 321 | u32 color32 = color.r | (color.g << 8) | (color.b << 16) | (0xff << 24); 322 | u128 val = color32 | ((u128)color32 << 32) | ((u128)color32 << 64) | ((u128)color32 << 96); 323 | 324 | u32* dst = (u32*)(currentFB + (y * currentFBWidth + x) * 4); 325 | for (u32 j = 0; j < h; j++) { 326 | for (u32 i = 0; i < w; i += 4) { 327 | *((u128*)&dst[i]) = val; 328 | } 329 | 330 | dst += currentFBWidth; 331 | } 332 | } -------------------------------------------------------------------------------- /source/switch/draw.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2018 nx-hbmenu Authors 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any purpose 5 | * with or without fee is hereby granted, provided that the above copyright notice 6 | * and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10 | * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 11 | * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA 12 | * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 13 | * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include FT_FREETYPE_H 20 | #include "types.h" 21 | #include 22 | 23 | extern u8 *currentFB; 24 | extern u32 currentFBWidth; 25 | extern u32 currentFBHeight; 26 | 27 | #define FONT_FACES_MAX PlSharedFontType_Total 28 | 29 | #define COLOR_WHITE MakeColor(255, 255, 255, 255) 30 | #define COLOR_BLACK MakeColor(0, 0, 0, 255) 31 | #define COLOR_BLUE MakeColor(0, 0, 255, 255) 32 | #define COLOR_GREEN MakeColor(0, 255, 201, 255) 33 | 34 | #define COLOR_GREY_DARKER MakeColor(70, 70, 70, 255) 35 | #define COLOR_GREY_DARK MakeColor(79, 79, 79, 255) 36 | #define COLOR_GREY_MEDIUM MakeColor(94, 94, 94, 255) 37 | #define COLOR_GREY_LIGHT MakeColor(138, 138, 138, 255) 38 | 39 | enum 40 | { 41 | font24 = 7, 42 | font16 = 5, 43 | font14 = 4, 44 | fontSizesCount = 3 45 | }; 46 | 47 | extern u8* currentFB; 48 | extern u32 currentFBWidth; 49 | extern u32 currentFBHeight; 50 | 51 | // the following code is from nx-hbmenu 52 | // https://github.com/switchbrew/nx-hbmenu/blob/master/common/common.h#L63 53 | 54 | static inline u8 BlendColor(u32 src, u32 dst, u8 alpha) { 55 | u8 one_minus_alpha = (u8)255 - alpha; 56 | return (dst * alpha + src * one_minus_alpha) / (u8)255; 57 | } 58 | 59 | static inline color_t MakeColor(u8 r, u8 g, u8 b, u8 a) { 60 | color_t clr; 61 | clr.r = r; 62 | clr.g = g; 63 | clr.b = b; 64 | clr.a = a; 65 | return clr; 66 | } 67 | 68 | static inline void DrawPixel(u32 x, u32 y, color_t clr) { 69 | if (x >= 1280 || y >= 720) return; 70 | u32 off = (y * currentFBWidth + x) * 4; 71 | currentFB[off] = BlendColor(currentFB[off], clr.r, clr.a); 72 | off++; 73 | currentFB[off] = BlendColor(currentFB[off], clr.g, clr.a); 74 | off++; 75 | currentFB[off] = BlendColor(currentFB[off], clr.b, clr.a); 76 | off++; 77 | currentFB[off] = 0xff; 78 | } 79 | 80 | static inline void Draw4PixelsRaw(u32 x, u32 y, color_t clr) { 81 | if (x >= 1280 || y >= 720 || x > 1280 - 4) return; 82 | 83 | u32 color = clr.r | (clr.g << 8) | (clr.b << 16) | (0xff << 24); 84 | u128 val = color | ((u128)color << 32) | ((u128)color << 64) | ((u128)color << 96); 85 | u32 off = (y * currentFBWidth + x) * 4; 86 | *((u128*)¤tFB[off]) = val; 87 | } 88 | 89 | Result textInit(void); 90 | bool fontInit(void); 91 | void fontExit(); 92 | 93 | void drawRect(u32 x, u32 y, u32 w, u32 h, color_t color); 94 | void drawRectRaw(u32 x, u32 y, u32 w, u32 h, color_t color); 95 | void drawPixel(u32 x, u32 y, color_t clr); 96 | void drawText(u32 font, u32 x, u32 y, color_t clr, const char* text, ...); 97 | void drawTextTruncate(u32 font, u32 x, u32 y, color_t clr, u32 max_width, const char* end_text, const char* text, ...); 98 | void getTextDimensions(u32 font, const char* text, u32* width_out, u32* height_out); -------------------------------------------------------------------------------- /source/switch/gbaover.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | char romtitle[256]; 3 | char romid[5]; 4 | int flashSize; 5 | int saveType; 6 | int rtcEnabled; 7 | int mirroringEnabled; 8 | int useBios; 9 | } ini_t; 10 | 11 | static const ini_t gbaover[256] = { 12 | // romtitle, romid flash save rtc mirror bios 13 | {"2 Games in 1 - Dragon Ball Z - The Legacy of Goku I & II (USA)", "BLFE", 0, 1, 0, 0, 0}, 14 | {"2 Games in 1 - Dragon Ball Z - Buu's Fury + Dragon Ball GT - Transformation (USA)", "BUFE", 0, 1, 0, 0, 0}, 15 | {"Boktai - The Sun Is in Your Hand (Europe)(En,Fr,De,Es,It)", "U3IP", 0, 0, 1, 0, 0}, 16 | {"Boktai - The Sun Is in Your Hand (USA)", "U3IE", 0, 0, 1, 0, 0}, 17 | {"Boktai 2 - Solar Boy Django (USA)", "U32E", 0, 0, 1, 0, 0}, 18 | {"Boktai 2 - Solar Boy Django (Europe)(En,Fr,De,Es,It)", "U32P", 0, 0, 1, 0, 0}, 19 | {"Bokura no Taiyou - Taiyou Action RPG (Japan)", "U3IJ", 0, 0, 1, 0, 0}, 20 | {"Card e-Reader+ (Japan)", "PSAJ", 131072, 0, 0, 0, 0}, 21 | {"Classic NES Series - Bomberman (USA, Europe)", "FBME", 0, 1, 0, 1, 0}, 22 | {"Classic NES Series - Castlevania (USA, Europe)", "FADE", 0, 1, 0, 1, 0}, 23 | {"Classic NES Series - Donkey Kong (USA, Europe)", "FDKE", 0, 1, 0, 1, 0}, 24 | {"Classic NES Series - Dr. Mario (USA, Europe)", "FDME", 0, 1, 0, 1, 0}, 25 | {"Classic NES Series - Excitebike (USA, Europe)", "FEBE", 0, 1, 0, 1, 0}, 26 | {"Classic NES Series - Legend of Zelda (USA, Europe)", "FZLE", 0, 1, 0, 1, 0}, 27 | {"Classic NES Series - Ice Climber (USA, Europe)", "FICE", 0, 1, 0, 1, 0}, 28 | {"Classic NES Series - Metroid (USA, Europe)", "FMRE", 0, 1, 0, 1, 0}, 29 | {"Classic NES Series - Pac-Man (USA, Europe)", "FP7E", 0, 1, 0, 1, 0}, 30 | {"Classic NES Series - Super Mario Bros. (USA, Europe)", "FSME", 0, 1, 0, 1, 0}, 31 | {"Classic NES Series - Xevious (USA, Europe)", "FXVE", 0, 1, 0, 1, 0}, 32 | {"Classic NES Series - Zelda II - The Adventure of Link (USA, Europe)", "FLBE", 0, 1, 0, 1, 0}, 33 | {"Digi Communication 2 - Datou! Black Gemagema Dan (Japan)", "BDKJ", 0, 1, 0, 0, 0}, 34 | {"e-Reader (USA)", "PSAE", 131072, 0, 0, 0, 0}, 35 | {"Dragon Ball GT - Transformation (USA)", "BT4E", 0, 1, 0, 0, 0}, 36 | {"Dragon Ball Z - Buu's Fury (USA)", "BG3E", 0, 1, 0, 0, 0}, 37 | {"Dragon Ball Z - Taiketsu (Europe)(En,Fr,De,Es,It)", "BDBP", 0, 1, 0, 0, 0}, 38 | {"Dragon Ball Z - Taiketsu (USA)", "BDBE", 0, 1, 0, 0, 0}, 39 | {"Dragon Ball Z - The Legacy of Goku II International (Japan)", "ALFJ", 0, 1, 0, 0, 0}, 40 | {"Dragon Ball Z - The Legacy of Goku II (Europe)(En,Fr,De,Es,It)", "ALFP", 0, 1, 0, 0, 0}, 41 | {"Dragon Ball Z - The Legacy of Goku II (USA)", "ALFE", 0, 1, 0, 0, 0}, 42 | {"Dragon Ball Z - The Legacy Of Goku (Europe)(En,Fr,De,Es,It)", "ALGP", 0, 1, 0, 0, 0}, 43 | {"Dragon Ball Z - The Legacy of Goku (USA)", "ALGE", 131072, 1, 0, 0, 0}, 44 | {"F-Zero - Climax (Japan)", "BFTJ", 131072, 0, 0, 0, 0}, 45 | {"Famicom Mini Vol. 01 - Super Mario Bros. (Japan)", "FMBJ", 0, 1, 0, 1, 0}, 46 | {"Famicom Mini Vol. 12 - Clu Clu Land (Japan)", "FCLJ", 0, 1, 0, 1, 0}, 47 | {"Famicom Mini Vol. 13 - Balloon Fight (Japan)", "FBFJ", 0, 1, 0, 1, 0}, 48 | {"Famicom Mini Vol. 14 - Wrecking Crew (Japan)", "FWCJ", 0, 1, 0, 1, 0}, 49 | {"Famicom Mini Vol. 15 - Dr. Mario (Japan)", "FDMJ", 0, 1, 0, 1, 0}, 50 | {"Famicom Mini Vol. 16 - Dig Dug (Japan)", "FTBJ", 0, 1, 0, 1, 0}, 51 | {"Famicom Mini Vol. 17 - Takahashi Meijin no Boukenjima (Japan)", "FTBJ", 0, 1, 0, 1, 0}, 52 | {"Famicom Mini Vol. 18 - Makaimura (Japan)", "FMKJ", 0, 1, 0, 1, 0}, 53 | {"Famicom Mini Vol. 19 - Twin Bee (Japan)", "FTWJ", 0, 1, 0, 1, 0}, 54 | {"Famicom Mini Vol. 20 - Ganbare Goemon! Karakuri Douchuu (Japan)", "FGGJ", 0, 1, 0, 1, 0}, 55 | {"Famicom Mini Vol. 21 - Super Mario Bros. 2 (Japan)", "FM2J", 0, 1, 0, 1, 0}, 56 | {"Famicom Mini Vol. 22 - Nazo no Murasame Jou (Japan)", "FNMJ", 0, 1, 0, 1, 0}, 57 | {"Famicom Mini Vol. 23 - Metroid (Japan)", "FMRJ", 0, 1, 0, 1, 0}, 58 | {"Famicom Mini Vol. 24 - Hikari Shinwa - Palthena no Kagami (Japan)", "FPTJ", 0, 1, 0, 1, 0}, 59 | {"Famicom Mini Vol. 25 - The Legend of Zelda 2 - Link no Bouken (Japan)", "FLBJ", 0, 1, 0, 1, 0}, 60 | {"Famicom Mini Vol. 26 - Famicom Mukashi Banashi - Shin Onigashima - Zen Kou Hen (Japan)", "FFMJ", 0, 1, 0, 1, 0}, 61 | {"Famicom Mini Vol. 27 - Famicom Tantei Club - Kieta Koukeisha - Zen Kou Hen (Japan)", "FTKJ", 0, 1, 0, 1, 0}, 62 | {"Famicom Mini Vol. 28 - Famicom Tantei Club Part II - Ushiro ni Tatsu Shoujo - Zen Kou Hen (Japan)", "FTUJ", 0, 1, 0, 1, 0}, 63 | {"Famicom Mini Vol. 29 - Akumajou Dracula (Japan)", "FADJ", 0, 1, 0, 1, 0}, 64 | {"Famicom Mini Vol. 30 - SD Gundam World - Gachapon Senshi Scramble Wars (Japan)", "FSDJ", 0, 1, 0, 1, 0}, 65 | {"Game Boy Wars Advance 1+2 (Japan)", "BGWJ", 131072, 0, 0, 0, 0}, 66 | {"Golden Sun - The Lost Age (USA)", "AGFE", 65536, 0, 0, 1, 0}, 67 | {"Golden Sun (USA)", "AGSE", 65536, 0, 0, 1, 0}, 68 | {"Iridion II (Europe) (En,Fr,De)", "AI2P", 0, 5, 0, 0, 0}, 69 | {"Iridion II (USA)", "AI2E", 0, 5, 0, 0, 0}, 70 | {"Koro Koro Puzzle - Happy Panechu! (Japan)", "KHPJ", 0, 4, 0, 0, 0}, 71 | {"Mario vs. Donkey Kong (Europe)", "BM5P", 0, 3, 0, 0, 0}, 72 | {"Pocket Monsters - Emerald (Japan)", "BPEJ", 131072, 0, 1, 0, 0}, 73 | {"Pocket Monsters - Fire Red (Japan)", "BPRJ", 131072, 0, 0, 0, 0}, 74 | {"Pocket Monsters - Leaf Green (Japan)", "BPGJ", 131072, 0, 0, 0, 0}, 75 | {"Pocket Monsters - Ruby (Japan)", "AXVJ", 131072, 0, 1, 0, 0}, 76 | {"Pocket Monsters - Sapphire (Japan)", "AXPJ", 131072, 0, 1, 0, 0}, 77 | {"Pokemon Mystery Dungeon - Red Rescue Team (USA, Australia)", "B24E", 131072, 0, 0, 0, 0}, 78 | {"Pokemon Mystery Dungeon - Red Rescue Team (En,Fr,De,Es,It)", "B24P", 131072, 0, 0, 0, 0}, 79 | {"Pokemon - Blattgruene Edition (Germany)", "BPGD", 131072, 0, 0, 0, 0}, 80 | {"Pokemon - Edicion Rubi (Spain)", "AXVS", 131072, 0, 1, 0, 0}, 81 | {"Pokemon - Edicion Esmeralda (Spain)", "BPES", 131072, 0, 1, 0, 0}, 82 | {"Pokemon - Edicion Rojo Fuego (Spain)", "BPRS", 131072, 1, 0, 0, 0}, 83 | {"Pokemon - Edicion Verde Hoja (Spain)", "BPGS", 131072, 1, 0, 0, 0}, 84 | {"Pokemon - Eidicion Zafiro (Spain)", "AXPS", 131072, 0, 1, 0, 0}, 85 | {"Pokemon - Emerald Version (USA, Europe)", "BPEE", 131072, 0, 1, 0, 0}, 86 | {"Pokemon - Feuerrote Edition (Germany)", "BPRD", 131072, 0, 0, 0, 0}, 87 | {"Pokemon - Fire Red Version (USA, Europe)", "BPRE", 131072, 0, 0, 0, 0}, 88 | {"Pokemon - Leaf Green Version (USA, Europe)", "BPGE", 131072, 0, 0, 0, 0}, 89 | {"Pokemon - Rubin Edition (Germany)", "AXVD", 131072, 0, 1, 0, 0}, 90 | {"Pokemon - Ruby Version (USA, Europe)", "AXVE", 131072, 0, 1, 0, 0}, 91 | {"Pokemon - Sapphire Version (USA, Europe)", "AXPE", 131072, 0, 1, 0, 0}, 92 | {"Pokemon - Saphir Edition (Germany)", "AXPD", 131072, 0, 1, 0, 0}, 93 | {"Pokemon - Smaragd Edition (Germany)", "BPED", 131072, 0, 1, 0, 0}, 94 | {"Pokemon - Version Emeraude (France)", "BPEF", 131072, 0, 1, 0, 0}, 95 | {"Pokemon - Version Rouge Feu (France)", "BPRF", 131072, 0, 0, 0, 0}, 96 | {"Pokemon - Version Rubis (France)", "AXVF", 131072, 0, 1, 0, 0}, 97 | {"Pokemon - Version Saphir (France)", "AXPF", 131072, 0, 1, 0, 0}, 98 | {"Pokemon - Version Vert Feuille (France)", "BPGF", 131072, 0, 0, 0, 0}, 99 | {"Pokemon - Versione Rubino (Italy)", "AXVI", 131072, 0, 1, 0, 0}, 100 | {"Pokemon - Versione Rosso Fuoco (Italy)", "BPRI", 131072, 0, 0, 0, 0}, 101 | {"Pokemon - Versione Smeraldo (Italy)", "BPEI", 131072, 0, 1, 0, 0}, 102 | {"Pokemon - Versione Verde Foglia (Italy)", "BPGI", 131072, 0, 0, 0, 0}, 103 | {"Pokemon - Versione Zaffiro (Italy)", "AXPI", 131072, 0, 1, 0, 0}, 104 | {"Rockman EXE 4.5 - Real Operation (Japan)", "BR4J", 0, 0, 1, 0, 0}, 105 | {"Rocky (Europe)(En,Fr,De,Es,It)", "AROP", 0, 1, 0, 0, 0}, 106 | {"Rocky (USA)(En,Fr,De,Es,It)", "AR8e", 0, 1, 0, 0, 0}, 107 | {"Sennen Kazoku (Japan)", "BKAJ", 131072, 0, 1, 0, 0}, 108 | {"Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan)", "U33J", 0, 1, 1, 0, 0}, 109 | {"Super Mario Advance 4 (Japan)", "AX4J", 131072, 0, 0, 0, 0}, 110 | {"Super Mario Advance 4 - Super Mario Bros. 3 (Europe)(En,Fr,De,Es,It)", "AX4P", 131072, 0, 0, 0, 0}, 111 | {"Super Mario Advance 4 - Super Mario Bros 3 - Super Mario Advance 4 v1.1 (USA)", "AX4E", 131072, 0, 0, 0, 0}, 112 | {"Top Gun - Combat Zones (USA)(En,Fr,De,Es,It)", "A2YE", 0, 5, 0, 0, 0}, 113 | {"Yoshi's Universal Gravitation (Europe)(En,Fr,De,Es,It)", "KYGP", 0, 4, 0, 0, 0}, 114 | {"Yoshi no Banyuuinryoku (Japan)", "KYGJ", 0, 4, 0, 0, 0}, 115 | {"Yoshi - Topsy-Turvy (USA)", "KYGE", 0, 1, 0, 0, 0}, 116 | {"Yu-Gi-Oh! GX - Duel Academy (USA)", "BYGE", 0, 2, 0, 0, 1}, 117 | {"Yu-Gi-Oh! - Ultimate Masters - 2006 (Europe)(En,Jp,Fr,De,Es,It)", "BY6P", 0, 2, 0, 0, 0}, 118 | {"Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan)", "U32J", 0, 0, 1, 0, 0}}; -------------------------------------------------------------------------------- /source/switch/hbkbd.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Checkpoint 3 | * Copyright (C) 2017-2018 Bernardo Giordano 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | #include "hbkbd.h" 28 | #include "theme.h" 29 | #include "ui.h" 30 | 31 | static const u32 buttonSpacing = 4; 32 | static const u32 normalWidth = 92; 33 | static const u32 bigWidth = 116; 34 | static const u32 height = 60; 35 | static const u32 margintb = 63; 36 | static const u32 marginlr = 54; 37 | static const u32 starty = 720 - 450 + margintb; 38 | 39 | static const std::string letters = "1234567890@qwertyuiop+asdfghjkl_:zxcvbnm,.-/"; 40 | static std::vector buttons; 41 | static size_t prevSelectedButtonIndex; 42 | 43 | static float keyRepeatWait; 44 | static float keyRepeatWaitLength; 45 | 46 | size_t hbkbd::count(void) { return buttons.size(); } 47 | 48 | void hbkbd::hid(size_t& currentEntry, float dt) { 49 | static const size_t columns = 11; 50 | 51 | u64 kHeld = hidKeysHeld(CONTROLLER_P1_AUTO); 52 | bool sleep = false; 53 | 54 | u64 kUp = hidKeysUp(CONTROLLER_P1_AUTO); 55 | if (kUp & KEY_LEFT || kUp & KEY_RIGHT || kUp & KEY_DOWN || kUp & KEY_UP) { 56 | keyRepeatWaitLength = FASTSCROLL_WAIT; 57 | keyRepeatWait = 0.f; 58 | } 59 | 60 | keyRepeatWait -= dt; 61 | 62 | if (keyRepeatWait <= 0.f) { 63 | if (kHeld & KEY_LEFT) { 64 | switch (currentEntry) { 65 | case 0: // 1 66 | case columns: // q 67 | case columns * 2: // a 68 | case columns * 3: // z 69 | case INDEX_CAPS: 70 | break; 71 | case INDEX_BACK: // back -> @ 72 | currentEntry = columns - 1; 73 | break; 74 | case INDEX_RETURN: // return -> + 75 | currentEntry = columns * 2 - 1; 76 | break; 77 | case INDEX_OK: // OK -> space 78 | currentEntry = INDEX_SPACE; 79 | break; 80 | case INDEX_SPACE: // space -> caps 81 | currentEntry = INDEX_CAPS; 82 | break; 83 | default: 84 | currentEntry--; 85 | } 86 | sleep = true; 87 | } else if (kHeld & KEY_RIGHT) { 88 | switch (currentEntry) { 89 | case 10: // @ -> back 90 | currentEntry = INDEX_BACK; 91 | break; 92 | case 21: // + -> return 93 | case 32: // : -> return 94 | currentEntry = INDEX_RETURN; 95 | break; 96 | case 43: // /-> OK 97 | case INDEX_SPACE: // space -> OK 98 | currentEntry = INDEX_OK; 99 | break; 100 | case INDEX_CAPS: // caps -> space 101 | currentEntry = INDEX_SPACE; 102 | break; 103 | case INDEX_BACK: 104 | case INDEX_RETURN: 105 | case INDEX_OK: 106 | break; 107 | default: 108 | currentEntry++; 109 | } 110 | sleep = true; 111 | } else if (kHeld & KEY_UP) { 112 | switch (currentEntry) { 113 | case 0 ... 10: // 1 to @ 114 | case INDEX_BACK: 115 | break; 116 | case INDEX_CAPS: // caps -> x 117 | currentEntry = 34; 118 | break; 119 | case INDEX_SPACE: // space -> . 120 | currentEntry = 41; 121 | break; 122 | case INDEX_RETURN: // return -> back 123 | currentEntry = INDEX_BACK; 124 | break; 125 | case INDEX_OK: // OK -> return 126 | currentEntry = INDEX_RETURN; 127 | break; 128 | default: 129 | currentEntry -= 11; 130 | } 131 | sleep = true; 132 | } else if (kHeld & KEY_DOWN) { 133 | switch (currentEntry) { 134 | case INDEX_CAPS: 135 | case INDEX_SPACE: 136 | case INDEX_OK: 137 | break; 138 | case 33 ... 35: // z x c -> caps 139 | currentEntry = INDEX_CAPS; 140 | break; 141 | case 36 ... 43: // v b n m , . - / -> space 142 | currentEntry = INDEX_SPACE; 143 | break; 144 | case INDEX_BACK: // back -> return 145 | currentEntry = INDEX_RETURN; 146 | break; 147 | case INDEX_RETURN: // return -> OK 148 | currentEntry = INDEX_OK; 149 | break; 150 | default: 151 | currentEntry += 11; 152 | } 153 | sleep = true; 154 | } 155 | } 156 | 157 | if (sleep) { 158 | keyRepeatWait = keyRepeatWaitLength; 159 | keyRepeatWaitLength *= FASTSCROLL_ACCL; 160 | if(keyRepeatWaitLength < FASTSCROLL_MAX_VEL) keyRepeatWait = FASTSCROLL_MAX_VEL; 161 | } 162 | } 163 | 164 | void hbkbd::init(void) { 165 | buttons.clear(); 166 | 167 | // fill with the above characters 168 | for (size_t i = 0; i < 4; i++) { 169 | for (size_t j = 0; j < 11; j++) { 170 | HbkbdButton* button = new HbkbdButton( 171 | marginlr + (buttonSpacing + normalWidth) * j, starty + (buttonSpacing + height) * i, normalWidth, height, 172 | currentTheme.keyboardKeyBackgroundColor, currentTheme.textColor, letters.substr(i * 11 + j, 1), true); 173 | buttons.push_back(button); 174 | } 175 | } 176 | 177 | HbkbdButton* backspace = new HbkbdButton(marginlr + (buttonSpacing + normalWidth) * 11, starty, bigWidth, height, 178 | currentTheme.keyboardSPKeyBackgroundColor, currentTheme.keyboardSPKeyTextColor, "←", true); 179 | buttons.push_back(backspace); 180 | 181 | HbkbdButton* returnb = 182 | new HbkbdButton(marginlr + (buttonSpacing + normalWidth) * 11, starty + height + 4, bigWidth, height * 2 + 4, 183 | currentTheme.keyboardSPKeyBackgroundColor, currentTheme.keyboardSPKeyTextColor, "return", true); 184 | buttons.push_back(returnb); 185 | 186 | HbkbdButton* ok = 187 | new HbkbdButton(marginlr + (buttonSpacing + normalWidth) * 11, starty + height * 3 + 4 * 3, bigWidth, height * 2 + 4, 188 | currentTheme.keyboardOKKeyBackgroundColor, currentTheme.keyboardOKKeyTextColor, "OK", true); 189 | buttons.push_back(ok); 190 | 191 | HbkbdButton* caps = new HbkbdButton(marginlr + buttonSpacing + normalWidth, starty + height * 4 + 4 * 4, normalWidth, height, 192 | currentTheme.keyboardSPKeyBackgroundColor, currentTheme.textColor, "⇧", true); 193 | buttons.push_back(caps); 194 | 195 | HbkbdButton* spacebar = 196 | new HbkbdButton(marginlr + (buttonSpacing + normalWidth) * 3, starty + height * 4 + 4 * 4, normalWidth * 8 + buttonSpacing * 7, 197 | height, currentTheme.keyboardSPKeyBackgroundColor, currentTheme.textColor, "space", true); 198 | buttons.push_back(spacebar); 199 | 200 | // set first button as selected 201 | buttons.at(0)->selected(true); 202 | buttons.at(0)->invertColors(); 203 | prevSelectedButtonIndex = 0; 204 | } 205 | 206 | void hbkbd::exit(void) { 207 | for (size_t i = 0, sz = buttons.size(); i < sz; i++) { 208 | delete buttons[i]; 209 | } 210 | } 211 | 212 | static bool logic(std::string& str, size_t i) { 213 | if (buttons.at(i)->text().compare("⇧") == 0) { 214 | std::locale loc; 215 | bool islower = std::islower(buttons.at(11)->text()[0], loc); 216 | for (size_t t = 0; t < letters.length(); t++) { 217 | std::string l = 218 | islower ? std::string(1, std::toupper(letters[t], loc)) : std::string(1, std::tolower(letters[t], loc)); 219 | buttons.at(t)->text(l); 220 | } 221 | } else if (buttons.at(i)->text().compare("←") == 0) { 222 | if (!str.empty()) { 223 | str.erase(str.length() - 1); 224 | } 225 | } else if (buttons.at(i)->text().compare("space") == 0) { 226 | if (str.length() < CUSTOM_PATH_LEN) { 227 | str.append(" "); 228 | } 229 | } else if (buttons.at(i)->text().compare("return") == 0) { 230 | // str.append("\n"); 231 | } else if (buttons.at(i)->text().compare("OK") == 0) { 232 | return true; 233 | } else if (str.length() < CUSTOM_PATH_LEN) { 234 | str.append(buttons.at(i)->text()); 235 | } 236 | 237 | return false; 238 | } 239 | 240 | #define SECONDS_PER_TICKS (1.0 / 19200000) 241 | 242 | std::string hbkbd::keyboard(const std::string& suggestion) { 243 | size_t index; 244 | std::string str; 245 | 246 | keyRepeatWait = 0.0; 247 | keyRepeatWaitLength = FASTSCROLL_WAIT; 248 | 249 | double lastTime = (double)svcGetSystemTick() * SECONDS_PER_TICKS; 250 | while (appletMainLoop() && !(hidKeysDown(CONTROLLER_P1_AUTO) & KEY_B)) { 251 | double time = (double)svcGetSystemTick() * SECONDS_PER_TICKS; 252 | double dt = time - lastTime; 253 | lastTime = time; 254 | 255 | hidScanInput(); 256 | index = prevSelectedButtonIndex; 257 | 258 | // handle keys 259 | hid(index, dt); 260 | if (index != prevSelectedButtonIndex) { 261 | buttons.at(prevSelectedButtonIndex)->selected(false); 262 | buttons.at(prevSelectedButtonIndex)->invertColors(); 263 | prevSelectedButtonIndex = index; 264 | buttons.at(index)->selected(true); 265 | buttons.at(prevSelectedButtonIndex)->invertColors(); 266 | } 267 | 268 | if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_A) { 269 | bool ret = logic(str, index); 270 | if (ret) { 271 | return str.empty() ? suggestion : str; 272 | } 273 | } 274 | 275 | currentFB = gfxGetFramebuffer(¤tFBWidth, NULL); 276 | 277 | drawRect(0, 0, 1280, 270, COLOR_GREY_DARK); 278 | 279 | drawRect(marginlr, 140, 1280 - marginlr * 2, 84, COLOR_GREY_MEDIUM); // Text input background 280 | drawRect(0, starty - margintb, 1280, 450, currentTheme.keyboardBackgroundColor); // Keyboard background 281 | 282 | u32 texth; 283 | getTextDimensions(font24, " ", NULL, &texth); 284 | if (str.empty()) { 285 | drawText(font24, marginlr * 2, 140 + (84 - texth) / 2, COLOR_GREY_LIGHT, 286 | suggestion.c_str()); // Text input placeholder 287 | } else { 288 | drawText(font24, marginlr * 2, 140 + (84 - texth) / 2, COLOR_WHITE, str.c_str()); // Text input value 289 | } 290 | 291 | for (size_t i = 0, sz = buttons.size(); i < sz; i++) { 292 | if (buttons.at(i)->released()) { 293 | bool ret = logic(str, i); 294 | if (ret) { 295 | return str.empty() ? suggestion : str; 296 | } 297 | } 298 | 299 | // selection logic 300 | if (buttons.at(i)->held() && i != prevSelectedButtonIndex) { 301 | buttons.at(prevSelectedButtonIndex)->selected(false); 302 | buttons.at(prevSelectedButtonIndex)->invertColors(); 303 | prevSelectedButtonIndex = i; 304 | buttons.at(i)->selected(true); 305 | buttons.at(i)->invertColors(); 306 | } 307 | 308 | buttons.at(i)->draw(); 309 | } 310 | 311 | uiDrawTipButton(buttonB, 1, "Cancel"); 312 | uiDrawTipButton(buttonA, 2, "Enter"); 313 | 314 | gfxFlushBuffers(); 315 | gfxSwapBuffers(); 316 | gfxWaitForVsync(); 317 | } 318 | 319 | return suggestion; 320 | } -------------------------------------------------------------------------------- /source/switch/hbkbd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Checkpoint 3 | * Copyright (C) 2017-2018 Bernardo Giordano 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | #ifndef HBKBD_HPP 28 | #define HBKBD_HPP 29 | 30 | #include "clickable.h" 31 | #include "draw.h" 32 | #include "util.h" 33 | #include "theme.h" 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #define CUSTOM_PATH_LEN 49 40 | #define FASTSCROLL_WAIT 0.15 41 | #define FASTSCROLL_ACCL 0.8 42 | #define FASTSCROLL_MAX_VEL 0.05 43 | 44 | class HbkbdButton : public Clickable { 45 | public: 46 | HbkbdButton(u32 x, u32 y, u16 w, u16 h, color_t colorBg, color_t colorText, const std::string& message, bool centered) 47 | : Clickable(x, y, w, h, colorBg, colorText, message, centered) { 48 | mSelected = false; 49 | } 50 | 51 | void selected(bool v) { mSelected = v; } 52 | 53 | void draw(void) { 54 | Clickable::draw(); 55 | // outline 56 | if (mSelected) { 57 | color_t color = currentTheme.keyboardHighlightColor; 58 | static const size_t size = 4; 59 | drawRect(mx - size, my - size, mw + 2 * size, size, color); // top 60 | drawRect(mx - size, my, size, mh, color); // left 61 | drawRect(mx + mw, my, size, mh, color); // right 62 | drawRect(mx - size, my + mh, mw + 2 * size, size, color); // bottom 63 | } 64 | } 65 | 66 | protected: 67 | bool mSelected; 68 | }; 69 | 70 | // hardcoded, but it's not going to change often 71 | #define INDEX_BACK 44 72 | #define INDEX_RETURN 45 73 | #define INDEX_OK 46 74 | #define INDEX_CAPS 47 75 | #define INDEX_SPACE 48 76 | 77 | namespace hbkbd { 78 | void init(void); 79 | void exit(void); 80 | size_t count(void); 81 | void hid(size_t& currentEntry, float dt); 82 | std::string keyboard(const std::string& suggestion); 83 | } // namespace hbkbd 84 | 85 | #endif -------------------------------------------------------------------------------- /source/switch/iclickable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Checkpoint 3 | * Copyright (C) 2017-2018 Bernardo Giordano 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | #ifndef ICLICKABLE_HPP 28 | #define ICLICKABLE_HPP 29 | 30 | #include 31 | 32 | typedef uint8_t u8; 33 | typedef uint16_t u16; 34 | typedef uint32_t u32; 35 | 36 | template 37 | class IClickable 38 | { 39 | public: 40 | IClickable(int x, int y, u16 w, u16 h, T colorBg, T colorText, const std::string& message, bool centered) 41 | : mx(x), my(y), mw(w), mh(h), mColorBg(colorBg), mColorText(colorText), mText(message), mCentered(centered) 42 | { 43 | mOldPressed = false; 44 | } 45 | 46 | virtual ~IClickable(void) { } 47 | 48 | virtual void draw(void) = 0; 49 | virtual void draw(float size) = 0; 50 | virtual bool held(void) = 0; 51 | virtual bool released(void) = 0; 52 | 53 | void invertColors(void) 54 | { 55 | T tmp = mColorBg; 56 | mColorBg = mColorText; 57 | mColorText = tmp; 58 | } 59 | 60 | void setColors(T bg, T text) 61 | { 62 | mColorBg = bg; 63 | mColorText = text; 64 | } 65 | 66 | std::string text(void) 67 | { 68 | return mText; 69 | } 70 | 71 | void text(const std::string& v) 72 | { 73 | mText = v; 74 | } 75 | 76 | protected: 77 | int mx; 78 | int my; 79 | u16 mw; 80 | u16 mh; 81 | T mColorBg; 82 | T mColorText; 83 | std::string mText; 84 | bool mCentered; 85 | bool mOldPressed; 86 | }; 87 | 88 | #endif -------------------------------------------------------------------------------- /source/switch/image.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "image.h" 4 | 5 | #include "stb_image.h" 6 | 7 | void imageLoad(Image* image, const char* filename) { 8 | int channelsInFile = 4; 9 | image->data = stbi_load(filename, &image->width, &image->height, &channelsInFile, 4); 10 | } 11 | 12 | void imageDeinit(Image* image) { stbi_image_free(image->data); } 13 | 14 | #define CLAMP(v, s, t) ((((v) > (t) ? (t) : (v))) < (s) ? (s) : (v)) 15 | 16 | void imageDraw(Image* image, int x, int y, int _alpha, int u, int v, int imageWidth, int imageHeight) { 17 | extern u8* currentFB; 18 | extern u32 currentFBWidth; 19 | extern u32 currentFBHeight; 20 | 21 | if (imageWidth == 0) imageWidth = image->width; 22 | if (imageHeight == 0) imageHeight = image->height; 23 | 24 | u = CLAMP(u, 0, image->width); 25 | v = CLAMP(v, 0, image->height); 26 | imageWidth = CLAMP(imageWidth, 0, image->width - u); 27 | imageHeight = CLAMP(imageHeight, 0, image->height - v); 28 | 29 | if (x + imageWidth < 0 || y + imageHeight < 0 || x >= currentFBWidth || y >= currentFBHeight) { 30 | return; 31 | } 32 | 33 | if (x < 0) { 34 | imageWidth += x; 35 | u -= x; 36 | } 37 | 38 | if (y < 0) { 39 | imageHeight += y; 40 | v -= x; 41 | } 42 | 43 | if (x + imageWidth >= currentFBWidth) { 44 | imageWidth = currentFBWidth - x; 45 | } 46 | 47 | if (y + imageHeight >= currentFBHeight) { 48 | imageHeight = currentFBHeight - y; 49 | } 50 | 51 | u8* fbAddr = currentFB + (x + y * currentFBWidth) * 4; 52 | u8* imageAddr = image->data + (u + v * image->width) * 4; 53 | for (int j = 0; j < imageHeight; j++) { 54 | u8* fbLineStart = fbAddr; 55 | 56 | for (int i = 0; i < imageWidth; i++) { 57 | u8 alpha = (imageAddr[3] * _alpha) / 255; 58 | 59 | u8 oneMinusAlpha = 255 - alpha; 60 | 61 | fbAddr[0] = (imageAddr[0] * alpha) / 255 + (fbAddr[0] * oneMinusAlpha) / 255; 62 | fbAddr[1] = (imageAddr[1] * alpha) / 255 + (fbAddr[1] * oneMinusAlpha) / 255; 63 | fbAddr[2] = (imageAddr[2] * alpha) / 255 + (fbAddr[2] * oneMinusAlpha) / 255; 64 | fbAddr[3] = 0xff; 65 | 66 | imageAddr += 4; 67 | fbAddr += 4; 68 | } 69 | imageAddr += u * 4; 70 | fbAddr = fbLineStart + currentFBWidth * 4; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /source/switch/image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct Image { 8 | u8* data; 9 | int width, height; 10 | }; 11 | 12 | void imageLoad(Image* image, const char* filename); 13 | void imageDeinit(Image* image); 14 | 15 | void imageDraw(Image* image, int x, int y, int _alpha = 255, int u = 0, int v = 0, int imageWidth = 0, int imageHeight = 0); -------------------------------------------------------------------------------- /source/switch/ini/ini.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "ini.h" 29 | 30 | struct ini_t { 31 | char *data; 32 | char *end; 33 | }; 34 | 35 | 36 | /* Case insensitive string compare */ 37 | static int strcmpci(const char *a, const char *b) { 38 | for (;;) { 39 | int d = tolower(*a) - tolower(*b); 40 | if (d != 0 || !*a) { 41 | return d; 42 | } 43 | a++, b++; 44 | } 45 | } 46 | 47 | /* Returns the next string in the split data */ 48 | static char* next(ini_t *ini, char *p) { 49 | p += strlen(p); 50 | while (p < ini->end && *p == '\0') { 51 | p++; 52 | } 53 | return p; 54 | } 55 | 56 | static void trim_back(ini_t *ini, char *p) { 57 | while (p >= ini->data && (*p == ' ' || *p == '\t' || *p == '\r')) { 58 | *p-- = '\0'; 59 | } 60 | } 61 | 62 | static char* discard_line(ini_t *ini, char *p) { 63 | while (p < ini->end && *p != '\n') { 64 | *p++ = '\0'; 65 | } 66 | return p; 67 | } 68 | 69 | 70 | static char *unescape_quoted_value(ini_t *ini, char *p) { 71 | /* Use `q` as write-head and `p` as read-head, `p` is always ahead of `q` 72 | * as escape sequences are always larger than their resultant data */ 73 | char *q = p; 74 | p++; 75 | while (p < ini->end && *p != '"' && *p != '\r' && *p != '\n') { 76 | if (*p == '\\') { 77 | /* Handle escaped char */ 78 | p++; 79 | switch (*p) { 80 | default : *q = *p; break; 81 | case 'r' : *q = '\r'; break; 82 | case 'n' : *q = '\n'; break; 83 | case 't' : *q = '\t'; break; 84 | case '\r' : 85 | case '\n' : 86 | case '\0' : goto end; 87 | } 88 | 89 | } else { 90 | /* Handle normal char */ 91 | *q = *p; 92 | } 93 | q++, p++; 94 | } 95 | end: 96 | return q; 97 | } 98 | 99 | 100 | /* Splits data in place into strings containing section-headers, keys and 101 | * values using one or more '\0' as a delimiter. Unescapes quoted values */ 102 | static void split_data(ini_t *ini) { 103 | char *value_start, *line_start; 104 | char *p = ini->data; 105 | 106 | while (p < ini->end) { 107 | switch (*p) { 108 | case '\r': 109 | case '\n': 110 | case '\t': 111 | case ' ': 112 | *p = '\0'; 113 | /* Fall through */ 114 | 115 | case '\0': 116 | p++; 117 | break; 118 | 119 | case '[': 120 | p += strcspn(p, "]\n"); 121 | *p = '\0'; 122 | break; 123 | 124 | case ';': 125 | p = discard_line(ini, p); 126 | break; 127 | 128 | default: 129 | line_start = p; 130 | p += strcspn(p, "=\n"); 131 | 132 | /* Is line missing a '='? */ 133 | if (*p != '=') { 134 | p = discard_line(ini, line_start); 135 | break; 136 | } 137 | trim_back(ini, p - 1); 138 | 139 | /* Replace '=' and whitespace after it with '\0' */ 140 | do { 141 | *p++ = '\0'; 142 | } while (*p == ' ' || *p == '\r' || *p == '\t'); 143 | 144 | /* Is a value after '=' missing? */ 145 | if (*p == '\n' || *p == '\0') { 146 | p = discard_line(ini, line_start); 147 | break; 148 | } 149 | 150 | if (*p == '"') { 151 | /* Handle quoted string value */ 152 | value_start = p; 153 | p = unescape_quoted_value(ini, p); 154 | 155 | /* Was the string empty? */ 156 | if (p == value_start) { 157 | p = discard_line(ini, line_start); 158 | break; 159 | } 160 | 161 | /* Discard the rest of the line after the string value */ 162 | p = discard_line(ini, p); 163 | 164 | } else { 165 | /* Handle normal value */ 166 | p += strcspn(p, "\n"); 167 | trim_back(ini, p - 1); 168 | } 169 | break; 170 | } 171 | } 172 | } 173 | 174 | 175 | 176 | ini_t* ini_load(const char *filename) { 177 | ini_t *ini = NULL; 178 | FILE *fp = NULL; 179 | int n, sz; 180 | 181 | /* Init ini struct */ 182 | ini = malloc(sizeof(*ini)); 183 | if (!ini) { 184 | goto fail; 185 | } 186 | memset(ini, 0, sizeof(*ini)); 187 | 188 | /* Open file */ 189 | fp = fopen(filename, "rb"); 190 | if (!fp) { 191 | goto fail; 192 | } 193 | 194 | /* Get file size */ 195 | fseek(fp, 0, SEEK_END); 196 | sz = ftell(fp); 197 | rewind(fp); 198 | 199 | /* Load file content into memory, null terminate, init end var */ 200 | ini->data = malloc(sz + 1); 201 | ini->data[sz] = '\0'; 202 | ini->end = ini->data + sz; 203 | n = fread(ini->data, 1, sz, fp); 204 | if (n != sz) { 205 | goto fail; 206 | } 207 | 208 | /* Prepare data */ 209 | split_data(ini); 210 | 211 | /* Clean up and return */ 212 | fclose(fp); 213 | return ini; 214 | 215 | fail: 216 | if (fp) fclose(fp); 217 | if (ini) ini_free(ini); 218 | return NULL; 219 | } 220 | 221 | 222 | void ini_free(ini_t *ini) { 223 | free(ini->data); 224 | free(ini); 225 | } 226 | 227 | 228 | const char* ini_get(ini_t *ini, const char *section, const char *key) { 229 | char *current_section = ""; 230 | char *val; 231 | char *p = ini->data; 232 | 233 | if (*p == '\0') { 234 | p = next(ini, p); 235 | } 236 | 237 | while (p < ini->end) { 238 | if (*p == '[') { 239 | /* Handle section */ 240 | current_section = p + 1; 241 | 242 | } else { 243 | /* Handle key */ 244 | val = next(ini, p); 245 | if (!section || !strcmpci(section, current_section)) { 246 | if (!strcmpci(p, key)) { 247 | return val; 248 | } 249 | } 250 | p = val; 251 | } 252 | 253 | p = next(ini, p); 254 | } 255 | 256 | return NULL; 257 | } 258 | 259 | 260 | int ini_sget( 261 | ini_t *ini, const char *section, const char *key, 262 | const char *scanfmt, void *dst 263 | ) { 264 | const char *val = ini_get(ini, section, key); 265 | if (!val) { 266 | return 0; 267 | } 268 | if (scanfmt) { 269 | sscanf(val, scanfmt, dst); 270 | } else { 271 | *((const char**) dst) = val; 272 | } 273 | return 1; 274 | } -------------------------------------------------------------------------------- /source/switch/ini/ini.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `ini.c` for details. 6 | */ 7 | 8 | #ifndef INI_H 9 | #define INI_H 10 | 11 | #define INI_VERSION "0.1.1" 12 | 13 | typedef struct ini_t ini_t; 14 | 15 | ini_t* ini_load(const char *filename); 16 | void ini_free(ini_t *ini); 17 | const char* ini_get(ini_t *ini, const char *section, const char *key); 18 | int ini_sget(ini_t *ini, const char *section, const char *key, const char *scanfmt, void *dst); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /source/switch/localtime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "localtime.h" 5 | 6 | #include 7 | 8 | 9 | struct consLoc { 10 | char str[0x24]; 11 | }; 12 | 13 | struct consLoc consoleLocation; 14 | 15 | static Service g_timeSrv; 16 | static Service g_timeUserSystemClock; 17 | static Service g_timeNetworkSystemClock; 18 | static Service g_timeTimeZoneService; 19 | static Service g_timeLocalSystemClock; 20 | 21 | 22 | 23 | static Result _timeGetSession(Service* srv_out, u64 cmd_id) { 24 | IpcCommand c; 25 | ipcInitialize(&c); 26 | 27 | struct { 28 | u64 magic; 29 | u64 cmd_id; 30 | } *raw; 31 | 32 | raw = ipcPrepareHeader(&c, sizeof(*raw)); 33 | 34 | raw->magic = SFCI_MAGIC; 35 | raw->cmd_id = cmd_id; 36 | 37 | Result rc = serviceIpcDispatch(&g_timeSrv); 38 | 39 | if (R_SUCCEEDED(rc)) { 40 | IpcParsedCommand r; 41 | ipcParse(&r); 42 | 43 | struct { 44 | u64 magic; 45 | u64 result; 46 | } *resp = r.Raw; 47 | 48 | rc = resp->result; 49 | 50 | if (R_SUCCEEDED(rc)) { 51 | serviceCreate(srv_out, r.Handles[0]); 52 | } 53 | } 54 | 55 | return rc; 56 | } 57 | 58 | Result localTimeInit() { 59 | if (serviceIsActive(&g_timeSrv)) 60 | return 0; 61 | 62 | Result rc; 63 | 64 | rc = smGetService(&g_timeSrv, "time:s"); 65 | if (R_FAILED(rc)) 66 | rc = smGetService(&g_timeSrv, "time:u"); 67 | 68 | if (R_FAILED(rc)) 69 | return rc; 70 | 71 | rc = _timeGetSession(&g_timeUserSystemClock, 0); 72 | 73 | if (R_SUCCEEDED(rc)) 74 | rc = _timeGetSession(&g_timeNetworkSystemClock, 1); 75 | 76 | if (R_SUCCEEDED(rc)) 77 | rc = _timeGetSession(&g_timeTimeZoneService, 3); 78 | 79 | if (R_SUCCEEDED(rc)) 80 | rc = _timeGetSession(&g_timeLocalSystemClock, 4); 81 | 82 | if (R_FAILED(rc)) 83 | timeExit(); 84 | 85 | return rc; 86 | } 87 | 88 | void localTimeExit() { 89 | serviceClose(&g_timeLocalSystemClock); 90 | serviceClose(&g_timeTimeZoneService); 91 | serviceClose(&g_timeNetworkSystemClock); 92 | serviceClose(&g_timeUserSystemClock); 93 | serviceClose(&g_timeSrv); 94 | } 95 | 96 | 97 | Result getConsoleLocationName() { 98 | 99 | IpcCommand c; 100 | ipcInitialize(&c); 101 | 102 | struct { 103 | u64 magic; 104 | u64 cmd_id; 105 | } *raw; 106 | 107 | raw = ipcPrepareHeader(&c, sizeof(*raw)); 108 | 109 | raw->magic = SFCI_MAGIC; 110 | raw->cmd_id = 0; 111 | 112 | Result rc = serviceIpcDispatch(&g_timeTimeZoneService); 113 | 114 | if (R_SUCCEEDED(rc)) { 115 | IpcParsedCommand r; 116 | ipcParse(&r); 117 | 118 | struct { 119 | u64 magic; 120 | u64 result; 121 | struct consLoc locationString; 122 | } *resp = r.Raw; 123 | 124 | rc = resp->result; 125 | 126 | if (R_SUCCEEDED(rc)) { 127 | consoleLocation = resp->locationString; 128 | //memcpy(consoleLocation, resp->locationString, 0x10); 129 | } 130 | } 131 | 132 | return rc; 133 | } 134 | 135 | 136 | void setupLocalTimeOffset() { 137 | timeExit(); 138 | // Free up the service-handles that are needed. 139 | localTimeInit(); 140 | getConsoleLocationName(); 141 | //printf("Location is: %s\n", consoleLocation.str); 142 | localTimeExit(); 143 | timeInitialize(); 144 | // Reinitialize the time.h svc-handlers. 145 | 146 | for(int i = 0; i < 447; i++) { 147 | if(!strcmp(timezones[i], consoleLocation.str)) { 148 | offset = timezoneOffsets[i]; 149 | } 150 | } 151 | } 152 | 153 | 154 | struct tm* getRealLocalTime() { 155 | 156 | time_t unixTime = time(NULL); 157 | 158 | struct tm* timeStruct = gmtime((const time_t *)&unixTime); //Gets UTC time. 159 | 160 | timeStruct->tm_sec += offset*3600; 161 | mktime(timeStruct); 162 | 163 | 164 | return timeStruct; 165 | } -------------------------------------------------------------------------------- /source/switch/localtime.h: -------------------------------------------------------------------------------- 1 | // I couldn't get the LoadTimeZoneRule command to work so this is the workaround :/ 2 | 3 | static float offset = -1; 4 | 5 | static char const *timezones[447] = {"Etc/GMT+12", 6 | "Etc/GMT+11", 7 | "Pacific/Midway", 8 | "Pacific/Niue", 9 | "Pacific/Pago_Pago", 10 | "Etc/GMT+10", 11 | "Pacific/Honolulu", 12 | "Pacific/Johnston", 13 | "Pacific/Rarotonga", 14 | "Pacific/Tahiti", 15 | "America/Anchorage", 16 | "America/Juneau", 17 | "America/Nome", 18 | "America/Sitka", 19 | "America/Yakutat", 20 | "America/Santa_Isabel", 21 | "America/Dawson", 22 | "America/Los_Angeles", 23 | "America/Tijuana", 24 | "America/Vancouver", 25 | "America/Whitehorse", 26 | "PST8PDT", 27 | "America/Creston", 28 | "America/Dawson_Creek", 29 | "America/Hermosillo", 30 | "America/Phoenix", 31 | "Etc/GMT+7", 32 | "America/Chihuahua", 33 | "America/Mazatlan", 34 | "America/Boise", 35 | "America/Cambridge_Bay", 36 | "America/Denver", 37 | "America/Edmonton", 38 | "America/Inuvik", 39 | "America/Ojinaga", 40 | "America/Yellowknife", 41 | "MST7MDT", 42 | "America/Belize", 43 | "America/Costa_Rica", 44 | "America/El_Salvador", 45 | "America/Guatemala", 46 | "America/Managua", 47 | "America/Tegucigalpa", 48 | "Etc/GMT+6", 49 | "Pacific/Galapagos", 50 | "America/Chicago", 51 | "America/Indiana/Knox", 52 | "America/Indiana/Tell_City", 53 | "America/Matamoros", 54 | "America/Menominee", 55 | "America/North_Dakota/Beulah", 56 | "America/North_Dakota/Center", 57 | "America/North_Dakota/New_Salem", 58 | "America/Rainy_River", 59 | "America/Rankin_Inlet", 60 | "America/Resolute", 61 | "America/Winnipeg", 62 | "CST6CDT", 63 | "America/Bahia_Banderas", 64 | "America/Cancun", 65 | "America/Merida", 66 | "America/Mexico_City", 67 | "America/Monterrey", 68 | "America/Regina", 69 | "America/Swift_Current", 70 | "America/Bogota", 71 | "America/Cayman", 72 | "America/Coral_Harbour", 73 | "America/Eirunepe", 74 | "America/Guayaquil", 75 | "America/Jamaica", 76 | "America/Lima", 77 | "America/Panama", 78 | "America/Rio_Branco", 79 | "Etc/GMT+5", 80 | "America/Detroit", 81 | "America/Havana", 82 | "America/Indiana/Petersburg", 83 | "America/Indiana/Vincennes", 84 | "America/Indiana/Winamac", 85 | "America/Iqaluit", 86 | "America/Kentucky/Monticello", 87 | "America/Louisville", 88 | "America/Montreal", 89 | "America/Nassau", 90 | "America/New_York", 91 | "America/Nipigon", 92 | "America/Pangnirtung", 93 | "America/Port-au-Prince", 94 | "America/Thunder_Bay", 95 | "America/Toronto", 96 | "EST5EDT", 97 | "America/Indiana/Marengo", 98 | "America/Indiana/Vevay", 99 | "America/Indianapolis", 100 | "America/Caracas", 101 | "America/Asuncion", 102 | "America/Glace_Bay", 103 | "America/Goose_Bay", 104 | "America/Halifax", 105 | "America/Moncton", 106 | "America/Thule", 107 | "Atlantic/Bermuda", 108 | "America/Campo_Grande", 109 | "America/Cuiaba", 110 | "America/Anguilla", 111 | "America/Antigua", 112 | "America/Aruba", 113 | "America/Barbados", 114 | "America/Blanc-Sablon", 115 | "America/Boa_Vista", 116 | "America/Curacao", 117 | "America/Dominica", 118 | "America/Grand_Turk", 119 | "America/Grenada", 120 | "America/Guadeloupe", 121 | "America/Guyana", 122 | "America/Kralendijk", 123 | "America/La_Paz", 124 | "America/Lower_Princes", 125 | "America/Manaus", 126 | "America/Marigot", 127 | "America/Martinique", 128 | "America/Montserrat", 129 | "America/Port_of_Spain", 130 | "America/Porto_Velho", 131 | "America/Puerto_Rico", 132 | "America/Santo_Domingo", 133 | "America/St_Barthelemy", 134 | "America/St_Kitts", 135 | "America/St_Lucia", 136 | "America/St_Thomas", 137 | "America/St_Vincent", 138 | "America/Tortola", 139 | "Etc/GMT+4", 140 | "America/Santiago", 141 | "Antarctica/Palmer", 142 | "America/St_Johns", 143 | "America/Sao_Paulo", 144 | "America/Argentina/La_Rioja", 145 | "America/Argentina/Rio_Gallegos", 146 | "America/Argentina/Salta", 147 | "America/Argentina/San_Juan", 148 | "America/Argentina/San_Luis", 149 | "America/Argentina/Tucuman", 150 | "America/Argentina/Ushuaia", 151 | "America/Buenos_Aires", 152 | "America/Catamarca", 153 | "America/Cordoba", 154 | "America/Jujuy", 155 | "America/Mendoza", 156 | "America/Araguaina", 157 | "America/Belem", 158 | "America/Cayenne", 159 | "America/Fortaleza", 160 | "America/Maceio", 161 | "America/Paramaribo", 162 | "America/Recife", 163 | "America/Santarem", 164 | "Antarctica/Rothera", 165 | "Atlantic/Stanley", 166 | "Etc/GMT+3", 167 | "America/Godthab", 168 | "America/Montevideo", 169 | "America/Bahia", 170 | "America/Noronha", 171 | "Atlantic/South_Georgia", 172 | "Etc/GMT+2", 173 | "America/Scoresbysund", 174 | "Atlantic/Azores", 175 | "Atlantic/Cape_Verde", 176 | "Etc/GMT+1", 177 | "Africa/Casablanca", 178 | "Africa/El_Aaiun", 179 | "America/Danmarkshavn", 180 | "Etc/GMT", 181 | "Atlantic/Canary", 182 | "Atlantic/Faeroe", 183 | "Atlantic/Madeira", 184 | "Europe/Dublin", 185 | "Europe/Guernsey", 186 | "Europe/Isle_of_Man", 187 | "Europe/Jersey", 188 | "Europe/Lisbon", 189 | "Europe/London", 190 | "Africa/Abidjan", 191 | "Africa/Accra", 192 | "Africa/Bamako", 193 | "Africa/Banjul", 194 | "Africa/Bissau", 195 | "Africa/Conakry", 196 | "Africa/Dakar", 197 | "Africa/Freetown", 198 | "Africa/Lome", 199 | "Africa/Monrovia", 200 | "Africa/Nouakchott", 201 | "Africa/Ouagadougou", 202 | "Africa/Sao_Tome", 203 | "Atlantic/Reykjavik", 204 | "Atlantic/St_Helena", 205 | "Arctic/Longyearbyen", 206 | "Europe/Amsterdam", 207 | "Europe/Andorra", 208 | "Europe/Berlin", 209 | "Europe/Busingen", 210 | "Europe/Gibraltar", 211 | "Europe/Luxembourg", 212 | "Europe/Malta", 213 | "Europe/Monaco", 214 | "Europe/Oslo", 215 | "Europe/Rome", 216 | "Europe/San_Marino", 217 | "Europe/Stockholm", 218 | "Europe/Vaduz", 219 | "Europe/Vatican", 220 | "Europe/Vienna", 221 | "Europe/Zurich", 222 | "Europe/Belgrade", 223 | "Europe/Bratislava", 224 | "Europe/Budapest", 225 | "Europe/Ljubljana", 226 | "Europe/Podgorica", 227 | "Europe/Prague", 228 | "Europe/Tirane", 229 | "Africa/Ceuta", 230 | "Europe/Brussels", 231 | "Europe/Copenhagen", 232 | "Europe/Madrid", 233 | "Europe/Paris", 234 | "Europe/Sarajevo", 235 | "Europe/Skopje", 236 | "Europe/Warsaw", 237 | "Europe/Zagreb", 238 | "Africa/Algiers", 239 | "Africa/Bangui", 240 | "Africa/Brazzaville", 241 | "Africa/Douala", 242 | "Africa/Kinshasa", 243 | "Africa/Lagos", 244 | "Africa/Libreville", 245 | "Africa/Luanda", 246 | "Africa/Malabo", 247 | "Africa/Ndjamena", 248 | "Africa/Niamey", 249 | "Africa/Porto-Novo", 250 | "Africa/Tunis", 251 | "Etc/GMT-1", 252 | "Africa/Windhoek", 253 | "Asia/Nicosia", 254 | "Europe/Athens", 255 | "Europe/Bucharest", 256 | "Europe/Chisinau", 257 | "Asia/Beirut", 258 | "Africa/Cairo", 259 | "Asia/Damascus", 260 | "Asia/Nicosia", 261 | "Europe/Athens", 262 | "Europe/Bucharest", 263 | "Europe/Chisinau", 264 | "Europe/Helsinki", 265 | "Europe/Kiev", 266 | "Europe/Mariehamn", 267 | "Europe/Nicosia", 268 | "Europe/Riga", 269 | "Europe/Sofia", 270 | "Europe/Tallinn", 271 | "Europe/Uzhgorod", 272 | "Europe/Vilnius", 273 | "Europe/Zaporozhye", 274 | "Africa/Blantyre", 275 | "Africa/Bujumbura", 276 | "Africa/Gaborone", 277 | "Africa/Harare", 278 | "Africa/Johannesburg", 279 | "Africa/Kigali", 280 | "Africa/Lubumbashi", 281 | "Africa/Lusaka", 282 | "Africa/Maputo", 283 | "Africa/Maseru", 284 | "Africa/Mbabane", 285 | "Etc/GMT-2", 286 | "Europe/Helsinki", 287 | "Europe/Kiev", 288 | "Europe/Mariehamn", 289 | "Europe/Riga", 290 | "Europe/Sofia", 291 | "Europe/Tallinn", 292 | "Europe/Uzhgorod", 293 | "Europe/Vilnius", 294 | "Europe/Zaporozhye", 295 | "Europe/Istanbul", 296 | "Asia/Jerusalem", 297 | "Africa/Tripoli", 298 | "Asia/Amman", 299 | "Asia/Baghdad", 300 | "Europe/Kaliningrad", 301 | "Europe/Minsk", 302 | "Asia/Aden", 303 | "Asia/Bahrain", 304 | "Asia/Kuwait", 305 | "Asia/Qatar", 306 | "Asia/Riyadh", 307 | "Africa/Addis_Ababa", 308 | "Africa/Asmera", 309 | "Africa/Dar_es_Salaam", 310 | "Africa/Djibouti", 311 | "Africa/Juba", 312 | "Africa/Kampala", 313 | "Africa/Khartoum", 314 | "Africa/Mogadishu", 315 | "Africa/Nairobi", 316 | "Antarctica/Syowa", 317 | "Etc/GMT-3", 318 | "Indian/Antananarivo", 319 | "Indian/Comoro", 320 | "Indian/Mayotte", 321 | "Europe/Kirov", 322 | "Europe/Moscow", 323 | "Europe/Simferopol", 324 | "Europe/Volgograd", 325 | "Europe/Astrakhan", 326 | "Europe/Samara", 327 | "Europe/Ulyanovsk", 328 | "Asia/Tehran", 329 | "Asia/Dubai", 330 | "Asia/Muscat", 331 | "Etc/GMT-4", 332 | "Asia/Baku", 333 | "Indian/Mahe", 334 | "Indian/Mauritius", 335 | "Indian/Reunion", 336 | "Asia/Tbilisi", 337 | "Asia/Yerevan", 338 | "Asia/Kabul", 339 | "Antarctica/Mawson", 340 | "Asia/Aqtau", 341 | "Asia/Aqtobe", 342 | "Asia/Ashgabat", 343 | "Asia/Dushanbe", 344 | "Asia/Oral", 345 | "Asia/Samarkand", 346 | "Asia/Tashkent", 347 | "Etc/GMT-5", 348 | "Indian/Kerguelen", 349 | "Indian/Maldives", 350 | "Asia/Karachi", 351 | "Asia/Kolkata", 352 | "Asia/Colombo", 353 | "Asia/Katmandu", 354 | "Antarctica/Vostok", 355 | "Asia/Almaty", 356 | "Asia/Bishkek", 357 | "Asia/Qyzylorda", 358 | "Asia/Urumqi", 359 | "Etc/GMT-6", 360 | "Indian/Chagos", 361 | "Asia/Dhaka", 362 | "Asia/Thimphu", 363 | "Asia/Yekaterinburg", 364 | "Asia/Rangoon", 365 | "Indian/Cocos", 366 | "Antarctica/Davis", 367 | "Asia/Bangkok", 368 | "Asia/Hovd", 369 | "Asia/Jakarta", 370 | "Asia/Phnom_Penh", 371 | "Asia/Pontianak", 372 | "Asia/Saigon", 373 | "Asia/Vientiane", 374 | "Etc/GMT-7", 375 | "Indian/Christmas", 376 | "Asia/Novokuznetsk", 377 | "Asia/Novosibirsk", 378 | "Asia/Omsk", 379 | "Asia/Hong_Kong", 380 | "Asia/Macau", 381 | "Asia/Shanghai", 382 | "Asia/Krasnoyarsk", 383 | "Asia/Brunei", 384 | "Asia/Kuala_Lumpur", 385 | "Asia/Kuching", 386 | "Asia/Makassar", 387 | "Asia/Manila", 388 | "Asia/Singapore", 389 | "Etc/GMT-8", 390 | "Antarctica/Casey", 391 | "Australia/Perth", 392 | "Asia/Taipei", 393 | "Asia/Choibalsan", 394 | "Asia/Ulaanbaatar", 395 | "Asia/Irkutsk", 396 | "Asia/Dili", 397 | "Asia/Jayapura", 398 | "Asia/Tokyo", 399 | "Etc/GMT-9", 400 | "Pacific/Palau", 401 | "Asia/Pyongyang", 402 | "Asia/Seoul", 403 | "Australia/Adelaide", 404 | "Australia/Broken_Hill", 405 | "Australia/Darwin", 406 | "Australia/Brisbane", 407 | "Australia/Lindeman", 408 | "Australia/Melbourne", 409 | "Australia/Sydney", 410 | "Antarctica/DumontDUrville", 411 | "Etc/GMT-10", 412 | "Pacific/Guam", 413 | "Pacific/Port_Moresby", 414 | "Pacific/Saipan", 415 | "Pacific/Truk", 416 | "Australia/Currie", 417 | "Australia/Hobart", 418 | "Asia/Chita", 419 | "Asia/Khandyga", 420 | "Asia/Yakutsk", 421 | "Antarctica/Macquarie", 422 | "Etc/GMT-11", 423 | "Pacific/Efate", 424 | "Pacific/Guadalcanal", 425 | "Pacific/Kosrae", 426 | "Pacific/Noumea", 427 | "Pacific/Ponape", 428 | "Asia/Sakhalin", 429 | "Asia/Ust-Nera", 430 | "Asia/Vladivostok", 431 | "Antarctica/McMurdo", 432 | "Pacific/Auckland", 433 | "Etc/GMT-12", 434 | "Pacific/Funafuti", 435 | "Pacific/Kwajalein", 436 | "Pacific/Majuro", 437 | "Pacific/Nauru", 438 | "Pacific/Tarawa", 439 | "Pacific/Wake", 440 | "Pacific/Wallis", 441 | "Pacific/Fiji", 442 | "Asia/Anadyr", 443 | "Asia/Kamchatka", 444 | "Asia/Magadan", 445 | "Asia/Srednekolymsk", 446 | "Asia/Kamchatka", 447 | "Etc/GMT-13", 448 | "Pacific/Enderbury", 449 | "Pacific/Fakaofo", 450 | "Pacific/Tongatapu", 451 | "Pacific/Apia"}; 452 | 453 | 454 | static float const timezoneOffsets[447] = { 455 | -12, -11, -11, -11, -11, -10, -10, -10, -10, -10, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, 456 | -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, 457 | -5, -5, -5, -5, -5, -5, -5, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, 458 | -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4.5, -4, -3, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, 459 | -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -2.5, -3, -3, 460 | -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, 461 | 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 462 | 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 463 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 464 | 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 465 | 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 466 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4.5, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4.5, 5, 5, 467 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5.5, 5.5, 5.75, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6.5, 6.5, 7, 7, 7, 468 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 469 | 9, 9, 9, 9, 9, 9, 9.5, 9.5, 9.5, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 470 | 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13}; 471 | 472 | void setupLocalTimeOffset(); 473 | struct tm* getRealLocalTime(); 474 | -------------------------------------------------------------------------------- /source/switch/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "../GBACheats.h" 13 | #include "../gba.h" 14 | #include "../globals.h" 15 | #include "../memory.h" 16 | #include "../port.h" 17 | #include "../sound.h" 18 | #include "../system.h" 19 | #include "../types.h" 20 | #include "gbaover.h" 21 | 22 | 23 | #include "util.h" 24 | #include "zoom.h" 25 | 26 | #include "draw.h" 27 | #include "ui.h" 28 | 29 | u8 *currentFB; 30 | u32 currentFBWidth; 31 | u32 currentFBHeight; 32 | 33 | #include 34 | 35 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 36 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 37 | #define SECONDS_PER_TICKS (1.0 / 19200000) 38 | #define TARGET_FPS (16777216.0 / 280896.0) 39 | #define TARGET_FRAMETIME (1.0 / TARGET_FPS) 40 | #define TARGET_FRAMETIME_us (u64)(TARGET_FRAMETIME * 1000000000) 41 | #define TARGET_FRAMETIME_TICKS (u64)(TARGET_FRAMETIME * 19200000) 42 | #define TICKS_PER_us (1000000000ul / 19200000ul) 43 | 44 | uint8_t libretro_save_buf[0x20000 + 0x2000]; /* Workaround for broken-by-design GBA save semantics. */ 45 | 46 | extern uint64_t joy; 47 | unsigned device_type = 0; 48 | 49 | static u32 *upscaleBuffer = NULL; 50 | static int upscaleBufferSize = 0; 51 | 52 | static bool emulationRunning = false; 53 | static bool emulationPaused = false; 54 | 55 | static const char *stringsRtcOffset[] = {"0 Hours", "1 Hour", "2 Hours", "3 Hours", "4 Hours", "5 Hours", "6 Hours", 56 | "7 Hours", "8 Hours", "9 Hours", "10 Hours", "11 Hours", "12 Hours", "13 Hours", 57 | "-1 Hour", "-2 Hours", "-3 Hours", "-4 Hours", "-5 Hours", "-6 Hours", "-7 Hours", 58 | "-8 Hours", "-9 Hours", "-10 Hours", "-11 Hours", "-12 Hours"}; 59 | 60 | enum { filterNearestInteger, 61 | filterNearest, 62 | filterBilinear, 63 | filtersCount, 64 | }; 65 | static const char *filterStrNames[] = {"Nearest Integer", "Nearest Fullscreen", "Bilinear Fullscreen(slow!)"}; 66 | static uint32_t scalingFilter = filterNearest; 67 | 68 | static const char *frameSkipNames[] = {"No Frameskip", "1/3", "1/2", "1", "2", "3", "4"}; 69 | static const int frameSkipValues[] = {0, 0x13, 0x12, 0x1, 0x2, 0x3, 0x4}; 70 | static uint32_t frameSkip = 0; 71 | 72 | static uint32_t disableAnalogStick = 0; 73 | static uint32_t switchRLButtons = 0; 74 | 75 | static char currentRomPath[PATH_LENGTH] = {'\0'}; 76 | 77 | static Mutex videoLock; 78 | static u16 *videoTransferBuffer; 79 | static u32 *conversionBuffer; 80 | 81 | static Mutex inputLock; 82 | static u32 inputTransferKeysHeld; 83 | 84 | static Mutex emulationLock; 85 | 86 | static CondVar requestFrameCond; 87 | static Mutex requestFrameLock; 88 | 89 | static bool running = true; 90 | 91 | char filename_bios[0x100] = {0}; 92 | 93 | #define AUDIO_SAMPLERATE 48000 94 | #define AUDIO_BUFFER_COUNT 6 95 | #define AUDIO_BUFFER_SAMPLES (AUDIO_SAMPLERATE / 20) 96 | #define AUDIO_TRANSFERBUF_SIZE (AUDIO_BUFFER_SAMPLES * 4) 97 | 98 | static int audioTransferBufferUsed = 0; 99 | static u32 *audioTransferBuffer; 100 | static Mutex audioLock; 101 | 102 | static unsigned libretro_save_size = sizeof(libretro_save_buf); 103 | 104 | uint32_t buttonMap[11] = { 105 | KEY_A, KEY_B, KEY_MINUS, KEY_PLUS, KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN, KEY_R, KEY_L, 106 | KEY_ZR // Speedhack Button 107 | }; 108 | 109 | static bool has_video_frame; 110 | static int audio_samples_written; 111 | 112 | static unsigned g_audio_frames = 0; 113 | static unsigned g_video_frames = 0; 114 | 115 | static unsigned serialize_size = 0; 116 | 117 | static bool scan_area(const uint8_t *data, unsigned size) { 118 | for (unsigned i = 0; i < size; i++) 119 | if (data[i] != 0xff) return true; 120 | 121 | return false; 122 | } 123 | 124 | void systemMessage(const char *fmt, ...) { 125 | va_list ap; 126 | va_start(ap, fmt); 127 | vprintf(fmt, ap); 128 | va_end(ap); 129 | } 130 | 131 | static void adjust_save_ram() { 132 | if (scan_area(libretro_save_buf, 512) && !scan_area(libretro_save_buf + 512, sizeof(libretro_save_buf) - 512)) { 133 | libretro_save_size = 512; 134 | printf("Detecting EEprom 8kbit\n"); 135 | } else if (scan_area(libretro_save_buf, 0x2000) && !scan_area(libretro_save_buf + 0x2000, sizeof(libretro_save_buf) - 0x2000)) { 136 | libretro_save_size = 0x2000; 137 | printf("Detecting EEprom 64kbit\n"); 138 | } 139 | 140 | else if (scan_area(libretro_save_buf, 0x10000) && !scan_area(libretro_save_buf + 0x10000, sizeof(libretro_save_buf) - 0x10000)) { 141 | libretro_save_size = 0x10000; 142 | printf("Detecting Flash 512kbit\n"); 143 | } else if (scan_area(libretro_save_buf, 0x20000) && !scan_area(libretro_save_buf + 0x20000, sizeof(libretro_save_buf) - 0x20000)) { 144 | libretro_save_size = 0x20000; 145 | printf("Detecting Flash 1Mbit\n"); 146 | } else 147 | printf("Did not detect any particular SRAM type.\n"); 148 | 149 | if (libretro_save_size == 512 || libretro_save_size == 0x2000) 150 | eepromData = libretro_save_buf; 151 | else if (libretro_save_size == 0x10000 || libretro_save_size == 0x20000) 152 | flashSaveMemory = libretro_save_buf; 153 | } 154 | 155 | void romPathWithExt(char *out, int outBufferLen, const char *ext) { 156 | strcpy_safe(out, currentRomPath, outBufferLen); 157 | int dotLoc = strlen(out); 158 | while (dotLoc >= 0 && out[dotLoc] != '.') dotLoc--; 159 | 160 | int extLen = strlen(ext); 161 | for (int i = 0; i < extLen + 1; i++) out[dotLoc + 1 + i] = ext[i]; 162 | } 163 | 164 | void retro_init(void) { 165 | memset(libretro_save_buf, 0xff, sizeof(libretro_save_buf)); 166 | adjust_save_ram(); 167 | #if HAVE_HLE_BIOS 168 | const char *dir = NULL; 169 | if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) { 170 | strncpy(filename_bios, dir, sizeof(filename_bios)); 171 | strncat(filename_bios, "/gba_bios.bin", sizeof(filename_bios)); 172 | } 173 | #endif 174 | 175 | #ifdef FRONTEND_SUPPORTS_RGB565 176 | enum retro_pixel_format rgb565 = RETRO_PIXEL_FORMAT_RGB565; 177 | if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565) && log_cb) 178 | log_cb(RETRO_LOG_INFO, "Frontend supports RGB565 - will use that instead of XRGB1555.\n"); 179 | #endif 180 | 181 | #if THREADED_RENDERER 182 | ThreadedRendererStart(); 183 | #endif 184 | } 185 | 186 | static void load_image_preferences(void) { 187 | char buffer[5]; 188 | buffer[0] = rom[0xac]; 189 | buffer[1] = rom[0xad]; 190 | buffer[2] = rom[0xae]; 191 | buffer[3] = rom[0xaf]; 192 | buffer[4] = 0; 193 | 194 | bool found = false; 195 | int found_no = 0; 196 | 197 | for (int i = 0; i < 256; i++) { 198 | if (!strcmp(gbaover[i].romid, buffer)) { 199 | found = true; 200 | found_no = i; 201 | break; 202 | } 203 | } 204 | 205 | if (found) { 206 | enableRtc = gbaover[found_no].rtcEnabled; 207 | 208 | if (gbaover[found_no].flashSize != 0) 209 | flashSize = gbaover[found_no].flashSize; 210 | else 211 | flashSize = 65536; 212 | 213 | cpuSaveType = gbaover[found_no].saveType; 214 | 215 | mirroringEnable = gbaover[found_no].mirroringEnabled; 216 | } 217 | } 218 | 219 | static void gba_init(void) { 220 | cpuSaveType = 0; 221 | flashSize = 0x10000; 222 | enableRtc = false; 223 | mirroringEnable = false; 224 | 225 | load_image_preferences(); 226 | 227 | if (flashSize == 0x10000 || flashSize == 0x20000) flashSetSize(flashSize); 228 | 229 | if (enableRtc) rtcEnable(enableRtc); 230 | 231 | doMirroring(mirroringEnable); 232 | 233 | soundSetSampleRate(AUDIO_SAMPLERATE + 10); // slight oversampling makes the sound better 234 | 235 | #if HAVE_HLE_BIOS 236 | bool usebios = false; 237 | 238 | struct retro_variable var; 239 | 240 | var.key = "vbanext_bios"; 241 | var.value = NULL; 242 | 243 | if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { 244 | if (strcmp(var.value, "disabled") == 0) 245 | usebios = false; 246 | else if (strcmp(var.value, "enabled") == 0) 247 | usebios = true; 248 | } 249 | 250 | if (usebios && filename_bios[0]) 251 | CPUInit(filename_bios, true); 252 | else 253 | CPUInit(NULL, false); 254 | #else 255 | CPUInit(NULL, false); 256 | #endif 257 | CPUReset(); 258 | 259 | soundReset(); 260 | 261 | uint8_t *state_buf = (uint8_t *)malloc(2000000); 262 | serialize_size = CPUWriteState(state_buf, 2000000); 263 | free(state_buf); 264 | } 265 | 266 | void retro_deinit(void) { 267 | #if THREADED_RENDERER 268 | ThreadedRendererStop(); 269 | #endif 270 | 271 | CPUCleanUp(); 272 | } 273 | 274 | void retro_reset(void) { CPUReset(); } 275 | 276 | void pause_emulation() { 277 | mutexLock(&emulationLock); 278 | emulationPaused = true; 279 | uiPushState(statePaused); 280 | char saveFilename[PATH_LENGTH]; 281 | romPathWithExt(saveFilename, PATH_LENGTH, "sav"); 282 | if (CPUWriteBatteryFile(saveFilename)) uiStatusMsg("Wrote save file."); 283 | mutexUnlock(&emulationLock); 284 | 285 | mutexLock(&audioLock); 286 | memset(audioTransferBuffer, 0, AUDIO_TRANSFERBUF_SIZE * sizeof(u32)); 287 | audioTransferBufferUsed = AUDIO_TRANSFERBUF_SIZE; 288 | mutexUnlock(&audioLock); 289 | } 290 | 291 | void unpause_emulation() { 292 | mutexLock(&emulationLock); 293 | emulationPaused = false; 294 | uiPopState(); 295 | mutexUnlock(&emulationLock); 296 | } 297 | 298 | void retro_run() { 299 | mutexLock(&inputLock); 300 | joy = 0; 301 | 302 | for (unsigned i = 0; i < 10; i++) { 303 | joy |= ((bool)(inputTransferKeysHeld & buttonMap[i])) << i; 304 | } 305 | 306 | mutexUnlock(&inputLock); 307 | 308 | has_video_frame = false; 309 | audio_samples_written = 0; 310 | UpdateJoypad(); 311 | do { 312 | CPULoop(); 313 | } while (!has_video_frame); 314 | } 315 | 316 | bool retro_load_game() { 317 | int ret = CPULoadRom(currentRomPath); 318 | 319 | gba_init(); 320 | 321 | char saveFileName[PATH_LENGTH]; 322 | romPathWithExt(saveFileName, PATH_LENGTH, "sav"); 323 | if (CPUReadBatteryFile(saveFileName)) uiStatusMsg("Loaded save file."); 324 | 325 | return ret; 326 | } 327 | 328 | void retro_unload_game(void) { 329 | printf("[VBA] Sync stats: Audio frames: %u, Video frames: %u, AF/VF: %.2f\n", g_audio_frames, g_video_frames, 330 | (float)g_audio_frames / g_video_frames); 331 | g_audio_frames = 0; 332 | g_video_frames = 0; 333 | 334 | char saveFilename[PATH_LENGTH]; 335 | romPathWithExt(saveFilename, PATH_LENGTH, "sav"); 336 | if (CPUWriteBatteryFile(saveFilename)) uiStatusMsg("Wrote save file."); 337 | } 338 | 339 | void audio_thread_main(void *) { 340 | AudioOutBuffer sources[2]; 341 | 342 | u32 raw_data_size = (AUDIO_BUFFER_SAMPLES * sizeof(u32) + 0xfff) & ~0xfff; 343 | u32 *raw_data[2]; 344 | for (int i = 0; i < 2; i++) { 345 | raw_data[i] = (u32 *)memalign(0x1000, raw_data_size); 346 | memset(raw_data[i], 0, raw_data_size); 347 | 348 | sources[i].next = 0; 349 | sources[i].buffer = raw_data[i]; 350 | sources[i].buffer_size = raw_data_size; 351 | sources[i].data_size = AUDIO_BUFFER_SAMPLES * sizeof(u32); 352 | sources[i].data_offset = 0; 353 | 354 | audoutAppendAudioOutBuffer(&sources[i]); 355 | } 356 | 357 | while (running) { 358 | u32 count; 359 | AudioOutBuffer *released; 360 | audoutWaitPlayFinish(&released, &count, U64_MAX); 361 | 362 | mutexLock(&audioLock); 363 | 364 | u32 size = (audioTransferBufferUsed < AUDIO_BUFFER_SAMPLES ? audioTransferBufferUsed : AUDIO_BUFFER_SAMPLES) * sizeof(u32); 365 | memcpy(released->buffer, audioTransferBuffer, size); 366 | released->data_size = size == 0 ? AUDIO_BUFFER_SAMPLES * sizeof(u32) : size; 367 | 368 | audioTransferBufferUsed -= size / sizeof(u32); 369 | memmove(audioTransferBuffer, audioTransferBuffer + (size / sizeof(u32)), audioTransferBufferUsed * sizeof(u32)); 370 | 371 | mutexUnlock(&audioLock); 372 | 373 | audoutAppendAudioOutBuffer(released); 374 | } 375 | 376 | free(raw_data[0]); 377 | free(raw_data[1]); 378 | } 379 | 380 | void systemOnWriteDataToSoundBuffer(int16_t *finalWave, int length) { 381 | mutexLock(&audioLock); 382 | if (audioTransferBufferUsed + length >= AUDIO_TRANSFERBUF_SIZE) { 383 | mutexUnlock(&audioLock); 384 | return; 385 | } 386 | 387 | memcpy(audioTransferBuffer + audioTransferBufferUsed, finalWave, length * sizeof(s16)); 388 | 389 | audioTransferBufferUsed += length / 2; 390 | mutexUnlock(&audioLock); 391 | 392 | audio_samples_written += length / 2; 393 | 394 | g_audio_frames += length / 2; 395 | } 396 | 397 | void systemDrawScreen() { 398 | mutexLock(&videoLock); 399 | memcpy(videoTransferBuffer, pix, sizeof(u16) * 256 * 160); 400 | mutexUnlock(&videoLock); 401 | 402 | g_video_frames++; 403 | has_video_frame = true; 404 | } 405 | 406 | void threadFunc(void *args) { 407 | mutexLock(&emulationLock); 408 | retro_init(); 409 | mutexUnlock(&emulationLock); 410 | 411 | while (running) { 412 | double startTime = (double)svcGetSystemTick() * SECONDS_PER_TICKS; 413 | 414 | mutexLock(&emulationLock); 415 | 416 | if (emulationRunning && !emulationPaused) retro_run(); 417 | 418 | mutexUnlock(&emulationLock); 419 | 420 | double endTime = (double)svcGetSystemTick() * SECONDS_PER_TICKS; 421 | 422 | if (!(inputTransferKeysHeld & buttonMap[10])) condvarWaitTimeout(&requestFrameCond, TARGET_FRAMETIME_us); 423 | } 424 | 425 | mutexLock(&emulationLock); 426 | retro_deinit(); 427 | mutexUnlock(&emulationLock); 428 | } 429 | 430 | static void applyConfig() { 431 | mutexLock(&emulationLock); 432 | SetFrameskip(frameSkipValues[frameSkip]); 433 | 434 | if (!disableAnalogStick) { 435 | buttonMap[4] = KEY_RIGHT; 436 | buttonMap[5] = KEY_LEFT; 437 | buttonMap[6] = KEY_UP; 438 | buttonMap[7] = KEY_DOWN; 439 | } else { 440 | buttonMap[4] = KEY_DRIGHT; 441 | buttonMap[5] = KEY_DLEFT; 442 | buttonMap[6] = KEY_DUP; 443 | buttonMap[7] = KEY_DDOWN; 444 | } 445 | 446 | if (!switchRLButtons) { 447 | buttonMap[8] = KEY_R; 448 | buttonMap[9] = KEY_L; 449 | buttonMap[10] = KEY_ZR; 450 | } else { 451 | buttonMap[8] = KEY_ZR; 452 | buttonMap[9] = KEY_ZL; 453 | buttonMap[10] = KEY_R; 454 | } 455 | mutexUnlock(&emulationLock); 456 | } 457 | 458 | u32 __nx_applet_PerformanceConfiguration[2] = {/*0x92220008*/ /*0x20004*/ /*0x92220007*/ 0x92220007, 0x92220007}; 459 | 460 | int main(int argc, char *argv[]) { 461 | appletSetScreenShotPermission(1); 462 | 463 | #ifdef NXLINK_STDIO 464 | socketInitializeDefault(); 465 | nxlinkStdio(); 466 | #endif 467 | 468 | zoomInit(2048, 2048); 469 | 470 | romfsInit(); 471 | 472 | gfxInitDefault(); 473 | 474 | setsysInitialize(); 475 | 476 | plInitialize(); 477 | textInit(); 478 | fontInit(); 479 | timeInitialize(); 480 | cheatListInit(); 481 | 482 | audioTransferBuffer = (u32 *)malloc(AUDIO_TRANSFERBUF_SIZE * sizeof(u32)); 483 | mutexInit(&audioLock); 484 | 485 | audoutInitialize(); 486 | audoutStartAudioOut(); 487 | 488 | Thread audio_thread; 489 | threadCreate(&audio_thread, audio_thread_main, NULL, 0x10000, 0x2B, 2); 490 | threadStart(&audio_thread); 491 | 492 | videoTransferBuffer = (u16 *)malloc(256 * 160 * sizeof(u16)); 493 | conversionBuffer = (u32 *)malloc(256 * 160 * sizeof(u32)); 494 | 495 | mutexInit(&videoLock); 496 | 497 | uiInit(); 498 | 499 | uiAddSetting("Screen scaling method", &scalingFilter, filtersCount, filterStrNames); 500 | uiAddSetting("Frameskip", &frameSkip, sizeof(frameSkipValues) / sizeof(frameSkipValues[0]), frameSkipNames); 501 | uiAddSetting("Disable analog stick", &disableAnalogStick, 2, stringsNoYes); 502 | uiAddSetting("L R -> ZL ZR", &switchRLButtons, 2, stringsNoYes); 503 | uiAddSetting("In game clock offset", &rtcOffset, 26, stringsRtcOffset); 504 | 505 | uiFinaliseAndLoadSettings(); 506 | applyConfig(); 507 | 508 | mutexInit(&inputLock); 509 | mutexInit(&emulationLock); 510 | 511 | mutexInit(&requestFrameLock); 512 | condvarInit(&requestFrameCond, &requestFrameLock); 513 | 514 | Thread mainThread; 515 | threadCreate(&mainThread, threadFunc, NULL, 0x4000, 0x30, 1); 516 | threadStart(&mainThread); 517 | 518 | char saveFilename[PATH_LENGTH]; 519 | char cheatsFilename[PATH_LENGTH]; 520 | 521 | #ifdef NXLINK_STDIO 522 | double frameTimeSum = 0; 523 | int frameTimeFrames = 0; 524 | #endif 525 | 526 | while (appletMainLoop() && running) { 527 | #ifdef NXLINK_STDIO 528 | double frameStartTime = svcGetSystemTick() * SECONDS_PER_TICKS; 529 | #endif 530 | 531 | currentFB = gfxGetFramebuffer(¤tFBWidth, ¤tFBHeight); 532 | memset(currentFB, 0, 4 * currentFBWidth * currentFBHeight); 533 | 534 | hidScanInput(); 535 | u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO); 536 | u32 keysUp = hidKeysUp(CONTROLLER_P1_AUTO); 537 | 538 | if (emulationRunning && !emulationPaused) { 539 | mutexLock(&videoLock); 540 | 541 | u16 *bgr555src = videoTransferBuffer; 542 | u8 *rgba8888dst = (u8 *)conversionBuffer; 543 | 544 | uint16x8_t maskR = vdupq_n_u16(0x7c00); 545 | uint16x8_t maskG = vdupq_n_u16(0x3e0); 546 | uint16x8_t maskB = vdupq_n_u16(0x1f); 547 | uint8x8_t alphaFull = vdup_n_u8(255); 548 | 549 | for (int i = 0; i < 256 * 160 / 8; i++) { 550 | uint16x8_t bgr555 = vld1q_u16(bgr555src); 551 | 552 | uint8x8x4_t rgba8888; 553 | rgba8888.val[0] = vmovn_u16(vshrq_n_u16(vandq_u16(bgr555, maskR), 7)); 554 | rgba8888.val[1] = vmovn_u16(vshrq_n_u16(vandq_u16(bgr555, maskG), 2)); 555 | rgba8888.val[2] = vmovn_u16(vshlq_n_u16(vandq_u16(bgr555, maskB), 3)); 556 | rgba8888.val[3] = alphaFull; 557 | 558 | vst4_u8(rgba8888dst, rgba8888); 559 | 560 | bgr555src += 8; 561 | rgba8888dst += 8 * 4; 562 | } 563 | 564 | mutexUnlock(&videoLock); 565 | 566 | condvarWakeOne(&requestFrameCond); 567 | 568 | u32 *srcImage = conversionBuffer; 569 | 570 | float scale = (float)currentFBHeight / 160.f; 571 | int dstWidth = (int)(scale * 240.f); 572 | int dstHeight = MIN(currentFBHeight, (u32)(scale * 160.f)); 573 | int offsetX = currentFBWidth / 2 - dstWidth / 2; 574 | 575 | if (scalingFilter == filterBilinear) { 576 | int desiredSize = dstWidth * dstHeight * sizeof(u32); 577 | 578 | if (upscaleBufferSize < desiredSize) { 579 | upscaleBuffer = (u32 *)realloc(upscaleBuffer, desiredSize); 580 | upscaleBufferSize = desiredSize; 581 | } 582 | 583 | zoomResizeBilinear_RGB8888((u8 *)upscaleBuffer, dstWidth, dstHeight, (uint8_t *)srcImage, 240, 160, 584 | 256 * sizeof(u32)); 585 | 586 | u32 *src = upscaleBuffer; 587 | u32 *dst = ((u32 *)currentFB) + offsetX; 588 | for (int i = 0; i < dstHeight; i++) { 589 | memcpy(dst, src, dstWidth * sizeof(u32)); 590 | src += dstWidth; 591 | dst += currentFBWidth; 592 | } 593 | } else if (scalingFilter == filterNearest) { 594 | Surface srcSurface, dstSurface; 595 | srcSurface.w = 240; 596 | srcSurface.h = 160; 597 | srcSurface.pixels = srcImage; 598 | srcSurface.pitch = 256 * sizeof(u32); 599 | 600 | dstSurface.w = (int)(scale * 240.f); 601 | dstSurface.h = MIN(currentFBHeight, (u32)(scale * 160.f)); 602 | dstSurface.pixels = ((u32 *)currentFB) + offsetX; 603 | dstSurface.pitch = currentFBWidth * sizeof(u32); 604 | 605 | zoomSurfaceRGBA(&srcSurface, &dstSurface, 0, 0, 0); 606 | } else if (scalingFilter == filterNearestInteger) { 607 | unsigned intScale = (unsigned)floor(scale); 608 | unsigned offsetX = currentFBWidth / 2 - (intScale * 240) / 2; 609 | unsigned offsetY = currentFBHeight / 2 - (intScale * 160) / 2; 610 | 611 | u32 *dst = ((u32 *)currentFB) + offsetX + offsetY * currentFBWidth; 612 | for (int y = 0; y < 160; y++) { 613 | for (int x = 0; x < 240; x++) { 614 | u32 val = srcImage[x + y * 256]; 615 | for (unsigned j = 0; j < intScale * currentFBWidth; j += currentFBWidth) { 616 | for (unsigned i = 0; i < intScale; i++) { 617 | dst[i + j] = val; 618 | } 619 | } 620 | dst += intScale; 621 | } 622 | dst = ((u32 *)currentFB) + offsetX + (y * intScale + offsetY) * currentFBWidth; 623 | } 624 | } 625 | } 626 | 627 | bool actionStopEmulation = false; 628 | bool actionStartEmulation = false; 629 | 630 | uiDraw(keysDown); 631 | 632 | UIResult result; 633 | switch (result = uiLoop(keysDown)) { 634 | case resultSelectedFile: 635 | actionStopEmulation = true; 636 | actionStartEmulation = true; 637 | break; 638 | case resultClose: 639 | uiPopState(); 640 | if (uiGetState() == stateRunning) uiPopState(); 641 | break; 642 | case resultOpenCheats: 643 | uiPushState(stateCheats); 644 | break; 645 | case resultOpenSettings: 646 | uiPushState(stateSettings); 647 | break; 648 | case resultExit: 649 | actionStopEmulation = true; 650 | running = false; 651 | break; 652 | case resultUnpause: 653 | unpause_emulation(); 654 | keysDown = 0; 655 | break; 656 | case resultLoadState: 657 | case resultSaveState: { 658 | mutexLock(&emulationLock); 659 | char stateFilename[PATH_LENGTH]; 660 | romPathWithExt(stateFilename, PATH_LENGTH, "ram"); 661 | 662 | u8 *buffer = (u8 *)malloc(serialize_size); 663 | 664 | if (result == resultLoadState) { 665 | FILE *f = fopen(stateFilename, "rb"); 666 | 667 | if (f) { 668 | if (fread(buffer, 1, serialize_size, f) != serialize_size || 669 | !CPUReadState(buffer, serialize_size)) 670 | uiStatusMsg("Failed to read save state %s", stateFilename); 671 | 672 | fclose(f); 673 | } else 674 | printf("Failed to open %s for read", stateFilename); 675 | } else { 676 | if (!CPUWriteState(buffer, serialize_size)) 677 | uiStatusMsg("Failed to write save state %s", stateFilename); 678 | 679 | FILE *f = fopen(stateFilename, "wb"); 680 | 681 | if (f) { 682 | if (fwrite(buffer, 1, serialize_size, f) != serialize_size) 683 | printf("Failed to write to %s", stateFilename); 684 | fclose(f); 685 | } else 686 | printf("Failed to open %s for write", stateFilename); 687 | } 688 | 689 | free(buffer); 690 | mutexUnlock(&emulationLock); 691 | } break; 692 | case resultSaveSettings: 693 | applyConfig(); 694 | uiSaveSettings(); 695 | uiPopState(); 696 | break; 697 | case resultCancelSettings: 698 | uiCancelSettings(); 699 | uiPopState(); 700 | break; 701 | case resultCloseCheats: 702 | cheatsWriteHumanReadable(cheatsFilename); 703 | uiPopState(); 704 | break; 705 | case resultNone: 706 | default: 707 | break; 708 | } 709 | 710 | mutexLock(&inputLock); 711 | inputTransferKeysHeld |= keysDown; 712 | inputTransferKeysHeld &= ~keysUp; 713 | mutexUnlock(&inputLock); 714 | 715 | if (actionStopEmulation && emulationRunning) { 716 | mutexLock(&emulationLock); 717 | retro_unload_game(); 718 | mutexUnlock(&emulationLock); 719 | } 720 | 721 | if (actionStartEmulation) { 722 | mutexLock(&emulationLock); 723 | 724 | uiPushState(stateRunning); 725 | 726 | uiGetSelectedFile(currentRomPath, PATH_LENGTH); 727 | romPathWithExt(saveFilename, PATH_LENGTH, "sav"); 728 | romPathWithExt(cheatsFilename, PATH_LENGTH, "txt"); 729 | 730 | retro_load_game(); 731 | 732 | SetFrameskip(frameSkipValues[frameSkip]); 733 | 734 | emulationRunning = true; 735 | emulationPaused = false; 736 | 737 | cheatsDeleteAll(true); 738 | cheatsReadHumanReadable(cheatsFilename); 739 | 740 | mutexUnlock(&emulationLock); 741 | } 742 | 743 | if (emulationRunning && !emulationPaused && keysDown & KEY_X) pause_emulation(); 744 | 745 | if (emulationRunning && !emulationPaused && --autosaveCountdown == 0) { 746 | mutexLock(&emulationLock); 747 | CPUWriteBatteryFile(saveFilename); 748 | mutexUnlock(&emulationLock); 749 | } 750 | 751 | #if NXLINK_STDIO 752 | // going to use this to debug the 753 | double frameEndTime = svcGetSystemTick() * SECONDS_PER_TICKS; 754 | 755 | double dt = frameEndTime - frameStartTime; 756 | frameTimeSum += dt; 757 | frameTimeFrames++; 758 | 759 | if (frameTimeSum >= 1) { 760 | printf("avg. frametime %fms\n", 1000.0 * (frameTimeSum / (double)frameTimeFrames)); 761 | 762 | frameTimeSum = 0; 763 | frameTimeFrames = 0; 764 | } 765 | #endif 766 | 767 | gfxFlushBuffers(); 768 | gfxSwapBuffers(); 769 | gfxWaitForVsync(); 770 | } 771 | 772 | threadWaitForExit(&mainThread); 773 | threadClose(&mainThread); 774 | 775 | uiDeinit(); 776 | 777 | cheatListDeinit(); 778 | free(conversionBuffer); 779 | free(videoTransferBuffer); 780 | 781 | threadWaitForExit(&audio_thread); 782 | threadClose(&audio_thread); 783 | 784 | audoutStopAudioOut(); 785 | audoutExit(); 786 | 787 | free(audioTransferBuffer); 788 | 789 | fontExit(); 790 | 791 | gfxExit(); 792 | plExit(); 793 | 794 | timeExit(); 795 | 796 | romfsExit(); 797 | 798 | zoomDeinit(); 799 | if (upscaleBuffer != NULL) free(upscaleBuffer); 800 | 801 | #ifdef NXLINK_STDIO 802 | socketExit(); 803 | #endif 804 | 805 | return 0; 806 | } -------------------------------------------------------------------------------- /source/switch/stb_image.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include "stb_image.h" -------------------------------------------------------------------------------- /source/switch/theme.cpp: -------------------------------------------------------------------------------- 1 | #include "theme.h" 2 | 3 | static Image btnADark, btnALight, btnBDark, btnBLight, btnXDark, btnXLight, btnYDark, btnYLight, splashWhite, splashBlack; 4 | 5 | void themeInit() { 6 | imageLoad(&btnADark, "romfs:/btnADark.png"); 7 | imageLoad(&btnALight, "romfs:/btnALight.png"); 8 | imageLoad(&btnBDark, "romfs:/btnBDark.png"); 9 | imageLoad(&btnBLight, "romfs:/btnBLight.png"); 10 | imageLoad(&btnXDark, "romfs:/btnXDark.png"); 11 | imageLoad(&btnXLight, "romfs:/btnXLight.png"); 12 | imageLoad(&btnYDark, "romfs:/btnYDark.png"); 13 | imageLoad(&btnYLight, "romfs:/btnYLight.png"); 14 | 15 | imageLoad(&splashWhite, "romfs:/splashWhite.png"); 16 | imageLoad(&splashBlack, "romfs:/splashBlack.png"); 17 | } 18 | 19 | void themeDeinit() { 20 | imageDeinit(&splashWhite); 21 | imageDeinit(&splashBlack); 22 | 23 | imageDeinit(&btnADark); 24 | imageDeinit(&btnALight); 25 | imageDeinit(&btnBDark); 26 | imageDeinit(&btnBLight); 27 | imageDeinit(&btnXDark); 28 | imageDeinit(&btnXLight); 29 | imageDeinit(&btnYDark); 30 | imageDeinit(&btnYLight); 31 | } 32 | 33 | void themeSet(themeMode_t t) { 34 | switch (t) { 35 | case modeLight: 36 | currentTheme.backgroundColor = MakeColor(239, 239, 239, 255); 37 | currentTheme.textColor = MakeColor(45, 45, 45, 255); 38 | currentTheme.textActiveColor = MakeColor(50, 80, 240, 255); 39 | currentTheme.textActiveBGColor = MakeColor(253, 253, 253, 255); 40 | currentTheme.keyboardBackgroundColor = MakeColor(240, 240, 240, 255); 41 | currentTheme.keyboardKeyBackgroundColor = MakeColor(231, 231, 231, 255); 42 | currentTheme.keyboardSPKeyTextColor = MakeColor(155, 155, 155, 255); 43 | currentTheme.keyboardSPKeyBackgroundColor = MakeColor(218, 218, 218, 255); 44 | currentTheme.keyboardOKKeyTextColor = MakeColor(253, 253, 253, 255); 45 | currentTheme.keyboardOKKeyBackgroundColor = MakeColor(50, 80, 240, 255); 46 | currentTheme.keyboardHighlightColor = MakeColor(16, 178, 203, 255); 47 | currentTheme.btnA = btnADark; 48 | currentTheme.btnB = btnBDark; 49 | currentTheme.btnX = btnXDark; 50 | currentTheme.btnY = btnYDark; 51 | currentTheme.splashImage = splashWhite; 52 | break; 53 | case modeDark: 54 | currentTheme.backgroundColor = MakeColor(45, 45, 45, 255); 55 | currentTheme.textColor = MakeColor(255, 255, 255, 255); 56 | currentTheme.textActiveColor = MakeColor(0, 255, 197, 255); 57 | currentTheme.textActiveBGColor = MakeColor(33, 34, 39, 255); 58 | currentTheme.keyboardBackgroundColor = MakeColor(70, 70, 70, 255); 59 | currentTheme.keyboardKeyBackgroundColor = MakeColor(79, 79, 79, 255); 60 | currentTheme.keyboardSPKeyTextColor = MakeColor(150, 150, 150, 255); 61 | currentTheme.keyboardSPKeyBackgroundColor = MakeColor(96, 96, 96, 255); 62 | currentTheme.keyboardOKKeyTextColor = MakeColor(33, 77, 90, 255); 63 | currentTheme.keyboardOKKeyBackgroundColor = MakeColor(0, 248, 211, 255); 64 | currentTheme.keyboardHighlightColor = MakeColor(134, 241, 247, 255); 65 | currentTheme.btnA = btnALight; 66 | currentTheme.btnB = btnBLight; 67 | currentTheme.btnX = btnXLight; 68 | currentTheme.btnY = btnYLight; 69 | currentTheme.splashImage = splashBlack; 70 | break; 71 | } 72 | } 73 | 74 | theme_t currentTheme; -------------------------------------------------------------------------------- /source/switch/theme.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "colors.h" 4 | #include "image.h" 5 | 6 | typedef struct { 7 | color_t backgroundColor; 8 | color_t textColor; 9 | color_t textActiveColor; 10 | color_t textActiveBGColor; 11 | color_t keyboardBackgroundColor; 12 | color_t keyboardKeyBackgroundColor; 13 | color_t keyboardSPKeyTextColor; 14 | color_t keyboardSPKeyBackgroundColor; 15 | color_t keyboardOKKeyTextColor; 16 | color_t keyboardOKKeyBackgroundColor; 17 | color_t keyboardHighlightColor; 18 | Image btnA; 19 | Image btnB; 20 | Image btnX; 21 | Image btnY; 22 | Image splashImage; 23 | } theme_t; 24 | 25 | typedef enum { 26 | modeLight, 27 | modeDark, 28 | } themeMode_t; 29 | 30 | enum { switchTheme, darkTheme, lightTheme }; 31 | 32 | void themeInit(); 33 | void themeDeinit(); 34 | 35 | void themeSet(themeMode_t t); 36 | 37 | extern theme_t currentTheme; -------------------------------------------------------------------------------- /source/switch/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef union { 6 | u32 abgr; 7 | struct { 8 | u8 r,g,b,a; 9 | }; 10 | } color_t; 11 | 12 | typedef struct { 13 | u8 width, height; 14 | int8_t posX, posY, advance, pitch; 15 | const u8* data; 16 | } glyph_t; -------------------------------------------------------------------------------- /source/switch/ui.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | extern "C" { 17 | #include "ini/ini.h" 18 | #include "localtime.h" 19 | } 20 | 21 | #include "draw.h" 22 | #include "image.h" 23 | #include "hbkbd.h" 24 | 25 | #include "../system.h" 26 | #include "../types.h" 27 | #include "../GBACheats.h" 28 | #include "colors.h" 29 | #include "ui.h" 30 | #include "util.h" 31 | 32 | #define FILENAMEBUFFER_SIZE (1024 * 32) // 32kb 33 | #define FILENAMES_COUNT_MAX 2048 34 | 35 | #define SETTINGS_MAX (128) 36 | 37 | static char* filenameBuffer = NULL; 38 | static char* filenames[FILENAMES_COUNT_MAX]; 39 | static int filenamesCount = 0; 40 | 41 | static char statusMessage[FILENAMES_COUNT_MAX]; 42 | static int statusMessageFadeout = 0; 43 | 44 | static char selectedPath[PATH_LENGTH] = {'\0'}; 45 | static char currentDirectory[PATH_LENGTH] = {'\0'}; 46 | static int cursor = 0; 47 | static int scroll = 0; 48 | 49 | static const char* pauseMenuItems[] = {"Continue", "Load Savestate", "Write Savestate", "Cheats", "Settings", "Exit"}; 50 | 51 | const char* themeOptions[] = {"Switch", "Dark", "Light"}; 52 | 53 | Setting* settings; 54 | static int settingsMetaStart = 0; 55 | static int settingsCount = 0; 56 | static char* settingStrings[SETTINGS_MAX]; 57 | 58 | #define UI_STATESTACK_MAX 4 59 | static UIState uiStateStack[UI_STATESTACK_MAX]; 60 | static int uiStateStackCount = 0; 61 | 62 | static int rowsVisible = 0; 63 | 64 | static Image gbaImage, logoSmall; 65 | 66 | static u32 btnMargin = 0; 67 | 68 | static const char* settingsPath = "vba-switch.ini"; 69 | 70 | const char* stringsNoYes[] = {"No", "Yes"}; 71 | 72 | static ColorSetId switchColorSetID; 73 | static uint32_t themeM = 0; 74 | 75 | static int lastDst = 80; 76 | static int splashTime = 240; 77 | static u32 splashEnabled = 1; 78 | 79 | static void generateSettingString(Setting* setting) { 80 | if (!setting->meta) { 81 | snprintf(setting->generatedString, sizeof(setting->generatedString) - 1, "%s: %s", setting->name, 82 | setting->strValues[*setting->valueIdx]); 83 | } else { 84 | strcpy_safe(setting->generatedString, setting->name, sizeof(setting->generatedString)); 85 | } 86 | } 87 | 88 | void uiStatusMsg(const char* format, ...) { 89 | statusMessageFadeout = 500; 90 | va_list args; 91 | va_start(args, format); 92 | vsnprintf(statusMessage, sizeof(statusMessage) / sizeof(char), format, args); 93 | va_end(args); 94 | } 95 | 96 | static void enterDirectory() { 97 | filenamesCount = FILENAMES_COUNT_MAX; 98 | getDirectoryContents(filenameBuffer, &filenames[0], &filenamesCount, currentDirectory, "gba"); 99 | 100 | cursor = 0; 101 | scroll = 0; 102 | } 103 | 104 | void uiInit() { 105 | setupLocalTimeOffset(); 106 | 107 | filenameBuffer = (char*)malloc(FILENAMEBUFFER_SIZE); 108 | strcpy_safe(currentDirectory, "", PATH_LENGTH); 109 | enterDirectory(); 110 | 111 | settings = (Setting*)malloc(SETTINGS_MAX * sizeof(Setting)); 112 | 113 | themeInit(); 114 | 115 | imageLoad(&gbaImage, "romfs:/gba.png"); 116 | imageLoad(&logoSmall, "romfs:/logoSmall.png"); 117 | 118 | setsysGetColorSetId(&switchColorSetID); 119 | 120 | uiAddSetting("Enable splash screen", &splashEnabled, 2, stringsNoYes); 121 | uiAddSetting("Theme", &themeM, 3, themeOptions); 122 | 123 | uiPushState(stateFileselect); 124 | } 125 | 126 | void uiDeinit() { 127 | themeDeinit(); 128 | 129 | imageDeinit(&gbaImage); 130 | imageDeinit(&logoSmall); 131 | 132 | free(filenameBuffer); 133 | free(settings); 134 | } 135 | 136 | void uiFinaliseAndLoadSettings() { 137 | settingsMetaStart = settingsCount; 138 | 139 | // uiAddSetting("Remap Buttons", NULL, result) 140 | uiAddSetting("Save and return", NULL, resultSaveSettings, NULL, true); 141 | uiAddSetting("Cancel", NULL, resultCancelSettings, NULL, true); 142 | 143 | ini_t* cfg = ini_load(settingsPath); 144 | if (cfg) { 145 | for (int i = 0; i < settingsMetaStart; i++) { 146 | if (ini_sget(cfg, "misc", settings[i].name, "%d", settings[i].valueIdx)) generateSettingString(&settings[i]); 147 | } 148 | 149 | ini_free(cfg); 150 | } 151 | } 152 | 153 | void uiSaveSettings() { 154 | FILE* f = fopen(settingsPath, "wt"); 155 | if (f) { 156 | fprintf(f, "[Misc]\n"); 157 | 158 | for (int i = 0; i < settingsMetaStart; i++) fprintf(f, "%s=%d\n", settings[i].name, *settings[i].valueIdx); 159 | 160 | fclose(f); 161 | } 162 | } 163 | 164 | void uiCancelSettings() { 165 | ini_t* cfg = ini_load(settingsPath); 166 | if (cfg) { 167 | for (int i = 0; i < settingsMetaStart; i++) { 168 | if (ini_sget(cfg, "misc", settings[i].name, "%d", settings[i].valueIdx)) generateSettingString(&settings[i]); 169 | } 170 | 171 | ini_free(cfg); 172 | } 173 | } 174 | 175 | void uiGetSelectedFile(char* out, int outLength) { strcpy_safe(out, selectedPath, outLength); } 176 | 177 | void uiDraw(u32 keysDown) { 178 | UIState state = uiGetState(); 179 | 180 | if (state == stateRunning) return; 181 | 182 | if (themeM == switchTheme) 183 | themeSet((themeMode_t)switchColorSetID); 184 | else if (themeM == lightTheme) 185 | themeSet(modeLight); 186 | else 187 | themeSet(modeDark); 188 | 189 | btnMargin = 0; 190 | 191 | hbkbd::init(); 192 | 193 | int scrollAmount = 0; 194 | if (keysDown & KEY_DOWN) scrollAmount = 1; 195 | if (keysDown & KEY_UP) scrollAmount = -1; 196 | if (keysDown & KEY_LEFT) scrollAmount = -5; 197 | if (keysDown & KEY_RIGHT) scrollAmount = 5; 198 | 199 | const char** menu = NULL; 200 | int menuItemsCount; 201 | if (state == stateSettings) { 202 | menu = (const char**)settingStrings; 203 | menuItemsCount = settingsCount; 204 | } else if (state == stateCheats) { 205 | menu = (const char**)cheatsStringList; 206 | menuItemsCount = cheatsNumber + 1; 207 | } else if (state == statePaused) { 208 | menu = pauseMenuItems; 209 | menuItemsCount = sizeof(pauseMenuItems) / sizeof(pauseMenuItems[0]); 210 | } else { 211 | menu = (const char**)filenames; 212 | menuItemsCount = filenamesCount; 213 | } 214 | 215 | drawRect(0, 0, currentFBWidth, currentFBHeight, currentTheme.backgroundColor); 216 | 217 | imageDraw(&logoSmall, 52, 15); 218 | 219 | int i = 0; 220 | int separator = 40; 221 | int menuMarginTop = 80; 222 | 223 | rowsVisible = (currentFBHeight - 70 - menuMarginTop) / separator; 224 | 225 | if (scrollAmount > 0) { 226 | for (int i = 0; i < scrollAmount; i++) { 227 | if (cursor < menuItemsCount - 1) { 228 | cursor++; 229 | if (cursor - scroll >= rowsVisible) { 230 | scroll++; 231 | } 232 | } 233 | } 234 | } else if (scrollAmount < 0) { 235 | for (int i = 0; i < -scrollAmount; i++) { 236 | if (cursor > 0) { 237 | cursor--; 238 | if (cursor - scroll < 0) { 239 | scroll--; 240 | } 241 | } 242 | } 243 | } 244 | 245 | for (int j = scroll; j < menuItemsCount; j++) { 246 | u32 h, w; 247 | getTextDimensions(font16, menu[j], &w, &h); 248 | u32 heightOffset = (40 - h) / 2; 249 | u32 textMarginTop = heightOffset + menuMarginTop; 250 | 251 | if (i * separator + textMarginTop > currentFBHeight - 85) break; 252 | 253 | if (i + scroll == cursor) { 254 | float dst = i * separator + menuMarginTop; 255 | // float delta = (dst - lastDst) / 2.2; 256 | // dst = floor(lastDst + delta); 257 | 258 | drawRect(0, (u32)dst, currentFBWidth / 1.25, separator, currentTheme.textActiveBGColor); 259 | if (lastDst == dst) 260 | drawText(font16, 60, i * separator + textMarginTop, currentTheme.textActiveColor, menu[j]); 261 | else 262 | drawText(font16, 60, i * separator + textMarginTop, currentTheme.textColor, menu[j]); 263 | lastDst = dst; 264 | } else { 265 | drawText(font16, 60, i * separator + textMarginTop, currentTheme.textColor, menu[j]); 266 | } 267 | 268 | i++; 269 | if (i >= rowsVisible) break; 270 | } 271 | 272 | struct tm* timeStruct = getRealLocalTime(); 273 | 274 | drawText(font16, currentFBWidth - 115, 35, currentTheme.textColor, "%02i:%02i", timeStruct->tm_hour, timeStruct->tm_min); 275 | 276 | drawRect((u32)((currentFBWidth - 1220) / 2), currentFBHeight - 73, 1220, 1, currentTheme.textColor); 277 | 278 | // UI Buttom Bar Buttons Drawing routines 279 | switch (state) { 280 | case stateFileselect: 281 | drawText(font16, 60, currentFBHeight - 43, currentTheme.textColor, currentDirectory); 282 | uiDrawTipButton(buttonB, 1, "Back"); 283 | uiDrawTipButton(buttonA, 2, "Open"); 284 | uiDrawTipButton(buttonX, 3, "Exit VBA Next"); 285 | break; 286 | case stateCheats: 287 | uiDrawTipButton(buttonB, 1, "Back"); 288 | uiDrawTipButton(buttonA, 2, "Toggle Cheat"); 289 | uiDrawTipButton(buttonY, 3, "Delete Cheat"); 290 | uiDrawTipButton(buttonX, 4, "Exit VBA Next"); 291 | break; 292 | case stateSettings: 293 | uiDrawTipButton(buttonB, 1, "Cancel"); 294 | uiDrawTipButton(buttonA, 2, "OK"); 295 | uiDrawTipButton(buttonX, 3, "Exit VBA Next"); 296 | break; 297 | case statePaused: 298 | uiDrawTipButton(buttonB, 1, "Return Game"); 299 | uiDrawTipButton(buttonA, 2, "OK"); 300 | uiDrawTipButton(buttonX, 3, "Exit VBA Next"); 301 | break; 302 | default: 303 | break; 304 | } 305 | 306 | if (splashTime > 0) { 307 | if (splashEnabled) imageDraw(¤tTheme.splashImage, 0, 0, splashTime <= 120 ? splashTime * 255 / 120 : 255); 308 | splashTime -= 5; 309 | } 310 | } 311 | 312 | UIResult uiLoop(u32 keysDown) { 313 | UIState state = uiGetState(); 314 | if (state == stateRemapButtons) { 315 | // imageDraw(fb, currentFBWidth, currentFBHeight, &gbaImage, 0, 0); 316 | } else if (uiGetState() != stateRunning) { 317 | if (keysDown & KEY_X) return resultExit; 318 | 319 | if (keysDown & KEY_A || keysDown & KEY_B) { 320 | if (state == stateFileselect) { 321 | if (keysDown & KEY_B) cursor = 0; 322 | 323 | char path[PATH_LENGTH] = {'\0'}; 324 | 325 | if (!strcmp(filenames[cursor], "..")) { 326 | int length = strlen(currentDirectory); 327 | for (int i = length - 1; i >= 0; i--) { 328 | if (currentDirectory[i] == '/') { 329 | strncpy(path, currentDirectory, i); 330 | path[i] = '\0'; 331 | break; 332 | } 333 | } 334 | } else 335 | snprintf(path, PATH_LENGTH, "%s/%s", currentDirectory, filenames[cursor]); 336 | 337 | if (isDirectory(path)) { 338 | strcpy_safe(currentDirectory, path, PATH_LENGTH); 339 | enterDirectory(); 340 | } else { 341 | strcpy_safe(selectedPath, path, PATH_LENGTH); 342 | return resultSelectedFile; 343 | } 344 | } else if (state == stateSettings) { 345 | if (keysDown & KEY_B) return resultCancelSettings; 346 | 347 | Setting* setting = &settings[cursor]; 348 | 349 | if (setting->meta) return (UIResult)setting->valuesCount; 350 | *setting->valueIdx += (keysDown & KEY_A ? 1 : -1); 351 | if (*setting->valueIdx == UINT32_MAX) *setting->valueIdx = setting->valuesCount - 1; 352 | if (*setting->valueIdx >= setting->valuesCount) *setting->valueIdx = 0; 353 | 354 | generateSettingString(setting); 355 | 356 | return resultNone; 357 | } else if (state == stateCheats) { 358 | if ((keysDown & KEY_B)) return resultCloseCheats; 359 | 360 | if (cursor < cheatsNumber) { 361 | if(cheatsList[cursor].enabled) 362 | cheatsDisable(cursor); 363 | else 364 | cheatsEnable(cursor); 365 | } else { 366 | std::string cheatCode = hbkbd::keyboard("Enter the gameshark cheat"); 367 | cheatCode.erase(remove_if(cheatCode.begin(), cheatCode.end(), isspace), cheatCode.end()); 368 | std::transform(cheatCode.begin(), cheatCode.end(),cheatCode.begin(), ::toupper); 369 | 370 | if(cheatCode.length() == 16) { 371 | // Gameshark v3 372 | cheatsAddGSACode(cheatCode.c_str(), hbkbd::keyboard("Enter a description for the cheat").c_str(), true); 373 | } else if(cheatCode.length() == 12) { 374 | // Gameshark v1/2 375 | cheatCode.insert(8, "0000"); 376 | cheatsAddGSACode(cheatCode.c_str(), hbkbd::keyboard("Enter a description for the cheat").c_str(), false); 377 | } else { 378 | uiStatusMsg("Invalid cheat-code. Are you sure this is a gameshark-code?"); 379 | return resultNone; 380 | } 381 | #ifdef NXLINK_STDIO 382 | printf("Added cheat code: %s\n", cheatCode.c_str()); 383 | #endif 384 | } 385 | 386 | } else { 387 | if (keysDown & KEY_B) return resultUnpause; 388 | 389 | switch (cursor) { 390 | case 0: 391 | return resultUnpause; 392 | case 1: 393 | return resultLoadState; 394 | case 2: 395 | return resultSaveState; 396 | case 3: 397 | return resultOpenCheats; 398 | case 4: 399 | return resultOpenSettings; 400 | case 5: 401 | return resultClose; 402 | } 403 | } 404 | } else if (keysDown & KEY_Y) { 405 | if (state == stateCheats && cursor < cheatsNumber) { 406 | cheatsDelete(cursor, true); 407 | } 408 | } 409 | } 410 | 411 | if (statusMessageFadeout > 0) { 412 | int fadeout = statusMessageFadeout > 255 ? 255 : statusMessageFadeout; 413 | drawText(font16, 60, currentFBHeight - 43, MakeColor(58, 225, 208, fadeout), statusMessage); 414 | statusMessageFadeout -= 10; 415 | } 416 | 417 | return resultNone; 418 | } 419 | 420 | void uiPushState(UIState state) { 421 | if (uiStateStackCount < UI_STATESTACK_MAX) 422 | uiStateStack[uiStateStackCount++] = state; 423 | else 424 | printf("warning: push UI stack further than max\n"); 425 | 426 | cursor = 0; 427 | scroll = 0; 428 | } 429 | 430 | void uiPopState() { 431 | if (uiStateStackCount > 0) 432 | uiStateStackCount--; 433 | else 434 | printf("warning: pop empty UI stack\n"); 435 | 436 | cursor = 0; 437 | scroll = 0; 438 | } 439 | 440 | UIState uiGetState() { 441 | if (uiStateStackCount == 0) { 442 | printf("warning: uiGetState() for empty UI stack"); 443 | return stateFileselect; 444 | } 445 | return uiStateStack[uiStateStackCount - 1]; 446 | } 447 | 448 | void uiAddSetting(const char* name, u32* valueIdx, u32 valuesCount, const char* strValues[], bool meta) { 449 | settings[settingsCount].name = name; 450 | settings[settingsCount].valueIdx = valueIdx; 451 | settings[settingsCount].valuesCount = valuesCount; 452 | settings[settingsCount].strValues = strValues; 453 | settings[settingsCount].meta = meta; 454 | 455 | settingStrings[settingsCount] = settings[settingsCount].generatedString; 456 | 457 | generateSettingString(&settings[settingsCount]); 458 | 459 | settingsCount++; 460 | } 461 | 462 | void uiDrawTipButton(buttonType type, u32 pos, const char* text) { 463 | u32 h, w; 464 | getTextDimensions(font16, text, &w, &h); 465 | h = (73 - h) / 2; 466 | 467 | w += 25 + 13; 468 | pos == 1 ? btnMargin = w + 60 : btnMargin += w + 40; 469 | u32 x = currentFBWidth - btnMargin; 470 | u32 y = currentFBHeight - 50; 471 | 472 | switch (type) { 473 | case buttonA: 474 | imageDraw(¤tTheme.btnA, x, y); 475 | drawText(font16, x + 25 + 13, currentFBHeight - 73 + h, currentTheme.textColor, text); 476 | break; 477 | case buttonB: 478 | imageDraw(¤tTheme.btnB, x, y); 479 | drawText(font16, x + 25 + 13, currentFBHeight - 73 + h, currentTheme.textColor, text); 480 | break; 481 | case buttonY: 482 | imageDraw(¤tTheme.btnY, x, y); 483 | drawText(font16, x + 25 + 13, currentFBHeight - 73 + h, currentTheme.textColor, text); 484 | break; 485 | case buttonX: 486 | imageDraw(¤tTheme.btnX, x, y); 487 | drawText(font16, x + 25 + 13, currentFBHeight - 73 + h, currentTheme.textColor, text); 488 | break; 489 | default: 490 | break; 491 | } 492 | } -------------------------------------------------------------------------------- /source/switch/ui.h: -------------------------------------------------------------------------------- 1 | #ifndef __UI_H__ 2 | #define __UI_H__ 3 | 4 | #include "theme.h" 5 | 6 | typedef enum { 7 | resultSelectedFile, 8 | resultClose, 9 | resultUnpause, 10 | resultExit, 11 | resultNone, 12 | resultLoadState, 13 | resultSaveState, 14 | resultSettingsChanged, 15 | resultShowCredits, 16 | resultOpenCheats, 17 | resultCloseCheats, 18 | resultOpenSettings, 19 | resultSaveSettings, 20 | resultCancelSettings 21 | } UIResult; 22 | 23 | typedef enum { stateRunning, stateFileselect, statePaused, stateSettings, stateRemapButtons, stateCheats } UIState; 24 | 25 | struct Setting { 26 | const char* name; 27 | u32 valuesCount, *valueIdx; 28 | const char** strValues; 29 | char generatedString[256]; 30 | bool meta; 31 | }; 32 | 33 | enum buttonType { buttonA, buttonB, buttonY, buttonX }; 34 | 35 | #define PATH_LENGTH 512 36 | 37 | void uiInit(); 38 | void uiDeinit(); 39 | 40 | void uiGetSelectedFile(char* out, int outLength); 41 | 42 | UIResult uiLoop(u32 keysDown); 43 | void uiPushState(UIState state); 44 | void uiPopState(); 45 | UIState uiGetState(); 46 | 47 | void uiAddSetting(const char* name, u32* valueIdx, u32 valuesCount, const char* strValues[], bool meta = false); 48 | void uiFinaliseAndLoadSettings(); 49 | void uiSaveSettings(); 50 | void uiCancelSettings(); 51 | 52 | extern const char* stringsNoYes[]; 53 | 54 | void uiStatusMsg(const char* fmt, ...); 55 | void uiDrawTipButton(buttonType type, u32 pos, const char* text); 56 | 57 | void uiDraw(u32 keysDown); 58 | 59 | #endif -------------------------------------------------------------------------------- /source/switch/util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | struct alphabetize { 10 | inline bool operator()(char* a, char* b) { return strcasecmp(a, b) < 0; } 11 | }; 12 | 13 | static int sortAlpha(const void* a, const void* b) { return strcasecmp(*((const char**)a), *((const char**)b)); } 14 | 15 | bool isDirectory(char* path) { 16 | DIR* dir = opendir(path); 17 | if (!dir) { 18 | return false; 19 | } 20 | 21 | closedir(dir); 22 | return true; 23 | } 24 | 25 | void addString(char* filenameBuffer, char** filenames, int* filenamesCount, char** nextFilename, const char* string) { 26 | filenames[(*filenamesCount)++] = *nextFilename; 27 | strcpy(*nextFilename, string); 28 | *nextFilename += strlen(string) + 1; 29 | } 30 | 31 | void getDirectoryContents(char* filenameBuffer, char** filenames, int* filenamesCount, const char* directory, const char* extensionFilter) { 32 | char slash[strlen(directory) + 2]; 33 | snprintf(slash, sizeof(slash), "%s/", directory); 34 | DIR* dir = opendir(slash); 35 | 36 | int maxFilenamesCount = *filenamesCount; 37 | 38 | char* nextFilename = filenameBuffer; 39 | *filenamesCount = 0; 40 | 41 | addString(filenameBuffer, filenames, filenamesCount, &nextFilename, ".."); 42 | 43 | if (dir != NULL) { 44 | for (int i = 0; i < maxFilenamesCount; i++) { 45 | struct dirent* ent = readdir(dir); 46 | if (ent == NULL) { 47 | break; 48 | } 49 | 50 | char path[strlen(directory) + strlen(ent->d_name) + 2]; 51 | snprintf(path, sizeof(path) / sizeof(char), "%s/%s", directory, ent->d_name); 52 | if (isDirectory(path)) { 53 | addString(filenameBuffer, filenames, filenamesCount, &nextFilename, ent->d_name); 54 | } else { 55 | const char* dot = strrchr(path, '.'); 56 | if (dot && dot != path && !strcasecmp(dot + 1, extensionFilter)) { 57 | addString(filenameBuffer, filenames, filenamesCount, &nextFilename, ent->d_name); 58 | } 59 | } 60 | } 61 | 62 | closedir(dir); 63 | } 64 | 65 | qsort(filenames + 1, (*filenamesCount) - 1, // ".." should stay at the to 66 | sizeof(char*), &sortAlpha); 67 | } 68 | 69 | void strcpy_safe(char* dst, const char* src, unsigned src_length) { 70 | unsigned i = 0; 71 | while (src[i] != '\0' && i < src_length - 2) { 72 | dst[i] = src[i]; 73 | i++; 74 | } 75 | dst[i] = '\0'; 76 | } 77 | -------------------------------------------------------------------------------- /source/switch/util.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTIL_H__ 2 | #define __UTIL_H__ 3 | 4 | #include 5 | #include 6 | 7 | 8 | void addString(char* filenameBuffer, char** filenames, int* filenamesCount, char** nextFilename, const char* string); 9 | 10 | bool isDirectory(char* path); 11 | void getDirectoryContents(char* filenameBuffer, char** filenames, int* filenamesCount, const char* directory, const char* extensionFilter); 12 | 13 | #include 14 | 15 | /* 16 | Usually I don't write comments to explain my functions, but since is a "safe" function, 17 | it's probably the best idea to do this "safe" thing from start to finish. 18 | 19 | Why do I write such a long text? Because it's simply a bit frustrating that there's 20 | not such a function available in the standard library. 21 | 22 | Description: 23 | Writes the string stored at the point src is pointing to dst, including the null-terminating 24 | character. If src contains more bytes than dst can store, the copying is aborted on the character 25 | next to the last. The last character dst can hold is then used to store the string terminator. 26 | 27 | Arguments: 28 | dst_length contains the amount of bytes dst can hold including the null-terminating character. 29 | 30 | Short form: 31 | Use this instead of strcpy or strncpy. Put the length of dst in dst_length. 32 | 33 | TODO: add Unicode/UTF-8 support 34 | 35 | */ 36 | void strcpy_safe(char* dst, const char* src, unsigned dst_length); 37 | 38 | #endif -------------------------------------------------------------------------------- /source/switch/zoom.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "zoom.h" 7 | 8 | #define TMP_BUFFERS 4 9 | 10 | static void *zoomTmpBuffers[TMP_BUFFERS]; 11 | static int zoomTmpBufferSize[TMP_BUFFERS]; 12 | static int zoomTmpBuffersNext = 0; 13 | 14 | void zoomInit(int maxWidth, int maxHeight) { 15 | for (int i = 0; i < TMP_BUFFERS; i++) { 16 | zoomTmpBuffers[i] = NULL; 17 | zoomTmpBufferSize[i] = 0; 18 | } 19 | } 20 | void zoomDeinit() { 21 | for (int i = 0; i < TMP_BUFFERS; i++) free(zoomTmpBuffers[i]); 22 | } 23 | 24 | static inline void *tmpBufferAlloc(size_t size) { 25 | if (zoomTmpBufferSize[zoomTmpBuffersNext] < size) { 26 | zoomTmpBuffers[zoomTmpBuffersNext] = realloc(zoomTmpBuffers[zoomTmpBuffersNext], size); 27 | zoomTmpBufferSize[zoomTmpBuffersNext] = size; 28 | } 29 | return zoomTmpBuffers[zoomTmpBuffersNext++]; 30 | } 31 | static inline void tmpBuffersReset() { zoomTmpBuffersNext = 0; } 32 | 33 | /* 34 | 35 | The following image scaling function is based of SDL_gfx, but detached from SDL and other dependencies 36 | https://sourceforge.net/projects/sdlgfx/ 37 | 38 | Relevant file: 39 | https://sourceforge.net/p/sdlgfx/code/HEAD/tree/SDL_rotozoom.c 40 | 41 | What follows is the license header of SDL_rotozoom.c: 42 | */ 43 | /* 44 | 45 | SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces 46 | 47 | Copyright (C) 2001-2012 Andreas Schiffler 48 | 49 | This software is provided 'as-is', without any express or implied 50 | warranty. In no event will the authors be held liable for any damages 51 | arising from the use of this software. 52 | 53 | Permission is granted to anyone to use this software for any purpose, 54 | including commercial applications, and to alter it and redistribute it 55 | freely, subject to the following restrictions: 56 | 57 | 1. The origin of this software must not be misrepresented; you must not 58 | claim that you wrote the original software. If you use this software 59 | in a product, an acknowledgment in the product documentation would be 60 | appreciated but is not required. 61 | 62 | 2. Altered source versions must be plainly marked as such, and must not be 63 | misrepresented as being the original software. 64 | 65 | 3. This notice may not be removed or altered from any source 66 | distribution. 67 | 68 | Andreas Schiffler -- aschiffler at ferzkopp dot net 69 | 70 | */ 71 | int zoomSurfaceRGBA(Surface *src, Surface *dst, int flipx, int flipy, int smooth) { 72 | int x, y, sx, sy, ssx, ssy, *sax, *say, *csax, *csay, *salast, csx, csy, ex, ey, cx, cy, sstep, sstepx, sstepy; 73 | ColorRGBA *c00, *c01, *c10, *c11; 74 | ColorRGBA *sp, *csp, *dp; 75 | int spixelgap, spixelw, spixelh, dgap, t1, t2; 76 | 77 | /* 78 | * Allocate memory for row/column increments 79 | */ 80 | if ((sax = (int *)tmpBufferAlloc((dst->w + 1) * sizeof(uint32_t))) == NULL) { 81 | return (-1); 82 | } 83 | if ((say = (int *)tmpBufferAlloc((dst->h + 1) * sizeof(uint32_t))) == NULL) { 84 | return (-1); 85 | } 86 | 87 | /* 88 | * Precalculate row increments 89 | */ 90 | spixelw = (src->w - 1); 91 | spixelh = (src->h - 1); 92 | if (smooth) { 93 | sx = (int)(65536.0 * (float)spixelw / (float)(dst->w - 1)); 94 | sy = (int)(65536.0 * (float)spixelh / (float)(dst->h - 1)); 95 | } else { 96 | sx = (int)(65536.0 * (float)(src->w) / (float)(dst->w)); 97 | sy = (int)(65536.0 * (float)(src->h) / (float)(dst->h)); 98 | } 99 | 100 | /* Maximum scaled source size */ 101 | ssx = (src->w << 16) - 1; 102 | ssy = (src->h << 16) - 1; 103 | 104 | /* Precalculate horizontal row increments */ 105 | csx = 0; 106 | csax = sax; 107 | for (x = 0; x <= dst->w; x++) { 108 | *csax = csx; 109 | csax++; 110 | csx += sx; 111 | 112 | /* Guard from overflows */ 113 | if (csx > ssx) { 114 | csx = ssx; 115 | } 116 | } 117 | 118 | /* Precalculate vertical row increments */ 119 | csy = 0; 120 | csay = say; 121 | for (y = 0; y <= dst->h; y++) { 122 | *csay = csy; 123 | csay++; 124 | csy += sy; 125 | 126 | /* Guard from overflows */ 127 | if (csy > ssy) { 128 | csy = ssy; 129 | } 130 | } 131 | 132 | sp = (ColorRGBA *)src->pixels; 133 | dp = (ColorRGBA *)dst->pixels; 134 | dgap = dst->pitch - dst->w * 4; 135 | spixelgap = src->pitch / 4; 136 | 137 | if (flipx) sp += spixelw; 138 | if (flipy) sp += (spixelgap * spixelh); 139 | 140 | /* 141 | * Switch between interpolating and non-interpolating code 142 | */ 143 | if (smooth) { 144 | /* 145 | * Interpolating Zoom 146 | */ 147 | csay = say; 148 | for (y = 0; y < dst->h; y++) { 149 | csp = sp; 150 | csax = sax; 151 | 152 | for (x = 0; x < dst->w; x++) { 153 | /* 154 | * Setup color source pointers 155 | */ 156 | ex = (*csax & 0xffff); 157 | ey = (*csay & 0xffff); 158 | cx = (*csax >> 16); 159 | cy = (*csay >> 16); 160 | sstepx = cx < spixelw; 161 | sstepy = cy < spixelh; 162 | c00 = sp; 163 | c01 = sp; 164 | c10 = sp; 165 | if (sstepy) { 166 | if (flipy) { 167 | c10 -= spixelgap; 168 | } else { 169 | c10 += spixelgap; 170 | } 171 | } 172 | c11 = c10; 173 | if (sstepx) { 174 | if (flipx) { 175 | c01--; 176 | c11--; 177 | } else { 178 | c01++; 179 | c11++; 180 | } 181 | } 182 | 183 | /* 184 | * Draw and interpolate colors 185 | */ 186 | t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; 187 | t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; 188 | dp->r = (((t2 - t1) * ey) >> 16) + t1; 189 | t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; 190 | t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; 191 | dp->g = (((t2 - t1) * ey) >> 16) + t1; 192 | t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; 193 | t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; 194 | dp->b = (((t2 - t1) * ey) >> 16) + t1; 195 | t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; 196 | t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; 197 | dp->a = (((t2 - t1) * ey) >> 16) + t1; 198 | /* 199 | * Advance source pointer x 200 | */ 201 | salast = csax; 202 | csax++; 203 | sstep = (*csax >> 16) - (*salast >> 16); 204 | if (flipx) { 205 | sp -= sstep; 206 | } else { 207 | sp += sstep; 208 | } 209 | 210 | /* 211 | * Advance destination pointer x 212 | */ 213 | dp++; 214 | } 215 | /* 216 | * Advance source pointer y 217 | */ 218 | salast = csay; 219 | csay++; 220 | sstep = (*csay >> 16) - (*salast >> 16); 221 | sstep *= spixelgap; 222 | if (flipy) { 223 | sp = csp - sstep; 224 | } else { 225 | sp = csp + sstep; 226 | } 227 | 228 | /* 229 | * Advance destination pointer y 230 | */ 231 | dp = (ColorRGBA *)((uint8_t *)dp + dgap); 232 | } 233 | } else { 234 | /* 235 | * Non-Interpolating Zoom 236 | */ 237 | csay = say; 238 | for (y = 0; y < dst->h; y++) { 239 | csp = sp; 240 | csax = sax; 241 | for (x = 0; x < dst->w; x++) { 242 | /* 243 | * Draw 244 | */ 245 | *dp = *sp; 246 | 247 | /* 248 | * Advance source pointer x 249 | */ 250 | salast = csax; 251 | csax++; 252 | sstep = (*csax >> 16) - (*salast >> 16); 253 | if (flipx) sstep = -sstep; 254 | sp += sstep; 255 | 256 | /* 257 | * Advance destination pointer x 258 | */ 259 | dp++; 260 | } 261 | /* 262 | * Advance source pointer y 263 | */ 264 | salast = csay; 265 | csay++; 266 | sstep = (*csay >> 16) - (*salast >> 16); 267 | sstep *= spixelgap; 268 | if (flipy) sstep = -sstep; 269 | sp = csp + sstep; 270 | 271 | /* 272 | * Advance destination pointer y 273 | */ 274 | dp = (ColorRGBA *)((uint8_t *)dp + dgap); 275 | } 276 | } 277 | 278 | tmpBuffersReset(); 279 | 280 | return (0); 281 | } 282 | 283 | /* 284 | 285 | Bilinear Scaling functions based of the Ne10 project 286 | https://github.com/projectNe10/Ne10 287 | 288 | Relevant file: 289 | https://github.com/projectNe10/Ne10/blob/master/modules/imgproc/NE10_resize.c 290 | 291 | Why don't use the **Neon** version? 292 | - I tried it once and it didn't yield any performance boost, so I stuck with the C version 293 | 294 | What's following is the license of the Ne10 project: 295 | */ 296 | 297 | /* 298 | * Copyright 2013-16 ARM Limited and Contributors. 299 | * All rights reserved. 300 | * 301 | * Redistribution and use in source and binary forms, with or without 302 | * modification, are permitted provided that the following conditions are met: 303 | * * Redistributions of source code must retain the above copyright 304 | * notice, this list of conditions and the following disclaimer. 305 | * * Redistributions in binary form must reproduce the above copyright 306 | * notice, this list of conditions and the following disclaimer in the 307 | * documentation and/or other materials provided with the distribution. 308 | * * Neither the name of ARM Limited nor the 309 | * names of its contributors may be used to endorse or promote products 310 | * derived from this software without specific prior written permission. 311 | * 312 | * THIS SOFTWARE IS PROVIDED BY ARM LIMITED AND CONTRIBUTORS "AS IS" AND 313 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 314 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 315 | * DISCLAIMED. IN NO EVENT SHALL ARM LIMITED AND CONTRIBUTORS BE LIABLE FOR ANY 316 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 317 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 318 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 319 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 320 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 321 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 322 | */ 323 | 324 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 325 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 326 | 327 | #define INTER_RESIZE_COEF_BITS 11 328 | #define INTER_RESIZE_COEF_SCALE (1 << 11) 329 | #define NE10_MAX_ESIZE 16 330 | 331 | static inline uint32_t ne10_align_size(int32_t sz, int32_t n) { return (sz + n - 1) & -n; } 332 | 333 | static inline int32_t ne10_floor(float a) { return (((a) >= 0) ? ((int32_t)a) : ((int32_t)a - 1)); } 334 | 335 | static inline int32_t ne10_clip(int32_t x, int32_t a, int32_t b) { return (x >= a ? (x < b ? x : b - 1) : a); } 336 | 337 | static inline uint8_t ne10_cast_op(int32_t val) { 338 | int32_t bits = INTER_RESIZE_COEF_BITS * 2; 339 | int32_t SHIFT = bits; 340 | int32_t DELTA = 1 << (bits - 1); 341 | int32_t temp = MIN(255, MAX(0, (val + DELTA) >> SHIFT)); 342 | return (uint8_t)(temp); 343 | }; 344 | 345 | static void ne10_img_hresize_linear_c(const uint8_t **src, int32_t **dst, int32_t count, const int32_t *xofs, const int16_t *alpha, 346 | int32_t swidth, int32_t dwidth, int32_t cn, int32_t xmin, int32_t xmax) { 347 | int32_t dx, k; 348 | 349 | int32_t dx0 = 0; 350 | 351 | // for (k = 0; k <= count - 2; k++) 352 | if (count == 2) { 353 | k = 0; 354 | const uint8_t *S0 = src[k], *S1 = src[k + 1]; 355 | int32_t *D0 = dst[k], *D1 = dst[k + 1]; 356 | for (dx = dx0; dx < xmax; dx++) { 357 | int32_t sx = xofs[dx]; 358 | int32_t a0 = alpha[dx * 2], a1 = alpha[dx * 2 + 1]; 359 | int32_t t0 = S0[sx] * a0 + S0[sx + cn] * a1; 360 | int32_t t1 = S1[sx] * a0 + S1[sx + cn] * a1; 361 | D0[dx] = t0; 362 | D1[dx] = t1; 363 | } 364 | 365 | for (; dx < dwidth; dx++) { 366 | int32_t sx = xofs[dx]; 367 | D0[dx] = (int32_t)S0[sx] * INTER_RESIZE_COEF_SCALE; 368 | D1[dx] = (int32_t)S1[sx] * INTER_RESIZE_COEF_SCALE; 369 | } 370 | } 371 | 372 | // for (; k < count; k++) 373 | if (count == 1) { 374 | k = 0; 375 | const uint8_t *S = src[k]; 376 | int32_t *D = dst[k]; 377 | for (dx = 0; dx < xmax; dx++) { 378 | int32_t sx = xofs[dx]; 379 | D[dx] = S[sx] * alpha[dx * 2] + S[sx + cn] * alpha[dx * 2 + 1]; 380 | } 381 | 382 | for (; dx < dwidth; dx++) D[dx] = (int32_t)S[xofs[dx]] * INTER_RESIZE_COEF_SCALE; 383 | } 384 | } 385 | 386 | static void ne10_img_vresize_linear_c(const int32_t **src, uint8_t *dst, const int16_t *beta, int32_t width) { 387 | int32_t b0 = beta[0], b1 = beta[1]; 388 | const int32_t *S0 = src[0], *S1 = src[1]; 389 | 390 | int32_t x = 0; 391 | for (; x <= width - 4; x += 4) { 392 | int32_t t0, t1; 393 | t0 = S0[x] * b0 + S1[x] * b1; 394 | t1 = S0[x + 1] * b0 + S1[x + 1] * b1; 395 | dst[x] = ne10_cast_op(t0); 396 | dst[x + 1] = ne10_cast_op(t1); 397 | t0 = S0[x + 2] * b0 + S1[x + 2] * b1; 398 | t1 = S0[x + 3] * b0 + S1[x + 3] * b1; 399 | dst[x + 2] = ne10_cast_op(t0); 400 | dst[x + 3] = ne10_cast_op(t1); 401 | } 402 | 403 | for (; x < width; x++) dst[x] = ne10_cast_op(S0[x] * b0 + S1[x] * b1); 404 | } 405 | 406 | static void ne10_img_resize_generic_linear_c(uint8_t *src, uint8_t *dst, const int32_t *xofs, const int16_t *_alpha, const int32_t *yofs, 407 | const int16_t *_beta, int32_t xmin, int32_t xmax, int32_t ksize, int32_t srcw, int32_t srch, 408 | int32_t srcstep, int32_t dstw, int32_t dsth, int32_t channels) { 409 | const int16_t *alpha = _alpha; 410 | const int16_t *beta = _beta; 411 | int32_t cn = channels; 412 | srcw *= cn; 413 | dstw *= cn; 414 | 415 | int32_t bufstep = (int32_t)ne10_align_size(dstw, 16); 416 | int32_t dststep = (int32_t)ne10_align_size(dstw, 4); 417 | 418 | int32_t *buffer_ = (int32_t *)tmpBufferAlloc(bufstep * ksize * sizeof(int32_t)); 419 | 420 | const uint8_t *srows[NE10_MAX_ESIZE]; 421 | int32_t *rows[NE10_MAX_ESIZE]; 422 | int32_t prev_sy[NE10_MAX_ESIZE]; 423 | int32_t k, dy; 424 | xmin *= cn; 425 | xmax *= cn; 426 | 427 | for (k = 0; k < ksize; k++) { 428 | prev_sy[k] = -1; 429 | rows[k] = (int32_t *)buffer_ + bufstep * k; 430 | } 431 | 432 | // image resize is a separable operation. In case of not too strong 433 | for (dy = 0; dy < dsth; dy++, beta += ksize) { 434 | int32_t sy0 = yofs[dy], k, k0 = ksize, k1 = 0, ksize2 = ksize / 2; 435 | 436 | for (k = 0; k < ksize; k++) { 437 | int32_t sy = ne10_clip(sy0 - ksize2 + 1 + k, 0, srch); 438 | for (k1 = MAX(k1, k); k1 < ksize; k1++) { 439 | if (sy == prev_sy[k1]) // if the sy-th row has been computed already, reuse it. 440 | { 441 | if (k1 > k) memcpy(rows[k], rows[k1], bufstep * sizeof(rows[0][0])); 442 | break; 443 | } 444 | } 445 | if (k1 == ksize) k0 = MIN(k0, k); // remember the first row that needs to be computed 446 | srows[k] = (const uint8_t *)(src + srcstep * sy); 447 | prev_sy[k] = sy; 448 | } 449 | 450 | if (k0 < ksize) ne10_img_hresize_linear_c(srows + k0, rows + k0, ksize - k0, xofs, alpha, srcw, dstw, cn, xmin, xmax); 451 | 452 | ne10_img_vresize_linear_c((const int32_t **)rows, (uint8_t *)(dst + dststep * dy), beta, dstw); 453 | } 454 | } 455 | 456 | static void ne10_img_resize_cal_offset_linear(int32_t *xofs, int16_t *ialpha, int32_t *yofs, int16_t *ibeta, int32_t *xmin, int32_t *xmax, 457 | int32_t ksize, int32_t ksize2, int32_t srcw, int32_t srch, int32_t dstw, int32_t dsth, 458 | int32_t channels) { 459 | float inv_scale_x = (float)dstw / srcw; 460 | float inv_scale_y = (float)dsth / srch; 461 | 462 | int32_t cn = channels; 463 | float scale_x = 1. / inv_scale_x; 464 | float scale_y = 1. / inv_scale_y; 465 | int32_t k, sx, sy, dx, dy; 466 | 467 | float fx, fy; 468 | 469 | float cbuf[NE10_MAX_ESIZE]; 470 | 471 | for (dx = 0; dx < dstw; dx++) { 472 | fx = (float)((dx + 0.5) * scale_x - 0.5); 473 | sx = ne10_floor(fx); 474 | fx -= sx; 475 | 476 | if (sx < ksize2 - 1) { 477 | *xmin = dx + 1; 478 | if (sx < 0) fx = 0, sx = 0; 479 | } 480 | 481 | if (sx + ksize2 >= srcw) { 482 | *xmax = MIN(*xmax, dx); 483 | if (sx >= srcw - 1) fx = 0, sx = srcw - 1; 484 | } 485 | 486 | for (k = 0, sx *= cn; k < cn; k++) xofs[dx * cn + k] = sx + k; 487 | 488 | cbuf[0] = 1.f - fx; 489 | cbuf[1] = fx; 490 | 491 | for (k = 0; k < ksize; k++) ialpha[dx * cn * ksize + k] = (int16_t)(cbuf[k] * INTER_RESIZE_COEF_SCALE); 492 | for (; k < cn * ksize; k++) ialpha[dx * cn * ksize + k] = ialpha[dx * cn * ksize + k - ksize]; 493 | } 494 | 495 | for (dy = 0; dy < dsth; dy++) { 496 | fy = (float)((dy + 0.5) * scale_y - 0.5); 497 | sy = ne10_floor(fy); 498 | fy -= sy; 499 | 500 | yofs[dy] = sy; 501 | 502 | cbuf[0] = 1.f - fy; 503 | cbuf[1] = fy; 504 | 505 | for (k = 0; k < ksize; k++) ibeta[dy * ksize + k] = (int16_t)(cbuf[k] * INTER_RESIZE_COEF_SCALE); 506 | } 507 | } 508 | 509 | /** 510 | * @brief image resize of 8-bit data. 511 | * @param[out] *dst point to the destination image 512 | * @param[in] dst_width width of destination image 513 | * @param[in] dst_height height of destination image 514 | * @param[in] *src point to the source image 515 | * @param[in] src_width width of source image 516 | * @param[in] src_height height of source image 517 | * @param[in] src_stride stride of source buffer 518 | * @return none. 519 | * The function implements image resize 520 | */ 521 | void zoomResizeBilinear_RGB8888(uint8_t *dst, uint32_t dst_width, uint32_t dst_height, uint8_t *src, uint32_t src_width, 522 | uint32_t src_height, uint32_t src_stride) { 523 | int32_t dstw = dst_width; 524 | int32_t dsth = dst_height; 525 | int32_t srcw = src_width; 526 | int32_t srch = src_height; 527 | 528 | int32_t cn = 4; 529 | 530 | int32_t xmin = 0; 531 | int32_t xmax = dstw; 532 | int32_t width = dstw * cn; 533 | float fx, fy; 534 | 535 | int32_t ksize = 0, ksize2; 536 | ksize = 2; 537 | ksize2 = ksize / 2; 538 | 539 | uint8_t *buffer_ = (uint8_t *)tmpBufferAlloc((width + dsth) * (sizeof(int32_t) + sizeof(float) * ksize)); 540 | 541 | int32_t *xofs = (int32_t *)buffer_; 542 | int32_t *yofs = xofs + width; 543 | int16_t *ialpha = (int16_t *)(yofs + dsth); 544 | int16_t *ibeta = ialpha + width * ksize; 545 | 546 | ne10_img_resize_cal_offset_linear(xofs, ialpha, yofs, ibeta, &xmin, &xmax, ksize, ksize2, srcw, srch, dstw, dsth, cn); 547 | 548 | ne10_img_resize_generic_linear_c(src, dst, xofs, ialpha, yofs, ibeta, xmin, xmax, ksize, srcw, srch, src_stride, dstw, dsth, cn); 549 | 550 | tmpBuffersReset(); 551 | } 552 | -------------------------------------------------------------------------------- /source/switch/zoom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /* 7 | Taken from SDL_gfx 8 | 9 | */ 10 | 11 | typedef struct { 12 | int w, h, pitch; 13 | uint32_t *pixels; 14 | } Surface; 15 | 16 | typedef struct { uint8_t r, g, b, a; } ColorRGBA; 17 | 18 | void zoomInit(int maxWidth, int maxHeight); 19 | void zoomDeinit(); 20 | 21 | // don't use these from multiple threads, they share the same temp buffers 22 | int zoomSurfaceRGBA(Surface *src, Surface *dst, int flipx, int flipy, int smooth); 23 | 24 | void zoomResizeBilinear_RGB8888(uint8_t *dst, uint32_t dst_width, uint32_t dst_height, uint8_t *src, uint32_t src_width, 25 | uint32_t src_height, uint32_t src_stride); -------------------------------------------------------------------------------- /source/system.cpp: -------------------------------------------------------------------------------- 1 | #include "system.h" 2 | 3 | #ifdef USE_MOTION_SENSOR 4 | 5 | #if VITA 6 | 7 | #include 8 | 9 | //-lSceMotion_stub is required. 10 | 11 | static SceMotionSensorState state; 12 | static bool sensor_state = false; 13 | 14 | void systemUpdateMotionSensor (void) { 15 | sceMotionGetSensorState(&state, 1); 16 | } 17 | 18 | int systemGetAccelX (void) { 19 | return state.accelerometer.x * -0x30000000; 20 | } 21 | 22 | int systemGetAccelY (void) { 23 | return state.accelerometer.y * 0x30000000; 24 | } 25 | 26 | int systemGetGyroZ (void) { 27 | return state.gyro.z * -0x10000000; 28 | } 29 | 30 | void systemSetSensorState(bool val) { 31 | if(val == sensor_state) return; 32 | 33 | if(val) { 34 | sceMotionStartSampling(); 35 | } else { 36 | sceMotionStopSampling(); 37 | } 38 | sensor_state = val; 39 | } 40 | #endif 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /source/system.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSTEM_H 2 | #define SYSTEM_H 3 | 4 | #include 5 | 6 | extern void systemDrawScreen (void); 7 | extern bool systemReadJoypads (void); 8 | extern uint32_t systemGetClock (void); 9 | extern void systemMessage(const char *, ...); 10 | #ifdef USE_MOTION_SENSOR 11 | extern void systemUpdateMotionSensor (void); 12 | extern int systemGetAccelX (void); 13 | extern int systemGetAccelY (void); 14 | extern int systemGetGyroZ (void); 15 | extern void systemSetSensorState(bool); 16 | #endif 17 | 18 | // sound functions 19 | extern void systemOnWriteDataToSoundBuffer(int16_t * finalWave, int length); 20 | #endif // SYSTEM_H 21 | -------------------------------------------------------------------------------- /source/thread.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | //#include 3 | #include "thread.h" 4 | 5 | #ifdef THREADED_RENDERER 6 | 7 | #if VITA 8 | 9 | static int _thread_func(SceSize args, void* p) { 10 | void** argp = static_cast(p); 11 | threadfunc_t func = reinterpret_cast(argp[0]); 12 | (*func)(argp[1]); 13 | return sceKernelExitDeleteThread(0); 14 | } 15 | 16 | static int _thread_map_priority(int priority) { 17 | switch (priority) { 18 | case THREAD_PRIORITY_LOWEST: 19 | return 0x10000102; 20 | case THREAD_PRIORITY_LOW: 21 | return 0x10000101; 22 | case THREAD_PRIORITY_NORMAL: 23 | case THREAD_PRIORITY_HIGH: 24 | case THREAD_PRIORITY_HIGHEST: 25 | default: 26 | return 0x10000100; 27 | } 28 | } 29 | 30 | thread_t thread_run(threadfunc_t func, void* p, int priority) { 31 | void* argp[2]; 32 | argp[0] = reinterpret_cast(func); 33 | argp[1] = p; 34 | 35 | SceUID thid = 36 | sceKernelCreateThread("my_thread", (SceKernelThreadEntry)_thread_func, _thread_map_priority(priority), 0x10000, 0, 0, NULL); 37 | if (thid >= 0) sceKernelStartThread(thid, sizeof(argp), &argp); 38 | 39 | return thid; 40 | } 41 | 42 | thread_t thread_get() { return sceKernelGetThreadId(); } 43 | void thread_sleep(int ms) { sceKernelDelayThread(ms * 1000); } // retro_sleep causes crash 44 | void thread_set_priority(thread_t id, int priority) { sceKernelChangeThreadPriority(id, 0xFF & _thread_map_priority(priority)); } 45 | 46 | #elif __SWITCH__ 47 | 48 | #include 49 | #include 50 | #include 51 | 52 | static int _thread_map_priority(int priority) { 53 | switch (priority) { 54 | case THREAD_PRIORITY_LOWEST: 55 | return 0x2c + 1; 56 | case THREAD_PRIORITY_LOW: 57 | return 0x2c + 10; 58 | case THREAD_PRIORITY_NORMAL: 59 | return 0x2c + 15; 60 | case THREAD_PRIORITY_HIGH: 61 | case THREAD_PRIORITY_HIGHEST: 62 | default: 63 | return 0x2c + 20; 64 | } 65 | } 66 | 67 | static void _thread_func(void* p) { 68 | void** argp = static_cast(p); 69 | threadfunc_t func = reinterpret_cast(argp[0]); 70 | (*func)(argp[1]); 71 | } 72 | 73 | thread_t thread_get() { return 0; } 74 | thread_t thread_run(threadfunc_t func, void* p, int priority) { 75 | void* argp[2]; 76 | argp[0] = reinterpret_cast(func); 77 | argp[1] = p; 78 | 79 | thread_t thread = (thread_t*)malloc(sizeof(Thread)); 80 | printf("creating thread...\n"); 81 | threadCreate((Thread*)thread, _thread_func, argp, 0x10000, _thread_map_priority(priority), 0); 82 | printf("miau\n"); 83 | threadStart((Thread*)thread); 84 | printf("started!\n"); 85 | 86 | return thread; 87 | } 88 | void thread_sleep(int ms) { svcSleepThread(1000000UL * ms); } 89 | void thread_set_priority(thread_t id, int priority) { svcSetThreadPriority(((Thread*)id)->handle, _thread_map_priority(priority)); } 90 | 91 | #else // non-vita 92 | 93 | #include 94 | 95 | static void _thread_func(void* p) { 96 | void** argp = static_cast(p); 97 | threadfunc_t func = reinterpret_cast(argp[0]); 98 | (*func)(argp[1]); 99 | } 100 | 101 | thread_t thread_run(threadfunc_t func, void* p, int priority) { 102 | void* argp[2]; 103 | sthread_t* thid = NULL; 104 | argp[0] = reinterpret_cast(func); 105 | argp[1] = p; 106 | 107 | thid = sthread_create(_thread_func, &argp); 108 | sthread_detach(thid); 109 | 110 | return thid; 111 | } 112 | 113 | thread_t thread_get() { return 0; } 114 | void thread_sleep(int ms) { retro_sleep(ms); } 115 | void thread_set_priority(thread_t id, int priority) {} 116 | 117 | #endif 118 | 119 | #endif // THREADED_RENDERER 120 | -------------------------------------------------------------------------------- /source/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREAD_H__ 2 | #define __THREAD_H__ 3 | 4 | #define THREAD_PRIORITY_HIGHEST 1 5 | #define THREAD_PRIORITY_HIGH 2 6 | #define THREAD_PRIORITY_NORMAL 3 7 | #define THREAD_PRIORITY_LOW 4 8 | #define THREAD_PRIORITY_LOWEST 5 9 | 10 | #include 11 | 12 | #if VITA 13 | #include 14 | typedef SceUID thread_t; 15 | #else 16 | typedef void* thread_t; 17 | #endif 18 | 19 | #ifdef THREADED_RENDERER 20 | typedef void(*threadfunc_t)(void*); 21 | 22 | thread_t thread_get(); 23 | thread_t thread_run(threadfunc_t func, void* p, int priority); 24 | void thread_sleep(int ms); 25 | void thread_set_priority(thread_t id, int priority); 26 | 27 | #endif 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /source/types.h: -------------------------------------------------------------------------------- 1 | // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. 2 | // Copyright (C) 2008 VBA-M development team 3 | 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2, or(at your option) 7 | // any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software Foundation, 16 | // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | 18 | #ifndef __VBA_TYPES_H__ 19 | #define __VBA_TYPES_H__ 20 | 21 | #include 22 | 23 | typedef uint8_t u8; 24 | typedef uint16_t u16; 25 | typedef uint32_t u32; 26 | typedef uint64_t u64; 27 | 28 | typedef int8_t s8; 29 | typedef int16_t s16; 30 | typedef int32_t s32; 31 | typedef int64_t s64; 32 | 33 | #endif // __VBA_TYPES_H__ 34 | --------------------------------------------------------------------------------