├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── libs ├── imgui-nx │ ├── Makefile │ ├── include │ │ ├── imgui_deko3d.h │ │ └── imgui_nx.h │ └── src │ │ ├── imgui_deko3d.cpp │ │ ├── imgui_fsh.glsl │ │ ├── imgui_nx.cpp │ │ └── imgui_vsh.glsl ├── imgui │ ├── Makefile │ └── include │ │ └── imgui.h └── stb_image │ ├── Makefile │ ├── include │ └── stb_image.h │ └── src │ └── stb_image.c └── source ├── gfx.cpp ├── gfx.hpp ├── main.cpp ├── ns.cpp ├── ns.h ├── version_list.cpp └── version_list.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.elf 2 | *.nacp 3 | *.nro 4 | build 5 | romfs 6 | lib 7 | .vscode 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/imgui/imgui"] 2 | path = libs/imgui/imgui 3 | url = https://github.com/ocornut/imgui.git 4 | -------------------------------------------------------------------------------- /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 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /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 | # ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) 19 | # 20 | # NO_ICON: if set to anything, do not use icon. 21 | # NO_NACP: if set to anything, no .nacp file is generated. 22 | # APP_TITLE is the name of the app stored in the .nacp file (Optional) 23 | # APP_AUTHOR is the author of the app stored in the .nacp file (Optional) 24 | # APP_VERSION is the version of the app stored in the .nacp file (Optional) 25 | # APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) 26 | # ICON is the filename of the icon (.jpg), relative to the project folder. 27 | # If not set, it attempts to use one of the following (in this order): 28 | # - .jpg 29 | # - icon.jpg 30 | # - /default_icon.jpg 31 | # 32 | # CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. 33 | # If not set, it attempts to use one of the following (in this order): 34 | # - .json 35 | # - config.json 36 | # If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead 37 | # of a homebrew executable (.nro). This is intended to be used for sysmodules. 38 | # NACP building is skipped as well. 39 | #--------------------------------------------------------------------------------- 40 | TARGET := $(notdir $(CURDIR)) 41 | BUILD := build 42 | SOURCES := source 43 | DATA := data 44 | INCLUDES := include 45 | APP_AUTHOR := HookedBehemoth 46 | APP_VERSION := 1.0.0 47 | ROMFS := romfs 48 | 49 | USR_DEFINES := #DEBUG 50 | 51 | #--------------------------------------------------------------------------------- 52 | # options for code generation 53 | #--------------------------------------------------------------------------------- 54 | ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE 55 | 56 | DEFINES := $(foreach def,$(USR_DEFINES),-D$(def)) 57 | CFLAGS := -g -Wall -O2 -ffunction-sections \ 58 | $(ARCH) $(DEFINES) 59 | 60 | CFLAGS += $(INCLUDE) -D__SWITCH__ 61 | 62 | CXXFLAGS := $(CFLAGS) -fno-rtti -std=c++20 63 | 64 | ASFLAGS := -g $(ARCH) 65 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 66 | 67 | LIBS := -limgui-nx -limgui -lstbi -ldeko3d -lnx 68 | 69 | #--------------------------------------------------------------------------------- 70 | # list of directories containing libraries, this must be the top level containing 71 | # include and lib 72 | #--------------------------------------------------------------------------------- 73 | LIBDIRS := $(PORTLIBS) $(LIBNX) $(TOPDIR)/libs/imgui-nx $(TOPDIR)/libs/imgui $(TOPDIR)/libs/stb_image 74 | 75 | #--------------------------------------------------------------------------------- 76 | # no real need to edit anything past this point unless you need to add additional 77 | # rules for different file extensions 78 | #--------------------------------------------------------------------------------- 79 | ifneq ($(BUILD),$(notdir $(CURDIR))) 80 | #--------------------------------------------------------------------------------- 81 | 82 | export OUTPUT := $(CURDIR)/$(TARGET) 83 | export TOPDIR := $(CURDIR) 84 | 85 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 86 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 87 | 88 | export DEPSDIR := $(CURDIR)/$(BUILD) 89 | 90 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 91 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 92 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 93 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 94 | 95 | #--------------------------------------------------------------------------------- 96 | # use CXX for linking C++ projects, CC for standard C 97 | #--------------------------------------------------------------------------------- 98 | ifeq ($(strip $(CPPFILES)),) 99 | #--------------------------------------------------------------------------------- 100 | export LD := $(CC) 101 | #--------------------------------------------------------------------------------- 102 | else 103 | #--------------------------------------------------------------------------------- 104 | export LD := $(CXX) 105 | #--------------------------------------------------------------------------------- 106 | endif 107 | #--------------------------------------------------------------------------------- 108 | 109 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 110 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 111 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 112 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 113 | 114 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 115 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 116 | -I$(CURDIR)/$(BUILD) 117 | 118 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 119 | 120 | ifeq ($(strip $(CONFIG_JSON)),) 121 | jsons := $(wildcard *.json) 122 | ifneq (,$(findstring $(TARGET).json,$(jsons))) 123 | export APP_JSON := $(TOPDIR)/$(TARGET).json 124 | else 125 | ifneq (,$(findstring config.json,$(jsons))) 126 | export APP_JSON := $(TOPDIR)/config.json 127 | endif 128 | endif 129 | else 130 | export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) 131 | endif 132 | 133 | ifeq ($(strip $(ICON)),) 134 | icons := $(wildcard *.jpg) 135 | ifneq (,$(findstring $(TARGET).jpg,$(icons))) 136 | export APP_ICON := $(TOPDIR)/$(TARGET).jpg 137 | else 138 | ifneq (,$(findstring icon.jpg,$(icons))) 139 | export APP_ICON := $(TOPDIR)/icon.jpg 140 | endif 141 | endif 142 | else 143 | export APP_ICON := $(TOPDIR)/$(ICON) 144 | endif 145 | 146 | ifeq ($(strip $(NO_ICON)),) 147 | export NROFLAGS += --icon=$(APP_ICON) 148 | endif 149 | 150 | ifeq ($(strip $(NO_NACP)),) 151 | export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp 152 | endif 153 | 154 | ifneq ($(APP_TITLEID),) 155 | export NACPFLAGS += --titleid=$(APP_TITLEID) 156 | endif 157 | 158 | ifneq ($(ROMFS),) 159 | export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) 160 | endif 161 | 162 | .PHONY: $(BUILD) clean all 163 | 164 | #--------------------------------------------------------------------------------- 165 | all: $(BUILD) 166 | 167 | shaders: 168 | @mkdir -p $(ROMFS) 169 | $(shell find libs/imgui-nx/lib/ -type f -name '*.dksh' -exec cp -u {} $(ROMFS) \;) 170 | 171 | $(BUILD): shaders 172 | @[ -d $@ ] || mkdir -p $@ 173 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 174 | 175 | #--------------------------------------------------------------------------------- 176 | clean: 177 | @echo clean ... 178 | ifeq ($(strip $(APP_JSON)),) 179 | @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf 180 | else 181 | @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf 182 | endif 183 | 184 | 185 | #--------------------------------------------------------------------------------- 186 | else 187 | .PHONY: all 188 | 189 | DEPENDS := $(OFILES:.o=.d) 190 | 191 | #--------------------------------------------------------------------------------- 192 | # main targets 193 | #--------------------------------------------------------------------------------- 194 | ifeq ($(strip $(APP_JSON)),) 195 | 196 | all : $(OUTPUT).nro 197 | 198 | ifeq ($(strip $(NO_NACP)),) 199 | $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp 200 | else 201 | $(OUTPUT).nro : $(OUTPUT).elf 202 | endif 203 | 204 | else 205 | 206 | all : $(OUTPUT).nsp 207 | 208 | $(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm 209 | 210 | $(OUTPUT).nso : $(OUTPUT).elf 211 | 212 | endif 213 | 214 | $(OUTPUT).elf : $(OFILES) 215 | 216 | $(OFILES_SRC) : $(HFILES_BIN) 217 | 218 | #--------------------------------------------------------------------------------- 219 | # you need a rule like this for each extension you use as binary data 220 | #--------------------------------------------------------------------------------- 221 | %.bin.o %_bin.h : %.bin 222 | #--------------------------------------------------------------------------------- 223 | @echo $(notdir $<) 224 | @$(bin2o) 225 | 226 | -include $(DEPENDS) 227 | 228 | #--------------------------------------------------------------------------------------- 229 | endif 230 | #--------------------------------------------------------------------------------------- 231 | -------------------------------------------------------------------------------- /libs/imgui-nx/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(DEVKITPRO)),) 2 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 3 | endif 4 | 5 | # ----------------------------------------------- 6 | 7 | TARGET = lib$(notdir $(CURDIR)) 8 | EXTENSION = a 9 | OUT = lib 10 | BUILD = build 11 | SOURCES = src 12 | INCLUDES = include ../imgui/include 13 | LIBS = 14 | 15 | DEFINES = __SWITCH__ 16 | ARCH = -march=armv8-a+crc+crypto+simd -mtune=cortex-a57 -mtp=soft -fpie 17 | FLAGS = -Wall -pipe -g -O2 -ffunction-sections -fdata-sections 18 | CFLAGS = -std=gnu11 19 | CXXFLAGS = -std=gnu++17 20 | ASFLAGS = 21 | ARFLAGS = -rc 22 | 23 | PREFIX = aarch64-none-elf- 24 | CC = $(PREFIX)gcc 25 | CXX = $(PREFIX)g++ 26 | AS = $(PREFIX)as 27 | AR = $(PREFIX)gcc-ar 28 | RANLIB = $(PREFIX)gcc-ranlib 29 | 30 | # ----------------------------------------------- 31 | 32 | export PATH := $(DEVKITPRO)/tools/bin:$(DEVKITPRO)/devkitA64/bin:$(PORTLIBS)/bin:$(PATH) 33 | 34 | PORTLIBS = $(DEVKITPRO)/portlibs/switch 35 | LIBNX = $(DEVKITPRO)/libnx 36 | LIBS := $(LIBS) $(LIBNX) $(PORTLIBS) 37 | 38 | # ----------------------------------------------- 39 | 40 | CFILES = $(shell find $(SOURCES) -name *.c) 41 | CPPFILES = $(shell find $(SOURCES) -name *.cpp) 42 | SFILES = $(shell find $(SOURCES) -name *.s -or -name *.S) 43 | GLSLFILES = $(shell find $(SOURCES) -name *.glsl) 44 | OFILES = $(CFILES:%=$(BUILD)/%.o) $(CPPFILES:%=$(BUILD)/%.o) $(SFILES:%=$(BUILD)/%.o) 45 | DFILES = $(OFILES:.o=.d) 46 | DKSHFILES = $(GLSLFILES:%.glsl=$(OUT)/%.dksh) 47 | 48 | LIB_TARGET = $(if $(OUT:=), $(OUT)/$(TARGET).$(EXTENSION), .$(OUT)/$(TARGET).$(EXTENSION)) 49 | 50 | DEFINE_FLAGS = $(addprefix -D,$(DEFINES)) 51 | INCLUDE_FLAGS = $(addprefix -I$(CURDIR)/,$(INCLUDES)) $(foreach dir,$(CUSTOM_LIBS),-I$(CURDIR)/$(dir)/include) \ 52 | $(foreach dir,$(filter-out $(CUSTOM_LIBS),$(LIBS)),-I$(dir)/include) 53 | 54 | # ----------------------------------------------- 55 | 56 | .SUFFIXES: 57 | 58 | .PHONY: all clean 59 | 60 | all: $(LIB_TARGET) $(DKSHFILES) 61 | 62 | $(LIB_TARGET): $(OFILES) 63 | @echo " AR " $@ 64 | @mkdir -p $(dir $@) 65 | @rm -f $@ 66 | @$(AR) $(ARFLAGS) $@ $^ 67 | @echo "Built" $(notdir $@) 68 | 69 | $(BUILD)/%.c.o: %.c 70 | @echo " CC " $@ 71 | @mkdir -p $(dir $@) 72 | @$(CC) -MMD -MP $(ARCH) $(FLAGS) $(CFLAGS) $(DEFINE_FLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 73 | 74 | $(BUILD)/%.cpp.o: %.cpp 75 | @echo " CXX " $@ 76 | @mkdir -p $(dir $@) 77 | @$(CXX) -MMD -MP $(ARCH) $(FLAGS) $(CXXFLAGS) $(DEFINE_FLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 78 | 79 | $(BUILD)/%.s.o: %.s %.S 80 | @echo " AS " $@ 81 | @mkdir -p $(dir $@) 82 | @$(AS) -MMD -MP -x assembler-with-cpp $(ARCH) $(FLAGS) $(ASFLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 83 | 84 | $(OUT)/%_vsh.dksh: %_vsh.glsl 85 | @mkdir -p $(dir $@) 86 | @echo " VERT" $@ 87 | @uam -s vert -o $@ $< 88 | 89 | $(OUT)/%_fsh.dksh: %_fsh.glsl 90 | @mkdir -p $(dir $@) 91 | @echo " FRAG" $@ 92 | @uam -s frag -o $@ $< 93 | 94 | clean: 95 | @echo Cleaning... 96 | @rm -rf $(BUILD) $(OUT) 97 | 98 | -include $(DFILES) 99 | -------------------------------------------------------------------------------- /libs/imgui-nx/include/imgui_deko3d.h: -------------------------------------------------------------------------------- 1 | // ftpd is a server implementation based on the following: 2 | // - RFC 959 (https://tools.ietf.org/html/rfc959) 3 | // - RFC 3659 (https://tools.ietf.org/html/rfc3659) 4 | // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (C) 2020 Michael Theall 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | 28 | #pragma once 29 | 30 | #ifndef CLASSIC 31 | #include 32 | 33 | #include 34 | 35 | namespace ImGui 36 | { 37 | namespace deko3d 38 | { 39 | /// \brief Initialize deko3d 40 | /// \param device_ deko3d device (used to allocate vertex/index and font texture buffers) 41 | /// \param queue_ deko3d queue (used to run command lists) 42 | /// \param cmdBuf_ Command buffer (used to build command lists) 43 | /// \param[out] samplerDescriptor_ Sampler descriptor for font texture 44 | /// \param[out] imageDescriptor_ Image descriptor for font texture 45 | /// \param fontTextureHandle_ Texture handle that references samplerDescriptor_ and imageDescriptor_ 46 | /// \param imageCount_ Images in the swapchain 47 | void init (dk::UniqueDevice &device_, 48 | dk::UniqueQueue &queue_, 49 | dk::UniqueCmdBuf &cmdBuf_, 50 | dk::SamplerDescriptor &samplerDescriptor_, 51 | dk::ImageDescriptor &imageDescriptor_, 52 | DkResHandle fontTextureHandle_, 53 | unsigned imageCount_); 54 | 55 | /// \brief Deinitialize deko3d 56 | void exit (); 57 | 58 | /// \brief Render ImGui draw list 59 | /// \param device_ deko3d device (used to reallocate vertex/index buffers) 60 | /// \param queue_ deko3d queue (used to run command lists) 61 | /// \param cmdBuf_ Command buffer (used to build command lists) 62 | /// \param slot_ Image slot 63 | void render (dk::UniqueDevice &device_, 64 | dk::UniqueQueue &queue_, 65 | dk::UniqueCmdBuf &cmdBuf_, 66 | unsigned slot_); 67 | 68 | /// \brief Make ImGui texture id from deko3d texture handle 69 | /// \param handle_ Texture handle 70 | inline void *makeTextureID (DkResHandle handle_) 71 | { 72 | return reinterpret_cast (static_cast (handle_)); 73 | } 74 | 75 | /// \brief Align power-of-two value 76 | /// \tparam T Value type 77 | /// \tparam U Alignment type 78 | /// \param size_ Value to align 79 | /// \param align_ Alignment 80 | template 81 | constexpr inline std::uint32_t align (T const &size_, U const &align_) 82 | { 83 | return static_cast (size_ + align_ - 1) & ~(align_ - 1); 84 | } 85 | } 86 | } 87 | #endif 88 | -------------------------------------------------------------------------------- /libs/imgui-nx/include/imgui_nx.h: -------------------------------------------------------------------------------- 1 | // ftpd is a server implementation based on the following: 2 | // - RFC 959 (https://tools.ietf.org/html/rfc959) 3 | // - RFC 3659 (https://tools.ietf.org/html/rfc3659) 4 | // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (C) 2020 Michael Theall 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | 28 | #pragma once 29 | 30 | #include 31 | 32 | namespace ImGui::nx { 33 | 34 | bool init(); 35 | void exit(); 36 | std::uint64_t newFrame(); 37 | 38 | } // namespace ImGui::nx 39 | -------------------------------------------------------------------------------- /libs/imgui-nx/src/imgui_deko3d.cpp: -------------------------------------------------------------------------------- 1 | // ftpd is a server implementation based on the following: 2 | // - RFC 959 (https://tools.ietf.org/html/rfc959) 3 | // - RFC 3659 (https://tools.ietf.org/html/rfc3659) 4 | // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (C) 2020 Michael Theall 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES 43 | #define GLM_FORCE_INTRINSICS 44 | #include 45 | #include 46 | #include 47 | 48 | #include "imgui_deko3d.h" 49 | 50 | namespace 51 | { 52 | /// \brief Vertex buffer size 53 | constexpr auto VTXBUF_SIZE = 1024u * 1024u; 54 | /// \brief Index buffer size 55 | constexpr auto IDXBUF_SIZE = 1024u * 1024u; 56 | 57 | /// \brief Vertex shader UBO 58 | struct VertUBO 59 | { 60 | /// \brief Projection matrix 61 | glm::mat4 projMtx; 62 | }; 63 | 64 | /// \brief Fragment shader UBO 65 | struct FragUBO 66 | { 67 | /// \brief Whether drawing a font or not 68 | std::uint32_t font; 69 | }; 70 | 71 | /// \brief Vertex attribute state 72 | constexpr std::array VERTEX_ATTRIB_STATE = { 73 | // clang-format off 74 | DkVtxAttribState{0, 0, offsetof (ImDrawVert, pos), DkVtxAttribSize_2x32, DkVtxAttribType_Float, 0}, 75 | DkVtxAttribState{0, 0, offsetof (ImDrawVert, uv), DkVtxAttribSize_2x32, DkVtxAttribType_Float, 0}, 76 | DkVtxAttribState{0, 0, offsetof (ImDrawVert, col), DkVtxAttribSize_4x8, DkVtxAttribType_Unorm, 0}, 77 | // clang-format on 78 | }; 79 | 80 | /// \brief Vertex buffer state 81 | constexpr std::array VERTEX_BUFFER_STATE = { 82 | DkVtxBufferState{sizeof (ImDrawVert), 0}, 83 | }; 84 | 85 | /// \brief Shader code memblock 86 | dk::UniqueMemBlock s_codeMemBlock; 87 | /// \brief Shaders (vertex, fragment) 88 | dk::Shader s_shaders[2]; 89 | 90 | /// \brief UBO memblock 91 | dk::UniqueMemBlock s_uboMemBlock; 92 | 93 | /// \brief Vertex data memblock 94 | std::vector s_vtxMemBlock; 95 | /// \brief Index data memblock 96 | std::vector s_idxMemBlock; 97 | 98 | /// \brief Font image memblock 99 | dk::UniqueMemBlock s_fontImageMemBlock; 100 | /// \brief Font texture handle 101 | DkResHandle s_fontTextureHandle; 102 | 103 | /// \brief Load shader code 104 | void loadShaders (dk::UniqueDevice &device_) 105 | { 106 | /// \brief Shader file descriptor 107 | struct ShaderFile 108 | { 109 | /// \brief Parameterized constructor 110 | /// \param shader_ Shader object 111 | /// \param path_ Path to source code 112 | ShaderFile (dk::Shader &shader_, char const *const path_) 113 | : shader (shader_), path (path_), size (getSize (path_)) 114 | { 115 | } 116 | 117 | /// \brief Get size of a file 118 | /// \param path_ Path to file 119 | static std::size_t getSize (char const *const path_) 120 | { 121 | struct stat st; 122 | auto const rc = ::stat (path_, &st); 123 | if (rc != 0) 124 | { 125 | std::fprintf (stderr, "stat(%s): %s\n", path_, std::strerror (errno)); 126 | std::abort (); 127 | } 128 | 129 | return st.st_size; 130 | } 131 | 132 | /// \brief Shader object 133 | dk::Shader &shader; 134 | /// \brief Path to source code 135 | char const *const path; 136 | /// \brief Source code file size 137 | std::size_t const size; 138 | }; 139 | 140 | auto shaderFiles = {ShaderFile{s_shaders[0], "romfs:/imgui_vsh.dksh"}, 141 | ShaderFile{s_shaders[1], "romfs:/imgui_fsh.dksh"}}; 142 | 143 | // calculate total size of shaders 144 | auto const codeSize = std::accumulate (std::begin (shaderFiles), 145 | std::end (shaderFiles), 146 | DK_SHADER_CODE_UNUSABLE_SIZE, 147 | [] (auto const sum_, auto const &file_) { 148 | return sum_ + ImGui::deko3d::align (file_.size, DK_SHADER_CODE_ALIGNMENT); 149 | }); 150 | 151 | // create shader code memblock 152 | s_codeMemBlock = 153 | dk::MemBlockMaker{device_, ImGui::deko3d::align (codeSize, DK_MEMBLOCK_ALIGNMENT)} 154 | .setFlags ( 155 | DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code) 156 | .create (); 157 | 158 | auto const addr = static_cast (s_codeMemBlock.getCpuAddr ()); 159 | std::size_t offset = 0; 160 | 161 | // read shaders into memblock 162 | for (auto &file : shaderFiles) 163 | { 164 | std::uint32_t const codeOffset = offset; 165 | 166 | auto *fp = fopen(file.path, "rb"); 167 | if (!fp) 168 | { 169 | std::fprintf (stderr, "open(%s): %s\n", file.path, std::strerror (errno)); 170 | std::abort (); 171 | } 172 | 173 | if (fread (&addr[offset], 1, file.size, fp) != file.size) 174 | { 175 | std::fprintf (stderr, "read(%s): %s\n", file.path, std::strerror (errno)); 176 | std::abort (); 177 | } 178 | 179 | fclose(fp); 180 | 181 | dk::ShaderMaker{s_codeMemBlock, codeOffset}.initialize (file.shader); 182 | 183 | offset = ImGui::deko3d::align (offset + file.size, DK_SHADER_CODE_ALIGNMENT); 184 | } 185 | } 186 | 187 | /// \brief Setup render state 188 | /// \param cmdBuf_ Command buffer 189 | /// \param drawData_ Data to draw 190 | /// \param width_ Framebuffer width 191 | /// \param height_ Framebuffer height 192 | DkCmdList setupRenderState (dk::UniqueCmdBuf &cmdBuf_, 193 | ImDrawData *const drawData_, 194 | unsigned const width_, 195 | unsigned const height_) 196 | { 197 | // setup viewport, orthographic projection matrix 198 | // our visible imgui space lies from drawData_->DisplayPos (top left) to 199 | // drawData_->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single 200 | // viewport apps. 201 | auto const L = drawData_->DisplayPos.x; 202 | auto const R = drawData_->DisplayPos.x + drawData_->DisplaySize.x; 203 | auto const T = drawData_->DisplayPos.y; 204 | auto const B = drawData_->DisplayPos.y + drawData_->DisplaySize.y; 205 | 206 | VertUBO vertUBO; 207 | vertUBO.projMtx = glm::orthoRH_ZO (L, R, B, T, -1.0f, 1.0f); 208 | 209 | // create command buffer to initialize/reset render state 210 | cmdBuf_.setViewports (0, 211 | DkViewport{0.0f, 0.0f, static_cast(width_), static_cast(height_)}); 212 | cmdBuf_.bindShaders (DkStageFlag_GraphicsMask, {&s_shaders[0], &s_shaders[1]}); 213 | cmdBuf_.bindUniformBuffer (DkStage_Vertex, 214 | 0, 215 | s_uboMemBlock.getGpuAddr (), 216 | ImGui::deko3d::align (sizeof (VertUBO), DK_UNIFORM_BUF_ALIGNMENT)); 217 | cmdBuf_.pushConstants (s_uboMemBlock.getGpuAddr (), 218 | ImGui::deko3d::align (sizeof (VertUBO), DK_UNIFORM_BUF_ALIGNMENT), 219 | 0, 220 | sizeof (VertUBO), 221 | &vertUBO); 222 | cmdBuf_.bindUniformBuffer (DkStage_Fragment, 223 | 0, 224 | s_uboMemBlock.getGpuAddr () + 225 | ImGui::deko3d::align (sizeof (VertUBO), DK_UNIFORM_BUF_ALIGNMENT), 226 | ImGui::deko3d::align (sizeof (FragUBO), DK_UNIFORM_BUF_ALIGNMENT)); 227 | cmdBuf_.bindRasterizerState (dk::RasterizerState{}.setCullMode (DkFace_None)); 228 | cmdBuf_.bindColorState (dk::ColorState{}.setBlendEnable (0, true)); 229 | cmdBuf_.bindColorWriteState (dk::ColorWriteState{}); 230 | cmdBuf_.bindDepthStencilState (dk::DepthStencilState{}.setDepthTestEnable (false)); 231 | cmdBuf_.bindBlendStates (0, 232 | dk::BlendState{}.setFactors (DkBlendFactor_SrcAlpha, 233 | DkBlendFactor_InvSrcAlpha, 234 | DkBlendFactor_InvSrcAlpha, 235 | DkBlendFactor_Zero)); 236 | cmdBuf_.bindVtxAttribState (VERTEX_ATTRIB_STATE); 237 | cmdBuf_.bindVtxBufferState (VERTEX_BUFFER_STATE); 238 | 239 | return cmdBuf_.finishList (); 240 | } 241 | } 242 | 243 | void ImGui::deko3d::init (dk::UniqueDevice &device_, 244 | dk::UniqueQueue &queue_, 245 | dk::UniqueCmdBuf &cmdBuf_, 246 | dk::SamplerDescriptor &samplerDescriptor_, 247 | dk::ImageDescriptor &imageDescriptor_, 248 | DkResHandle fontTextureHandle_, 249 | unsigned const imageCount_) 250 | { 251 | auto &io = ImGui::GetIO (); 252 | 253 | // setup back-end capabilities flags 254 | io.BackendRendererName = "deko3d"; 255 | io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; 256 | 257 | // load shader code 258 | loadShaders (device_); 259 | 260 | // create UBO memblock 261 | s_uboMemBlock = dk::MemBlockMaker{device_, 262 | align (align (sizeof (VertUBO), DK_UNIFORM_BUF_ALIGNMENT) + 263 | align (sizeof (FragUBO), DK_UNIFORM_BUF_ALIGNMENT), 264 | DK_MEMBLOCK_ALIGNMENT)} 265 | .setFlags (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 266 | .create (); 267 | 268 | // create memblocks for each image slot 269 | for (std::size_t i = 0; i < imageCount_; ++i) 270 | { 271 | // create vertex data memblock 272 | s_vtxMemBlock.emplace_back ( 273 | dk::MemBlockMaker{device_, align (VTXBUF_SIZE, DK_MEMBLOCK_ALIGNMENT)} 274 | .setFlags (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 275 | .create ()); 276 | 277 | // create index data memblock 278 | s_idxMemBlock.emplace_back ( 279 | dk::MemBlockMaker{device_, align (IDXBUF_SIZE, DK_MEMBLOCK_ALIGNMENT)} 280 | .setFlags (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 281 | .create ()); 282 | } 283 | 284 | // get texture atlas 285 | io.Fonts->SetTexID (makeTextureID (fontTextureHandle_)); 286 | s_fontTextureHandle = fontTextureHandle_; 287 | unsigned char *pixels; 288 | int width; 289 | int height; 290 | io.Fonts->GetTexDataAsAlpha8 (&pixels, &width, &height); 291 | 292 | // create memblock for transfer 293 | dk::UniqueMemBlock memBlock = 294 | dk::MemBlockMaker{device_, align (width * height, DK_MEMBLOCK_ALIGNMENT)} 295 | .setFlags (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 296 | .create (); 297 | std::memcpy (memBlock.getCpuAddr (), pixels, width * height); 298 | 299 | // initialize sampler descriptor 300 | samplerDescriptor_.initialize ( 301 | dk::Sampler{} 302 | .setFilter (DkFilter_Linear, DkFilter_Linear) 303 | .setWrapMode (DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge)); 304 | 305 | // initialize texture atlas image layout 306 | dk::ImageLayout layout; 307 | dk::ImageLayoutMaker{device_} 308 | .setFlags (0) 309 | .setFormat (DkImageFormat_R8_Unorm) 310 | .setDimensions (width, height) 311 | .initialize (layout); 312 | 313 | auto const fontAlign = layout.getAlignment (); 314 | auto const fontSize = layout.getSize (); 315 | 316 | // create image memblock 317 | s_fontImageMemBlock = dk::MemBlockMaker{device_, 318 | align (fontSize, std::max (fontAlign, DK_MEMBLOCK_ALIGNMENT))} 319 | .setFlags (DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image) 320 | .create (); 321 | 322 | // initialize font texture atlas image descriptor 323 | dk::Image fontTexture; 324 | fontTexture.initialize (layout, s_fontImageMemBlock, 0); 325 | imageDescriptor_.initialize (fontTexture); 326 | 327 | // copy font texture atlas to image view 328 | dk::ImageView imageView{fontTexture}; 329 | cmdBuf_.copyBufferToImage ({memBlock.getGpuAddr ()}, imageView, 330 | {0, 0, 0, static_cast(width), static_cast(height), 1}); 331 | 332 | // submit commands to transfer font texture 333 | queue_.submitCommands (cmdBuf_.finishList ()); 334 | 335 | // wait for commands to complete before releasing memblock 336 | queue_.waitIdle (); 337 | } 338 | 339 | void ImGui::deko3d::exit () 340 | { 341 | s_fontImageMemBlock = nullptr; 342 | 343 | s_idxMemBlock.clear (); 344 | s_vtxMemBlock.clear (); 345 | 346 | s_uboMemBlock = nullptr; 347 | s_codeMemBlock = nullptr; 348 | } 349 | 350 | void ImGui::deko3d::render (dk::UniqueDevice &device_, 351 | dk::UniqueQueue &queue_, 352 | dk::UniqueCmdBuf &cmdBuf_, 353 | unsigned const slot_) 354 | { 355 | // get ImGui draw data 356 | auto const drawData = ImGui::GetDrawData (); 357 | if (drawData->CmdListsCount <= 0) 358 | return; 359 | 360 | // get framebuffer dimensions 361 | unsigned width = drawData->DisplaySize.x * drawData->FramebufferScale.x; 362 | unsigned height = drawData->DisplaySize.y * drawData->FramebufferScale.y; 363 | if (width <= 0 || height <= 0) 364 | return; 365 | 366 | // setup desired render state 367 | auto const setupCmd = setupRenderState (cmdBuf_, drawData, width, height); 368 | queue_.submitCommands (setupCmd); 369 | 370 | // currently bound texture 371 | std::optional boundTextureHandle; 372 | 373 | // will project scissor/clipping rectangles into framebuffer space 374 | // (0,0) unless using multi-viewports 375 | auto const clipOff = drawData->DisplayPos; 376 | // (1,1) unless using retina display which are often (2,2) 377 | auto const clipScale = drawData->FramebufferScale; 378 | 379 | // check if we need to grow vertex data memblock 380 | if (s_vtxMemBlock[slot_].getSize () < drawData->TotalVtxCount * sizeof (ImDrawVert)) 381 | { 382 | // add 10% to avoid growing many frames in a row 383 | std::size_t const count = drawData->TotalVtxCount * 1.1f; 384 | 385 | s_vtxMemBlock[slot_] = 386 | dk::MemBlockMaker{device_, align (count * sizeof (ImDrawVert), DK_MEMBLOCK_ALIGNMENT)} 387 | .setFlags (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 388 | .create (); 389 | } 390 | 391 | // check if we need to grow index data memblock 392 | if (s_idxMemBlock[slot_].getSize () < drawData->TotalIdxCount * sizeof (ImDrawIdx)) 393 | { 394 | // add 10% to avoid growing many frames in a row 395 | std::size_t const count = drawData->TotalIdxCount * 1.1f; 396 | 397 | s_idxMemBlock[slot_] = 398 | dk::MemBlockMaker{device_, align (count * sizeof (ImDrawIdx), DK_MEMBLOCK_ALIGNMENT)} 399 | .setFlags (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 400 | .create (); 401 | } 402 | 403 | // get base cpu addresses 404 | auto const cpuVtx = static_cast (s_vtxMemBlock[slot_].getCpuAddr ()); 405 | auto const cpuIdx = static_cast (s_idxMemBlock[slot_].getCpuAddr ()); 406 | 407 | // get base gpu addresses 408 | auto const gpuVtx = s_vtxMemBlock[slot_].getGpuAddr (); 409 | auto const gpuIdx = s_idxMemBlock[slot_].getGpuAddr (); 410 | 411 | // get memblock sizes 412 | auto const sizeVtx = s_vtxMemBlock[slot_].getSize (); 413 | auto const sizeIdx = s_idxMemBlock[slot_].getSize (); 414 | 415 | // bind vertex/index data memblocks 416 | static_assert (sizeof (ImDrawIdx) == sizeof (std::uint16_t)); 417 | cmdBuf_.bindVtxBuffer (0, gpuVtx, sizeVtx); 418 | cmdBuf_.bindIdxBuffer (DkIdxFormat_Uint16, gpuIdx); 419 | 420 | // render command lists 421 | std::size_t offsetVtx = 0; 422 | std::size_t offsetIdx = 0; 423 | for (int i = 0; i < drawData->CmdListsCount; ++i) 424 | { 425 | auto const &cmdList = *drawData->CmdLists[i]; 426 | 427 | auto const vtxSize = cmdList.VtxBuffer.Size * sizeof (ImDrawVert); 428 | auto const idxSize = cmdList.IdxBuffer.Size * sizeof (ImDrawIdx); 429 | 430 | // double check that we don't overrun vertex data memblock 431 | if (sizeVtx - offsetVtx < vtxSize) 432 | { 433 | std::fprintf (stderr, "Not enough vertex buffer\n"); 434 | std::fprintf (stderr, "\t%zu/%u used, need %zu\n", offsetVtx, sizeVtx, vtxSize); 435 | continue; 436 | } 437 | 438 | // double check that we don't overrun index data memblock 439 | if (sizeIdx - offsetIdx < idxSize) 440 | { 441 | std::fprintf (stderr, "Not enough index buffer\n"); 442 | std::fprintf (stderr, "\t%zu/%u used, need %zu\n", offsetIdx, sizeIdx, idxSize); 443 | continue; 444 | } 445 | 446 | // copy vertex/index data into memblocks 447 | std::memcpy (cpuVtx + offsetVtx, cmdList.VtxBuffer.Data, vtxSize); 448 | std::memcpy (cpuIdx + offsetIdx, cmdList.IdxBuffer.Data, idxSize); 449 | 450 | for (auto const &cmd : cmdList.CmdBuffer) 451 | { 452 | if (cmd.UserCallback) 453 | { 454 | // submit commands to preserve ordering 455 | queue_.submitCommands (cmdBuf_.finishList ()); 456 | 457 | // user callback, registered via ImDrawList::AddCallback() 458 | // (ImDrawCallback_ResetRenderState is a special callback value used by the user to 459 | // request the renderer to reset render state.) 460 | if (cmd.UserCallback == ImDrawCallback_ResetRenderState) 461 | queue_.submitCommands (setupCmd); 462 | else 463 | cmd.UserCallback (&cmdList, &cmd); 464 | } 465 | else 466 | { 467 | // project scissor/clipping rectangles into framebuffer space 468 | ImVec4 clip; 469 | clip.x = (cmd.ClipRect.x - clipOff.x) * clipScale.x; 470 | clip.y = (cmd.ClipRect.y - clipOff.y) * clipScale.y; 471 | clip.z = (cmd.ClipRect.z - clipOff.x) * clipScale.x; 472 | clip.w = (cmd.ClipRect.w - clipOff.y) * clipScale.y; 473 | 474 | // check if clip coordinate are outside of the framebuffer 475 | if (clip.x >= width || clip.y >= height || clip.z < 0.0f || clip.w < 0.0f) 476 | continue; 477 | 478 | // keep scissor coordinates inside viewport 479 | if (clip.x < 0.0f) 480 | clip.x = 0.0f; 481 | if (clip.y < 0.0f) 482 | clip.y = 0.0f; 483 | if (clip.z > width) 484 | clip.z = width; 485 | if (clip.w > height) 486 | clip.z = height; 487 | 488 | // apply scissor boundaries 489 | cmdBuf_.setScissors (0, 490 | DkScissor{static_cast(clip.x), static_cast(clip.y), 491 | static_cast(clip.z - clip.x), static_cast(clip.w - clip.y)}); 492 | 493 | // get texture handle 494 | auto const textureHandle = reinterpret_cast (cmd.TextureId); 495 | 496 | // check if we need to bind a new texture 497 | if (!boundTextureHandle || textureHandle != *boundTextureHandle) 498 | { 499 | // check if this is the first draw or changing to or from the font texture 500 | if (!boundTextureHandle || textureHandle == s_fontTextureHandle || 501 | *boundTextureHandle == s_fontTextureHandle) 502 | { 503 | FragUBO fragUBO; 504 | fragUBO.font = (textureHandle == s_fontTextureHandle); 505 | 506 | // update fragment shader UBO 507 | cmdBuf_.pushConstants ( 508 | s_uboMemBlock.getGpuAddr () + 509 | align (sizeof (VertUBO), DK_UNIFORM_BUF_ALIGNMENT), 510 | align (sizeof (FragUBO), DK_UNIFORM_BUF_ALIGNMENT), 511 | 0, 512 | sizeof (FragUBO), 513 | &fragUBO); 514 | } 515 | 516 | boundTextureHandle = textureHandle; 517 | 518 | // bind the new texture 519 | cmdBuf_.bindTextures (DkStage_Fragment, 0, textureHandle); 520 | } 521 | 522 | // draw the draw list 523 | cmdBuf_.drawIndexed (DkPrimitive_Triangles, 524 | cmd.ElemCount, 525 | 1, 526 | cmd.IdxOffset + offsetIdx / sizeof (ImDrawIdx), 527 | cmd.VtxOffset + offsetVtx / sizeof (ImDrawVert), 528 | 0); 529 | } 530 | } 531 | 532 | offsetVtx += vtxSize; 533 | offsetIdx += idxSize; 534 | } 535 | 536 | // submit final commands 537 | queue_.submitCommands (cmdBuf_.finishList ()); 538 | } 539 | -------------------------------------------------------------------------------- /libs/imgui-nx/src/imgui_fsh.glsl: -------------------------------------------------------------------------------- 1 | // ftpd is a server implementation based on the following: 2 | // - RFC 959 (https://tools.ietf.org/html/rfc959) 3 | // - RFC 3659 (https://tools.ietf.org/html/rfc3659) 4 | // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (C) 2020 Michael Theall 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | 28 | #version 460 29 | 30 | layout (location = 0) in vec2 vtxUv; 31 | layout (location = 1) in vec4 vtxColor; 32 | 33 | layout (binding = 0) uniform sampler2D tex; 34 | 35 | layout (std140, binding = 0) uniform FragUBO { 36 | uint font; 37 | } ubo; 38 | 39 | layout (location = 0) out vec4 outColor; 40 | 41 | void main() 42 | { 43 | // font texture is single-channel (alpha) 44 | if (ubo.font != 0) 45 | outColor = vtxColor * vec4 (vec3 (1.0), texture (tex, vtxUv).r); 46 | else 47 | outColor = vtxColor * texture (tex, vtxUv); 48 | } 49 | -------------------------------------------------------------------------------- /libs/imgui-nx/src/imgui_nx.cpp: -------------------------------------------------------------------------------- 1 | // ftpd is a server implementation based on the following: 2 | // - RFC 959 (https://tools.ietf.org/html/rfc959) 3 | // - RFC 3659 (https://tools.ietf.org/html/rfc3659) 4 | // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (C) 2020 Michael Theall 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | 28 | #include "imgui_nx.h" 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | using namespace std::chrono_literals; 40 | 41 | namespace { 42 | 43 | std::chrono::steady_clock::time_point s_lastMouseUpdate; 44 | 45 | float s_width = 1280.0f; 46 | float s_height = 720.0f; 47 | 48 | ImVec2 s_mousePos = ImVec2(0.0f, 0.0f); 49 | 50 | AppletHookCookie s_appletHookCookie; 51 | 52 | PadState s_pad; 53 | 54 | void handleAppletHook(AppletHookType type, void *param) { 55 | if (type != AppletHookType_OnOperationMode) 56 | return; 57 | 58 | switch (appletGetOperationMode()) { 59 | default: 60 | case AppletOperationMode_Handheld: 61 | // use handheld mode resolution (720p) and scale 62 | s_width = 1280.0f, s_height = 720.0f; 63 | ImGui::GetStyle().ScaleAllSizes(1.9f / 2.6f); 64 | ImGui::GetIO().FontGlobalScale = 0.9f; 65 | break; 66 | 67 | case AppletOperationMode_Console: 68 | // use docked mode resolution (1080p) and scale 69 | s_width = 1920.0f, s_height = 1080.0f; 70 | ImGui::GetStyle().ScaleAllSizes(2.6f / 1.9f); 71 | ImGui::GetIO().FontGlobalScale = 1.4f; 72 | break; 73 | } 74 | } 75 | 76 | void updateTouch(ImGuiIO &io_) { 77 | // read touch positions 78 | HidTouchScreenState state = {0}; 79 | auto count = hidGetTouchScreenStates(&state, 1); 80 | if (count < 1 || state.count < 1) { 81 | io_.MouseDown[0] = false; 82 | return; 83 | } 84 | 85 | // set mouse position to touch point 86 | s_mousePos = ImVec2(state.touches[0].x, state.touches[0].y); 87 | io_.MouseDown[0] = true; 88 | } 89 | 90 | void updateKeys(ImGuiIO &io_) { 91 | constexpr std::array mapping = { 92 | std::pair(ImGuiNavInput_Activate, HidNpadButton_A), 93 | std::pair(ImGuiNavInput_Cancel, HidNpadButton_B), 94 | std::pair(ImGuiNavInput_Input, HidNpadButton_X), 95 | std::pair(ImGuiNavInput_Menu, HidNpadButton_Y), 96 | std::pair(ImGuiNavInput_FocusPrev, HidNpadButton_L), 97 | std::pair(ImGuiNavInput_TweakSlow, HidNpadButton_L), 98 | std::pair(ImGuiNavInput_FocusNext, HidNpadButton_R), 99 | std::pair(ImGuiNavInput_TweakFast, HidNpadButton_R), 100 | std::pair(ImGuiNavInput_DpadUp, HidNpadButton_Up), 101 | std::pair(ImGuiNavInput_DpadRight, HidNpadButton_Right), 102 | std::pair(ImGuiNavInput_DpadDown, HidNpadButton_Down), 103 | std::pair(ImGuiNavInput_DpadLeft, HidNpadButton_Left), 104 | }; 105 | 106 | padUpdate(&s_pad); 107 | auto down = padGetButtonsDown(&s_pad); 108 | 109 | for (auto [im, nx]: mapping) 110 | if (down & nx) 111 | io_.NavInputs[im] = 1.0f; 112 | } 113 | 114 | } // namespace 115 | 116 | bool ImGui::nx::init() { 117 | padConfigureInput(1, HidNpadStyleSet_NpadStandard); 118 | padInitializeDefault(&s_pad); 119 | 120 | auto &io = ImGui::GetIO(); 121 | 122 | // Load nintendo font 123 | PlFontData standard, extended; 124 | static ImWchar extended_range[] = {0xe000, 0xe152}; 125 | if (R_SUCCEEDED(plGetSharedFontByType(&standard, PlSharedFontType_Standard)) && 126 | R_SUCCEEDED(plGetSharedFontByType(&extended, PlSharedFontType_NintendoExt))) { 127 | std::uint8_t *px; 128 | int w, h, bpp; 129 | ImFontConfig font_cfg; 130 | 131 | font_cfg.FontDataOwnedByAtlas = false; 132 | io.Fonts->AddFontFromMemoryTTF(standard.address, standard.size, 20.0f, &font_cfg, io.Fonts->GetGlyphRangesDefault()); 133 | font_cfg.MergeMode = true; 134 | io.Fonts->AddFontFromMemoryTTF(extended.address, extended.size, 20.0f, &font_cfg, extended_range); 135 | 136 | // build font atlas 137 | io.Fonts->GetTexDataAsAlpha8(&px, &w, &h, &bpp); 138 | io.Fonts->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight; 139 | io.Fonts->Build(); 140 | } 141 | 142 | auto &style = ImGui::GetStyle(); 143 | auto mode = appletGetOperationMode(); 144 | if (mode == AppletOperationMode_Handheld) { 145 | s_width = 1280.0f, s_height = 720.0f; 146 | style.ScaleAllSizes(1.9f); 147 | io.FontGlobalScale = 0.9f; 148 | } else { 149 | s_width = 1920.0f, s_height = 1080.0f; 150 | style.ScaleAllSizes(2.6f); 151 | io.FontGlobalScale = 1.4f; 152 | } 153 | 154 | // initialize applet hooks 155 | appletHook(&s_appletHookCookie, handleAppletHook, nullptr); 156 | 157 | // disable imgui.ini file 158 | io.IniFilename = nullptr; 159 | 160 | // setup config flags 161 | io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen; 162 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; 163 | 164 | io.BackendFlags |= ImGuiBackendFlags_HasGamepad; 165 | 166 | // disable mouse cursor 167 | io.MouseDrawCursor = false; 168 | 169 | return true; 170 | } 171 | 172 | std::uint64_t ImGui::nx::newFrame() { 173 | auto &io = ImGui::GetIO(); 174 | 175 | // setup display metrics 176 | io.DisplaySize = ImVec2(s_width, s_height); 177 | io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); 178 | 179 | // time step 180 | static auto const start = std::chrono::steady_clock::now(); 181 | static auto prev = start; 182 | auto const now = std::chrono::steady_clock::now(); 183 | 184 | io.DeltaTime = std::chrono::duration (now - prev).count(); 185 | prev = now; 186 | 187 | // update inputs 188 | updateTouch(io); 189 | updateKeys(io); 190 | 191 | // clamp mouse to screen 192 | s_mousePos.x = std::clamp(s_mousePos.x, 0.0f, s_width); 193 | s_mousePos.y = std::clamp(s_mousePos.y, 0.0f, s_height); 194 | io.MousePos = s_mousePos; 195 | 196 | return padGetButtonsDown(&s_pad); 197 | } 198 | 199 | void ImGui::nx::exit() { 200 | // deinitialize applet hooks 201 | appletUnhook(&s_appletHookCookie); 202 | } 203 | -------------------------------------------------------------------------------- /libs/imgui-nx/src/imgui_vsh.glsl: -------------------------------------------------------------------------------- 1 | // ftpd is a server implementation based on the following: 2 | // - RFC 959 (https://tools.ietf.org/html/rfc959) 3 | // - RFC 3659 (https://tools.ietf.org/html/rfc3659) 4 | // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html 5 | // 6 | // The MIT License (MIT) 7 | // 8 | // Copyright (C) 2020 Michael Theall 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | 28 | #version 460 29 | 30 | layout (location = 0) in vec2 inPos; 31 | layout (location = 1) in vec2 inUv; 32 | layout (location = 2) in vec4 inColor; 33 | 34 | layout (location = 0) out vec2 vtxUv; 35 | layout (location = 1) out vec4 vtxColor; 36 | 37 | layout (std140, binding = 0) uniform VertUBO 38 | { 39 | mat4 projMtx; 40 | } ubo; 41 | 42 | void main() 43 | { 44 | gl_Position = ubo.projMtx * vec4 (inPos, 0.0, 1.0); 45 | vtxUv = inUv; 46 | vtxColor = inColor; 47 | } 48 | -------------------------------------------------------------------------------- /libs/imgui/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(DEVKITPRO)),) 2 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 3 | endif 4 | 5 | # ----------------------------------------------- 6 | 7 | TARGET = lib$(notdir $(CURDIR)) 8 | EXTENSION = a 9 | OUT = lib 10 | BUILD = build 11 | SOURCES = imgui -maxdepth 1 12 | INCLUDES = include imgui ../glad/include ../glfw/include 13 | LIBS = 14 | 15 | DEFINES = __SWITCH__ 16 | ARCH = -march=armv8-a+crc+crypto+simd -mtune=cortex-a57 -mtp=soft -fpie 17 | FLAGS = -Wall -pipe -g -O2 -ffunction-sections -fdata-sections 18 | CFLAGS = -std=gnu11 19 | CXXFLAGS = -std=gnu++17 20 | ASFLAGS = 21 | ARFLAGS = -rc 22 | 23 | PREFIX = aarch64-none-elf- 24 | CC = $(PREFIX)gcc 25 | CXX = $(PREFIX)g++ 26 | AS = $(PREFIX)as 27 | AR = $(PREFIX)gcc-ar 28 | RANLIB = $(PREFIX)gcc-ranlib 29 | 30 | # ----------------------------------------------- 31 | 32 | export PATH := $(DEVKITPRO)/tools/bin:$(DEVKITPRO)/devkitA64/bin:$(PORTLIBS)/bin:$(PATH) 33 | 34 | PORTLIBS = $(DEVKITPRO)/portlibs/switch 35 | LIBNX = $(DEVKITPRO)/libnx 36 | LIBS := $(LIBS) $(LIBNX) $(PORTLIBS) 37 | 38 | # ----------------------------------------------- 39 | 40 | CFILES = $(shell find $(SOURCES) -name *.c) 41 | CPPFILES = $(shell find $(SOURCES) -name *.cpp) 42 | SFILES = $(shell find $(SOURCES) -name *.s -or -name *.S) 43 | OFILES = $(CFILES:%=$(BUILD)/%.o) $(CPPFILES:%=$(BUILD)/%.o) $(SFILES:%=$(BUILD)/%.o) 44 | DFILES = $(OFILES:.o=.d) 45 | 46 | LIB_TARGET = $(if $(OUT:=), $(OUT)/$(TARGET).$(EXTENSION), .$(OUT)/$(TARGET).$(EXTENSION)) 47 | 48 | DEFINE_FLAGS = $(addprefix -D,$(DEFINES)) 49 | INCLUDE_FLAGS = $(addprefix -I$(CURDIR)/,$(INCLUDES)) $(foreach dir,$(CUSTOM_LIBS),-I$(CURDIR)/$(dir)/include) \ 50 | $(foreach dir,$(filter-out $(CUSTOM_LIBS),$(LIBS)),-I$(dir)/include) 51 | 52 | # ----------------------------------------------- 53 | 54 | .SUFFIXES: 55 | 56 | .PHONY: all clean 57 | 58 | all: $(LIB_TARGET) 59 | 60 | $(LIB_TARGET): $(OFILES) 61 | @echo " AR " $@ 62 | @mkdir -p $(dir $@) 63 | @rm -f $@ 64 | @$(AR) $(ARFLAGS) $@ $^ 65 | @echo "Built" $(notdir $@) 66 | 67 | $(BUILD)/%.c.o: %.c 68 | @echo " CC " $@ 69 | @mkdir -p $(dir $@) 70 | @$(CC) -MMD -MP $(ARCH) $(FLAGS) $(CFLAGS) $(DEFINE_FLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 71 | 72 | $(BUILD)/%.cpp.o: %.cpp 73 | @echo " CXX " $@ 74 | @mkdir -p $(dir $@) 75 | @$(CXX) -MMD -MP $(ARCH) $(FLAGS) $(CXXFLAGS) $(DEFINE_FLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 76 | 77 | $(BUILD)/%.s.o: %.s %.S 78 | @echo " AS " $@ 79 | @mkdir -p $(dir $@) 80 | @$(AS) -MMD -MP -x assembler-with-cpp $(ARCH) $(FLAGS) $(ASFLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 81 | 82 | clean: 83 | @echo Cleaning... 84 | @rm -rf $(BUILD) $(OUT) 85 | 86 | -include $(DFILES) 87 | -------------------------------------------------------------------------------- /libs/imgui/include/imgui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define IMGUI_DEFINE_MATH_OPERATORS 4 | 5 | #include "../imgui/imgui.h" 6 | #include "../imgui/imgui_internal.h" 7 | -------------------------------------------------------------------------------- /libs/stb_image/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(DEVKITPRO)),) 2 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 3 | endif 4 | 5 | # ----------------------------------------------- 6 | 7 | TARGET = libstbi 8 | EXTENSION = a 9 | OUT = lib 10 | BUILD = build 11 | SOURCES = src 12 | INCLUDES = include 13 | LIBS = 14 | 15 | DEFINES = __SWITCH__ 16 | ARCH = -march=armv8-a+crc+crypto+simd -mtune=cortex-a57 -mtp=soft -fpie 17 | FLAGS = -Wall -pipe -g -O2 -ffunction-sections -fdata-sections 18 | CFLAGS = -std=gnu11 19 | CXXFLAGS = -std=gnu++17 20 | ASFLAGS = 21 | ARFLAGS = -rc 22 | 23 | PREFIX = aarch64-none-elf- 24 | CC = $(PREFIX)gcc 25 | CXX = $(PREFIX)g++ 26 | AS = $(PREFIX)as 27 | AR = $(PREFIX)gcc-ar 28 | RANLIB = $(PREFIX)gcc-ranlib 29 | 30 | # ----------------------------------------------- 31 | 32 | export PATH := $(DEVKITPRO)/tools/bin:$(DEVKITPRO)/devkitA64/bin:$(PORTLIBS)/bin:$(PATH) 33 | 34 | PORTLIBS = $(DEVKITPRO)/portlibs/switch 35 | LIBNX = $(DEVKITPRO)/libnx 36 | LIBS := $(LIBS) $(LIBNX) $(PORTLIBS) 37 | 38 | # ----------------------------------------------- 39 | 40 | CFILES = $(shell find $(SOURCES) -name *.c) 41 | CPPFILES = $(shell find $(SOURCES) -name *.cpp) 42 | SFILES = $(shell find $(SOURCES) -name *.s -or -name *.S) 43 | OFILES = $(CFILES:%=$(BUILD)/%.o) $(CPPFILES:%=$(BUILD)/%.o) $(SFILES:%=$(BUILD)/%.o) 44 | DFILES = $(OFILES:.o=.d) 45 | 46 | LIB_TARGET = $(if $(OUT:=), $(OUT)/$(TARGET).$(EXTENSION), .$(OUT)/$(TARGET).$(EXTENSION)) 47 | 48 | DEFINE_FLAGS = $(addprefix -D,$(DEFINES)) 49 | INCLUDE_FLAGS = $(addprefix -I$(CURDIR)/,$(INCLUDES)) $(foreach dir,$(CUSTOM_LIBS),-I$(CURDIR)/$(dir)/include) \ 50 | $(foreach dir,$(filter-out $(CUSTOM_LIBS),$(LIBS)),-I$(dir)/include) 51 | 52 | # ----------------------------------------------- 53 | 54 | .SUFFIXES: 55 | 56 | .PHONY: all clean 57 | 58 | all: $(LIB_TARGET) 59 | 60 | $(LIB_TARGET): $(OFILES) 61 | @echo " AR " $@ 62 | @mkdir -p $(dir $@) 63 | @rm -f $@ 64 | @$(AR) $(ARFLAGS) $@ $^ 65 | @echo "Built" $(notdir $@) 66 | 67 | $(BUILD)/%.c.o: %.c 68 | @echo " CC " $@ 69 | @mkdir -p $(dir $@) 70 | @$(CC) -MMD -MP $(ARCH) $(FLAGS) $(CFLAGS) $(DEFINE_FLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 71 | 72 | $(BUILD)/%.cpp.o: %.cpp 73 | @echo " CXX " $@ 74 | @mkdir -p $(dir $@) 75 | @$(CXX) -MMD -MP $(ARCH) $(FLAGS) $(CXXFLAGS) $(DEFINE_FLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 76 | 77 | $(BUILD)/%.s.o: %.s %.S 78 | @echo " AS " $@ 79 | @mkdir -p $(dir $@) 80 | @$(AS) -MMD -MP -x assembler-with-cpp $(ARCH) $(FLAGS) $(ASFLAGS) $(INCLUDE_FLAGS) -c $(CURDIR)/$< -o $@ 81 | 82 | clean: 83 | @echo Cleaning... 84 | @rm -rf $(BUILD) $(OUT) 85 | 86 | -include $(DFILES) 87 | -------------------------------------------------------------------------------- /libs/stb_image/src/stb_image.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #define STBI_NEON 3 | #define STBI_ONLY_JPEG 4 | 5 | #include 6 | -------------------------------------------------------------------------------- /source/gfx.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 averne 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | // #include 24 | 25 | #include "imgui_deko3d.h" 26 | #include "imgui_nx.h" 27 | 28 | #include "gfx.hpp" 29 | 30 | namespace im = ImGui; 31 | 32 | namespace fz::gfx { 33 | 34 | namespace { 35 | 36 | constexpr auto MAX_SAMPLERS = 3; 37 | constexpr auto MAX_IMAGES = 4; 38 | 39 | constexpr auto FB_NUM = 2u; 40 | 41 | constexpr auto CMDBUF_SIZE = 1024 * 1024; 42 | 43 | unsigned s_width = 1920; 44 | unsigned s_height = 1080; 45 | 46 | dk::UniqueDevice s_device; 47 | 48 | dk::UniqueMemBlock s_depthMemBlock; 49 | dk::Image s_depthBuffer; 50 | 51 | dk::UniqueMemBlock s_fbMemBlock; 52 | dk::Image s_frameBuffers[FB_NUM]; 53 | 54 | dk::UniqueMemBlock s_cmdMemBlock[FB_NUM]; 55 | dk::UniqueCmdBuf s_cmdBuf[FB_NUM]; 56 | 57 | dk::UniqueMemBlock s_imageMemBlock; 58 | 59 | dk::UniqueMemBlock s_descriptorMemBlock; 60 | dk::SamplerDescriptor *s_samplerDescriptors = nullptr; 61 | dk::ImageDescriptor *s_imageDescriptors = nullptr; 62 | 63 | dk::UniqueQueue s_queue; 64 | dk::UniqueSwapchain s_swapchain; 65 | 66 | void rebuildSwapchain(unsigned const width_, unsigned const height_) { 67 | // destroy old swapchain 68 | s_swapchain = nullptr; 69 | 70 | // create new depth buffer image layout 71 | dk::ImageLayout depthLayout; 72 | dk::ImageLayoutMaker{s_device} 73 | .setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression) 74 | .setFormat(DkImageFormat_Z24S8) 75 | .setDimensions(width_, height_) 76 | .initialize(depthLayout); 77 | 78 | auto const depthAlign = depthLayout.getAlignment(); 79 | auto const depthSize = depthLayout.getSize(); 80 | 81 | // create depth buffer memblock 82 | if (!s_depthMemBlock) { 83 | s_depthMemBlock = dk::MemBlockMaker{s_device, 84 | im::deko3d::align( 85 | depthSize, std::max (depthAlign, DK_MEMBLOCK_ALIGNMENT))} 86 | .setFlags(DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image) 87 | .create(); 88 | } 89 | 90 | s_depthBuffer.initialize(depthLayout, s_depthMemBlock, 0); 91 | 92 | // create framebuffer image layout 93 | dk::ImageLayout fbLayout; 94 | dk::ImageLayoutMaker{s_device} 95 | .setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression) 96 | .setFormat(DkImageFormat_RGBA8_Unorm) 97 | .setDimensions(width_, height_) 98 | .initialize(fbLayout); 99 | 100 | auto const fbAlign = fbLayout.getAlignment(); 101 | auto const fbSize = fbLayout.getSize(); 102 | 103 | // create framebuffer memblock 104 | if (!s_fbMemBlock) { 105 | s_fbMemBlock = dk::MemBlockMaker{s_device, 106 | im::deko3d::align( 107 | FB_NUM * fbSize, std::max (fbAlign, DK_MEMBLOCK_ALIGNMENT))} 108 | .setFlags(DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image) 109 | .create(); 110 | } 111 | 112 | // initialize swapchain images 113 | std::array swapchainImages; 114 | for (unsigned i = 0; i < FB_NUM; ++i) { 115 | swapchainImages[i] = &s_frameBuffers[i]; 116 | s_frameBuffers[i].initialize(fbLayout, s_fbMemBlock, i * fbSize); 117 | } 118 | 119 | // create swapchain 120 | s_swapchain = dk::SwapchainMaker{s_device, nwindowGetDefault(), swapchainImages}.create(); 121 | } 122 | 123 | void deko3dInit() { 124 | // create deko3d device 125 | s_device = dk::DeviceMaker{}.create(); 126 | 127 | // initialize swapchain with maximum resolution 128 | rebuildSwapchain(1920, 1080); 129 | 130 | // create memblocks for each image slot 131 | for (std::size_t i = 0; i < FB_NUM; ++i) { 132 | // create command buffer memblock 133 | s_cmdMemBlock[i] = 134 | dk::MemBlockMaker{s_device, im::deko3d::align(CMDBUF_SIZE, DK_MEMBLOCK_ALIGNMENT)} 135 | .setFlags(DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 136 | .create(); 137 | 138 | // create command buffer 139 | s_cmdBuf[i] = dk::CmdBufMaker{s_device}.create(); 140 | s_cmdBuf[i].addMemory(s_cmdMemBlock[i], 0, s_cmdMemBlock[i].getSize()); 141 | } 142 | 143 | // create image/sampler memblock 144 | static_assert(sizeof(dk::ImageDescriptor) == DK_IMAGE_DESCRIPTOR_ALIGNMENT); 145 | static_assert(sizeof(dk::SamplerDescriptor) == DK_SAMPLER_DESCRIPTOR_ALIGNMENT); 146 | static_assert(DK_IMAGE_DESCRIPTOR_ALIGNMENT == DK_SAMPLER_DESCRIPTOR_ALIGNMENT); 147 | s_descriptorMemBlock = dk::MemBlockMaker{s_device, 148 | im::deko3d::align( 149 | (MAX_SAMPLERS + MAX_IMAGES) * sizeof(dk::ImageDescriptor), DK_MEMBLOCK_ALIGNMENT)} 150 | .setFlags(DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 151 | .create(); 152 | 153 | // get cpu address for descriptors 154 | s_samplerDescriptors = 155 | static_cast (s_descriptorMemBlock.getCpuAddr()); 156 | s_imageDescriptors = 157 | reinterpret_cast (&s_samplerDescriptors[MAX_SAMPLERS]); 158 | 159 | // create queue 160 | s_queue = dk::QueueMaker{s_device}.setFlags(DkQueueFlags_Graphics).create(); 161 | 162 | auto &cmdBuf = s_cmdBuf[0]; 163 | 164 | // bind image/sampler descriptors 165 | cmdBuf.bindSamplerDescriptorSet(s_descriptorMemBlock.getGpuAddr(), MAX_SAMPLERS); 166 | cmdBuf.bindImageDescriptorSet( 167 | s_descriptorMemBlock.getGpuAddr() + MAX_SAMPLERS * sizeof(dk::SamplerDescriptor), 168 | MAX_IMAGES); 169 | s_queue.submitCommands(cmdBuf.finishList()); 170 | s_queue.waitIdle(); 171 | 172 | cmdBuf.clear(); 173 | } 174 | 175 | void deko3dExit() { 176 | // clean up all of the deko3d objects 177 | s_imageMemBlock = nullptr; 178 | s_descriptorMemBlock = nullptr; 179 | 180 | for (unsigned i = 0; i < FB_NUM; ++i) { 181 | s_cmdBuf[i] = nullptr; 182 | s_cmdMemBlock[i] = nullptr; 183 | } 184 | 185 | s_queue = nullptr; 186 | s_swapchain = nullptr; 187 | s_fbMemBlock = nullptr; 188 | s_depthMemBlock = nullptr; 189 | s_device = nullptr; 190 | } 191 | 192 | } // namespace 193 | 194 | bool init() { 195 | im::CreateContext(); 196 | if (!im::nx::init()) 197 | return false; 198 | 199 | deko3dInit(); 200 | im::deko3d::init(s_device, 201 | s_queue, 202 | s_cmdBuf[0], 203 | s_samplerDescriptors[0], 204 | s_imageDescriptors[0], 205 | dkMakeTextureHandle(0, 0), 206 | FB_NUM); 207 | 208 | return true; 209 | } 210 | 211 | bool loop() { 212 | if (!appletMainLoop()) 213 | return false; 214 | 215 | auto down = im::nx::newFrame(); 216 | im::NewFrame(); 217 | 218 | return !(down & HidNpadButton_Plus); 219 | } 220 | 221 | void render() { 222 | im::Render(); 223 | 224 | auto &io = im::GetIO(); 225 | 226 | if (s_width != io.DisplaySize.x || s_height != io.DisplaySize.y) { 227 | s_width = io.DisplaySize.x; 228 | s_height = io.DisplaySize.y; 229 | rebuildSwapchain(s_width, s_height); 230 | } 231 | 232 | // get image from queue 233 | auto const slot = s_queue.acquireImage(s_swapchain); 234 | auto &cmdBuf = s_cmdBuf[slot]; 235 | 236 | cmdBuf.clear(); 237 | 238 | // bind frame/depth buffers and clear them 239 | dk::ImageView colorTarget{s_frameBuffers[slot]}; 240 | dk::ImageView depthTarget{s_depthBuffer}; 241 | cmdBuf.bindRenderTargets(&colorTarget, &depthTarget); 242 | cmdBuf.setScissors(0, DkScissor{0, 0, s_width, s_height}); 243 | cmdBuf.clearColor(0, DkColorMask_RGBA, 0.35f, 0.30f, 0.35f, 1.0f); 244 | cmdBuf.clearDepthStencil(true, 1.0f, 0xFF, 0); 245 | s_queue.submitCommands(cmdBuf.finishList()); 246 | 247 | im::deko3d::render(s_device, s_queue, cmdBuf, slot); 248 | 249 | // wait for fragments to be completed before discarding depth/stencil buffer 250 | cmdBuf.barrier(DkBarrier_Fragments, 0); 251 | cmdBuf.discardDepthStencil(); 252 | 253 | // present image 254 | s_queue.presentImage(s_swapchain, slot); 255 | } 256 | 257 | void exit() { 258 | im::nx::exit(); 259 | 260 | // wait for queue to be idle 261 | s_queue.waitIdle(); 262 | 263 | im::deko3d::exit(); 264 | deko3dExit(); 265 | } 266 | 267 | DkResHandle create_texture(std::uint8_t *data, int width, int height, std::uint32_t sampler_id, std::uint32_t image_id) { 268 | auto image_size = width * height * 4; 269 | 270 | // wait for previous commands to complete 271 | s_queue.waitIdle(); 272 | 273 | dk::ImageLayout layout; 274 | dk::ImageLayoutMaker{s_device} 275 | .setFlags(0) 276 | .setFormat(DkImageFormat_RGBA8_Unorm) 277 | .setDimensions(width, height) 278 | .initialize(layout); 279 | 280 | auto mem_block = dk::MemBlockMaker{s_device, im::deko3d::align(image_size, DK_MEMBLOCK_ALIGNMENT)} 281 | .setFlags(DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) 282 | .create(); 283 | 284 | s_imageMemBlock = dk::MemBlockMaker{s_device, im::deko3d::align(layout.getSize(), DK_MEMBLOCK_ALIGNMENT)} 285 | .setFlags(DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image) 286 | .create(); 287 | 288 | std::memcpy(mem_block.getCpuAddr(), data, image_size); 289 | 290 | dk::Image image; 291 | image.initialize(layout, s_imageMemBlock, 0); 292 | s_imageDescriptors[sampler_id].initialize(image); 293 | 294 | // copy texture to image 295 | dk::ImageView imageView(image); 296 | s_cmdBuf[0].copyBufferToImage({mem_block.getGpuAddr()}, 297 | imageView, 298 | {0, 0, 0, static_cast(width), static_cast(height), 1}); 299 | s_queue.submitCommands(s_cmdBuf[0].finishList()); 300 | 301 | // initialize sampler descriptor 302 | s_samplerDescriptors[sampler_id].initialize( 303 | dk::Sampler{} 304 | .setFilter(DkFilter_Linear, DkFilter_Linear) 305 | .setWrapMode(DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge)); 306 | 307 | // wait for commands to complete before releasing memblocks 308 | s_queue.waitIdle(); 309 | 310 | return dkMakeTextureHandle(image_id, sampler_id); 311 | } 312 | 313 | void TextureDecoder::start(const std::string_view &path, std::uint32_t sampler_id, std::uint32_t image_id) { 314 | this->sampler_id = sampler_id, this->image_id = image_id; 315 | this->thread = std::thread([&, this] { 316 | this->data = stbi_load(path.data(), &this->width, &this->height, nullptr, 4); 317 | if (!this->data) 318 | std::fprintf(stderr, "Failed to load background image: %s\n", stbi_failure_reason()); 319 | this->is_done = true; 320 | }); 321 | 322 | // Set the thread priority to the highest possible to speed to loading times 323 | svcSetThreadPriority(reinterpret_cast(this->thread.native_handle())->handle, 0x3f); 324 | } 325 | 326 | void TextureDecoder::end() { 327 | if (this->has_joined) 328 | return; 329 | this->thread.join(); 330 | this->handle = create_texture(this->data, this->width, this->height, this->sampler_id, this->image_id); 331 | stbi_image_free(this->data); 332 | this->has_joined = true; 333 | } 334 | 335 | } // namespace fz::gfx 336 | -------------------------------------------------------------------------------- /source/gfx.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 averne 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace fz::gfx { 24 | 25 | bool init(); 26 | bool loop(); 27 | void render(); 28 | void exit(); 29 | DkResHandle create_texture(std::uint8_t *data, int width, int height, std::uint32_t sampler_id, std::uint32_t image_id); 30 | 31 | class TextureDecoder { 32 | public: 33 | TextureDecoder() = default; 34 | 35 | TextureDecoder(const std::string_view &path, std::uint32_t sampler_id, std::uint32_t image_id) { 36 | this->start(path, sampler_id, image_id); 37 | } 38 | 39 | void start(const std::string_view &path, std::uint32_t sampler_id, std::uint32_t image_id); 40 | void end(); 41 | 42 | bool done() const { 43 | return this->is_done; 44 | } 45 | 46 | DkResHandle get_handle() { 47 | this->end(); 48 | return this->handle; 49 | } 50 | 51 | private: 52 | std::atomic_bool is_done = false; 53 | bool has_joined = false; 54 | std::thread thread; 55 | 56 | std::uint8_t *data; 57 | int width, height; 58 | std::uint32_t sampler_id, image_id; 59 | DkResHandle handle; 60 | }; 61 | 62 | } // namespace fz::gfx 63 | -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Luis Scheurenbrand 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #include "version_list.hpp" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include "gfx.hpp" 26 | #include 27 | 28 | #ifdef DEBUG 29 | #include 30 | static int nxlink = -1; 31 | #endif 32 | 33 | extern "C" void userAppInit() { 34 | #ifdef DEBUG 35 | socketInitializeDefault(); 36 | nxlink = nxlinkStdio(); 37 | #endif 38 | appletLockExit(); 39 | 40 | /* Initialize Network Interface Manager. */ 41 | nifmInitialize(NifmServiceType_System); 42 | plInitialize(PlServiceType_User); 43 | nsInitialize(); 44 | if (hosversionAtLeast(6,0,0)) 45 | avmInitialize(); 46 | 47 | romfsInit(); 48 | hidInitializeTouchScreen(); 49 | } 50 | 51 | extern "C" void userAppExit(void) { 52 | #ifdef DEBUG 53 | ::close(nxlink); 54 | socketExit(); 55 | #endif 56 | romfsExit(); 57 | 58 | if (hosversionAtLeast(6,0,0)) 59 | avmExit(); 60 | nsExit(); 61 | plExit(); 62 | nifmExit(); 63 | 64 | appletUnlockExit(); 65 | } 66 | 67 | int main() { 68 | if (!fz::gfx::init()) 69 | return EXIT_FAILURE; 70 | 71 | auto version_list = VersionList(); 72 | 73 | NifmInternetConnectionType contype; 74 | u32 wifiStrength=0; 75 | NifmInternetConnectionStatus connectionStatus; 76 | 77 | bool has_avm = hosversionAtLeast(6,0,0); 78 | bool avm_warn = !has_avm; 79 | bool has_internet = R_SUCCEEDED(nifmGetInternetConnectionStatus(&contype, &wifiStrength, &connectionStatus)); 80 | bool net_warn = !has_internet; 81 | bool join = false; 82 | 83 | auto status_thread = std::thread([&] { 84 | /* Make network request. */ 85 | NifmRequest request; 86 | nifmCreateRequest(&request, true); 87 | 88 | /* Submit request. */ 89 | nifmRequestSubmitAndWait(&request); 90 | 91 | bool previous = has_internet; 92 | 93 | do { 94 | /* Confirm network availability. */ 95 | has_internet = R_SUCCEEDED(nifmGetInternetConnectionStatus(&contype, &wifiStrength, &connectionStatus)); 96 | if (previous != has_internet) { 97 | net_warn = !has_internet; 98 | previous = has_internet; 99 | } 100 | } while (svcSleepThread(100'000'000), !join); 101 | 102 | nifmRequestClose(&request); 103 | }); 104 | 105 | while (fz::gfx::loop()) { 106 | auto [width, height] = ImGui::GetIO().DisplaySize; 107 | auto factor = width / 1280.0f; 108 | 109 | ImGui::SetNextWindowPos(ImVec2{40.f, 22.5f} * factor, ImGuiCond_Always); 110 | ImGui::SetNextWindowSize(ImVec2{1200.f, 675.f} * factor, ImGuiCond_Always); 111 | 112 | if (ImGui::Begin("UpThemAll", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBringToFrontOnFocus)) { 113 | if (has_internet && ImGui::Button("Update them all")) { 114 | version_list.UpdateAllApplications(); 115 | } 116 | if (has_internet) { 117 | ImGui::SameLine(); 118 | } 119 | if (ImGui::Button("Refresh List")) { 120 | version_list.Refresh(); 121 | } 122 | if (has_avm && (ImGui::SameLine(), ImGui::Button("Clear Version List"))) { 123 | version_list.Nuke(); 124 | } 125 | 126 | version_list.List(has_internet); 127 | ImGui::End(); 128 | } 129 | 130 | ImGui::SetNextWindowPos(ImVec2{400.f, 280.f} * factor, ImGuiCond_FirstUseEver); 131 | ImGui::SetNextWindowSize(ImVec2{480.f, 160.f} * factor, ImGuiCond_FirstUseEver); 132 | if (net_warn && ImGui::Begin("No Internet", &net_warn, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize)) { 133 | ImGui::Text("No internet connection available.\n\nUpdate functionality disabled."); 134 | if (ImGui::Button("Ok")) 135 | net_warn = false; 136 | ImGui::End(); 137 | } 138 | 139 | ImGui::SetNextWindowPos(ImVec2{350.f, 280.f} * factor, ImGuiCond_FirstUseEver); 140 | ImGui::SetNextWindowSize(ImVec2{580.f, 160.f} * factor, ImGuiCond_FirstUseEver); 141 | if (avm_warn && ImGui::Begin("<6.0.0", &avm_warn, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize)) { 142 | ImGui::Text("On this firmware version the Application Version Manager isn't available.\n\nClearing the update nag isn't possible."); 143 | if (ImGui::Button("Ok")) 144 | avm_warn = false; 145 | ImGui::End(); 146 | } 147 | 148 | fz::gfx::render(); 149 | } 150 | 151 | fz::gfx::exit(); 152 | 153 | join = true; 154 | status_thread.join(); 155 | 156 | return EXIT_SUCCESS; 157 | } 158 | -------------------------------------------------------------------------------- /source/ns.cpp: -------------------------------------------------------------------------------- 1 | #include "ns.h" 2 | 3 | #include 4 | 5 | static Result _nsVersionNoInNoOut(u32 cmd_id) { 6 | Result rc=0; 7 | Service srv={}; 8 | rc = nsGetApplicationVersionInterface(&srv); 9 | 10 | if (R_SUCCEEDED(rc)) rc = serviceDispatch(&srv, cmd_id); 11 | 12 | serviceClose(&srv); 13 | return rc; 14 | } 15 | 16 | static Result _nsVersionNoInBufOut(void* buffer, size_t size, u32 *out, u32 cmd_id) { 17 | Result rc=0; 18 | Service srv={}; 19 | rc = nsGetApplicationVersionInterface(&srv); 20 | 21 | if (R_SUCCEEDED(rc)) rc = serviceDispatchOut(&srv, cmd_id, *out, 22 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, 23 | .buffers = { { buffer, size } }, 24 | ); 25 | 26 | serviceClose(&srv); 27 | return rc; 28 | } 29 | 30 | static Result _nsPushVersion(u64 application_id, u32 version, u32 cmd_id) { 31 | Result rc=0; 32 | Service srv={}; 33 | rc = nsGetApplicationVersionInterface(&srv); 34 | 35 | const struct { 36 | u32 version; 37 | u64 application_id; 38 | } in = { version, application_id }; 39 | 40 | if (R_SUCCEEDED(rc)) rc = serviceDispatchIn(&srv, cmd_id, in); 41 | 42 | serviceClose(&srv); 43 | return rc; 44 | } 45 | 46 | Result nsGetLaunchRequiredVersion(u64 application_id, u32 *version) { 47 | Result rc=0; 48 | Service srv={}; 49 | rc = nsGetApplicationVersionInterface(&srv); 50 | 51 | if (R_SUCCEEDED(rc)) rc = serviceDispatchInOut(&srv, 0, application_id, *version); 52 | 53 | serviceClose(&srv); 54 | return rc; 55 | } 56 | 57 | Result nsUpgradeLaunchRequiredVersion(u64 application_id, u32 version) { 58 | return _nsPushVersion(application_id, version, 1); 59 | } 60 | 61 | Result nsUpdateVersionList(AvmVersionListEntry *buffer, size_t count) { 62 | Result rc=0; 63 | Service srv={}; 64 | rc = nsGetApplicationVersionInterface(&srv); 65 | 66 | if (R_SUCCEEDED(rc)) rc = serviceDispatch(&srv, 35, 67 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, 68 | .buffers = { { buffer, count * sizeof(*buffer) } }, 69 | ); 70 | 71 | serviceClose(&srv); 72 | return rc; 73 | } 74 | 75 | Result nsPushLaunchVersion(u64 application_id, u32 version) { 76 | return _nsPushVersion(application_id, version, 36); 77 | } 78 | 79 | Result nsListRequiredVersion(AvmRequiredVersionEntry *buffer, size_t count, u32 *out) { 80 | return _nsVersionNoInBufOut(buffer, count * sizeof(*buffer), out, 37); 81 | } 82 | 83 | Result nsRequestVersionList() { 84 | return _nsVersionNoInNoOut(800); 85 | } 86 | 87 | Result nsListVersionList(AvmVersionListEntry *buffer, size_t count, u32 *out) { 88 | return _nsVersionNoInBufOut(buffer, count * sizeof(*buffer), out, 801); 89 | } 90 | 91 | Result nsRequestVersionListData(AsyncValue *a) { 92 | Result rc=0; 93 | Service srv={}; 94 | 95 | rc = nsGetApplicationVersionInterface(&srv); 96 | 97 | memset(a, 0, sizeof(*a)); 98 | Handle event = INVALID_HANDLE; 99 | if (R_SUCCEEDED(rc)) rc = serviceDispatch(&srv, 802, 100 | .out_num_objects = 1, 101 | .out_objects = &a->s, 102 | .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, 103 | .out_handles = &event, 104 | ); 105 | 106 | if (R_SUCCEEDED(rc)) 107 | eventLoadRemote(&a->event, event, false); 108 | 109 | serviceClose(&srv); 110 | return rc; 111 | } 112 | 113 | Result nsPerformAutoUpdate(void) { 114 | return _nsVersionNoInNoOut(1000); 115 | } 116 | 117 | Result nsListAutoUpdateSchedule(void* unk, size_t size, u32 *out) { 118 | return _nsVersionNoInBufOut(unk, size, out, 1001); 119 | } 120 | -------------------------------------------------------------------------------- /source/ns.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ns.h 3 | * @brief NS services IPC wrapper. 4 | * @author yellows8 5 | * @copyright libnx Authors 6 | */ 7 | #pragma once 8 | #include 9 | 10 | typedef enum { 11 | NsApplicationRecordType_Running = 0x0, 12 | NsApplicationRecordType_Installed = 0x3, 13 | NsApplicationRecordType_Downloading = 0x4, 14 | NsApplicationRecordType_GamecardMissing = 0x5, 15 | NsApplicationRecordType_Downloaded = 0x6, 16 | NsApplicationRecordType_Updated = 0xa, 17 | NsApplicationRecordType_Archived = 0xb, 18 | NsApplicationRecordType_AlreadyStarted = 0x10, 19 | } NsApplicationRecordType; 20 | 21 | /// IApplicationVersionInterface 22 | Result nsGetLaunchRequiredVersion(u64 application_id, u32 *version); 23 | Result nsUpgradeLaunchRequiredVersion(u64 application_id, u32 version); 24 | Result nsUpdateVersionList(AvmVersionListEntry *buffer, size_t count); 25 | Result nsPushLaunchVersion(u64 application_id, u32 version); 26 | Result nsListRequiredVersion(AvmRequiredVersionEntry *buffer, size_t count, u32 *out); 27 | Result nsRequestVersionList(void); 28 | Result nsListVersionList(AvmVersionListEntry *buffer, size_t count, u32 *out); 29 | Result nsRequestVersionListData(AsyncValue *a); 30 | Result nsPerformAutoUpdate(void); 31 | Result nsListAutoUpdateSchedule(void* unk, size_t size, u32 *out); 32 | -------------------------------------------------------------------------------- /source/version_list.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2021 Luis Scheurenbrand 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #include 18 | 19 | #include "version_list.hpp" 20 | 21 | #include "gfx.hpp" 22 | #include 23 | 24 | #include "ns.h" 25 | 26 | VersionList::VersionList() { 27 | this->Refresh(); 28 | } 29 | 30 | void VersionList::Refresh() { 31 | this->impl.resize(0x4000); 32 | u32 count=0; 33 | nsListVersionList(this->impl.data(), this->impl.size(), &count); 34 | this->impl.resize(count); 35 | 36 | this->UpdateAvailable(); 37 | } 38 | 39 | /* Get installed version. 0 if no patch is installed. */ 40 | u32 VersionList::GetInstalledVersion(ApplicationId application_id) const noexcept { 41 | s32 index=0, count=0; 42 | NsApplicationContentMetaStatus meta={}; 43 | while (R_SUCCEEDED(nsListApplicationContentMetaStatus(application_id, index++, &meta, 1, &count)) && count != 0) { 44 | if (meta.meta_type == NcmContentMetaType_Patch) { 45 | return meta.version; 46 | } 47 | } 48 | return 0; 49 | } 50 | 51 | /* Get highest available version. 0 if not found. */ 52 | u32 VersionList::GetAvailableVersion(ApplicationId application_id) const noexcept { 53 | u64 patch_id = application_id | 0x800; 54 | const auto it = std::find_if(std::cbegin(impl), std::cend(impl), [&](const auto& entry) { return patch_id == entry.application_id; }); 55 | 56 | if (it != std::end(impl)) { 57 | return it->version; 58 | } else { 59 | return 0; 60 | } 61 | } 62 | 63 | u32 VersionList::GetLaunchRequiredVersion(ApplicationId application_id) const noexcept { 64 | u32 version = 0; 65 | if (R_FAILED(nsGetLaunchRequiredVersion(application_id, &version))) 66 | return 0; 67 | return version; 68 | } 69 | 70 | static NsApplicationControlData nacp={}; 71 | 72 | const char* VersionList::GetApplicationName(ApplicationId application_id) const noexcept { 73 | NacpLanguageEntry *entry = &nacp.nacp.lang[0]; 74 | u64 size=0; 75 | nsGetApplicationControlData(NsApplicationControlSource_Storage, application_id, &nacp, sizeof(nacp), &size); 76 | nacpGetLanguageEntry(&nacp.nacp, &entry); 77 | return entry->name; 78 | } 79 | 80 | const u8* VersionList::GetThumbnail(ApplicationId application_id) const noexcept { 81 | u64 size=0; 82 | nsGetApplicationControlData(NsApplicationControlSource_Storage, application_id, &nacp, sizeof(nacp), &size); 83 | return nacp.icon; 84 | } 85 | 86 | bool VersionList::UpdateSynchronous(ApplicationId application_id) const noexcept { 87 | this->log.appendf("Updating: [%016lX]: %s\n", application_id, GetApplicationName(application_id)); 88 | 89 | /* Request update. */ 90 | AsyncResult async; 91 | Result rc = nsRequestUpdateApplication2(&async, application_id); 92 | if (R_SUCCEEDED(rc)) { 93 | /* Wait for result. */ 94 | rc = asyncResultGet(&async); 95 | asyncResultClose(&async); 96 | } 97 | 98 | if (R_FAILED(rc)) 99 | this->log.appendf("Update failed: 0x%x\n", rc); 100 | 101 | return R_SUCCEEDED(rc); 102 | } 103 | 104 | void VersionList::UpdateAllApplications() noexcept { 105 | for (const auto &[application_id, pair]: this->available) { 106 | UpdateSynchronous(application_id); 107 | } 108 | 109 | this->available.clear(); 110 | this->selected = 0; 111 | } 112 | 113 | void VersionList::List(bool has_internet) noexcept { 114 | static DkResHandle handle = 0; 115 | 116 | if (ImGui::BeginChild("left pane", ImVec2{750.f, 400.f}, true)) { 117 | for (const auto &[application_id, pair]: this->available) { 118 | const auto &[name, required] = pair; 119 | if (required) 120 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.94f, 0.33f, 0.31f, 1.f}); 121 | if (ImGui::Selectable(name.c_str(), this->selected == application_id)) { 122 | if (this->selected != application_id) { 123 | auto jpeg = GetThumbnail(application_id); 124 | /* Decode image to */ 125 | int w,h; 126 | auto data = stbi_load_from_memory(jpeg, 0x20000, &w, &h, nullptr, 4); 127 | handle = fz::gfx::create_texture(data, w, h, 1, 1); 128 | free(data); 129 | this->selected = application_id; 130 | } 131 | } 132 | if (required) 133 | ImGui::PopStyleColor(); 134 | } 135 | ImGui::EndChild(); 136 | } 137 | ImGui::SameLine(); 138 | 139 | { 140 | ImGui::BeginGroup(); 141 | if (this->selected != 0) { 142 | if (!this->available.contains(this->selected)) 143 | this->selected = 0; 144 | 145 | auto &[name, required] = this->available.at(this->selected); 146 | 147 | ImGui::BeginChild("item view", ImVec2{0.f, 400.f - ImGui::GetFrameHeightWithSpacing()}); 148 | ImGui::Text(name.c_str()); 149 | ImGui::Separator(); 150 | if (handle != 0) { 151 | static auto ImageSize = ImVec2{256.f, 256.f}; 152 | ImGui::SetCursorPos((ImGui::GetWindowSize() - ImageSize) * 0.5f); 153 | ImGui::Image(reinterpret_cast(static_cast(handle)), ImageSize); 154 | } 155 | ImGui::EndChild(); 156 | 157 | if (has_internet && ImGui::Button("Update")) { 158 | if (UpdateSynchronous(this->selected)) { 159 | this->available.erase(this->selected); 160 | this->selected = 0; 161 | } 162 | } 163 | 164 | if (has_internet && required) 165 | ImGui::SameLine(); 166 | 167 | if (required && ImGui::Button("Reset Launch Version")) { 168 | this->log.appendf("Resetting launch required version for %s [%016lX]", name.c_str(), this->selected); 169 | nsPushLaunchVersion(this->selected, 0); 170 | required = false; 171 | } 172 | } 173 | 174 | ImGui::EndGroup(); 175 | } 176 | 177 | if (ImGui::BeginChild("Log", ImVec2{0.f, 0.f}, true)) { 178 | ImGui::TextUnformatted(this->log.begin(), this->log.end()); 179 | ImGui::EndChild(); 180 | } 181 | } 182 | 183 | void VersionList::Nuke() noexcept { 184 | AvmVersionListImporter importer={}; 185 | avmGetVersionListImporter(&importer); 186 | avmVersionListImporterSetTimestamp(&importer, 1609838100); 187 | avmVersionListImporterSetData(&importer, nullptr, 0); 188 | avmVersionListImporterFlush(&importer); 189 | avmVersionListImporterClose(&importer); 190 | 191 | Refresh(); 192 | } 193 | 194 | void VersionList::UpdateAvailable() { 195 | this->available.clear(); 196 | this->selected = 0; 197 | 198 | /* Iterate over installed applications. */ 199 | s32 offset=0, count=0; 200 | NsApplicationRecord record; 201 | while (R_SUCCEEDED(nsListApplicationRecord(&record, 1, offset++, &count)) && count != 0) { 202 | /* Skip archived and downloading applications. */ 203 | if (record.type == NsApplicationRecordType_Archived || record.type == NsApplicationRecordType_Downloading) 204 | continue; 205 | 206 | const u64 application_id = record.application_id; 207 | 208 | const u32 installed = GetInstalledVersion(application_id); 209 | const u32 available = GetAvailableVersion(application_id); 210 | const u32 required = GetLaunchRequiredVersion(application_id); 211 | 212 | /* Check if latest version is already installed. */ 213 | if (installed >= available && installed >= required) 214 | continue; 215 | 216 | const auto app_name = GetApplicationName(application_id); 217 | 218 | this->log.appendf("Adding: %s, installed: %d, available: %d\n", app_name, installed, available); 219 | 220 | this->available[application_id] = { app_name, required > installed }; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /source/version_list.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2021 Luis Scheurenbrand 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms and conditions of the GNU General Public License, 6 | * version 2, as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope it will be useful, but WITHOUT 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 | * more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | using ApplicationId = u64; 27 | 28 | class VersionList { 29 | private: 30 | std::vector impl; 31 | std::unordered_map> available; 32 | ApplicationId selected = 0; 33 | mutable ImGuiTextBuffer log; 34 | 35 | public: 36 | VersionList(); 37 | 38 | void Refresh(); 39 | 40 | u32 GetInstalledVersion(ApplicationId application_id) const noexcept; 41 | u32 GetAvailableVersion(ApplicationId application_id) const noexcept; 42 | u32 GetLaunchRequiredVersion(ApplicationId application_id) const noexcept; 43 | const char* GetApplicationName(ApplicationId application_id) const noexcept; 44 | const u8* GetThumbnail(ApplicationId application_id) const noexcept; 45 | bool UpdateSynchronous(ApplicationId application_id) const noexcept; 46 | void UpdateAllApplications() noexcept; 47 | 48 | void List(bool has_internet) noexcept; 49 | void Nuke() noexcept; 50 | 51 | private: 52 | void UpdateAvailable(); 53 | }; 54 | --------------------------------------------------------------------------------