commit 5d8ba66682d4a72a76333516fd42d3ae947da454 Author: Joshua Goins Date: Wed Jan 3 15:59:26 2024 -0500 Add old files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9cac77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +!/tools/* + +# custom 3ds guff +build/ +output/ +*.smdh +*.zip +*.cia +*.elf +*.3dsx \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d31da2f --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Joshua Goins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..87077ff --- /dev/null +++ b/Makefile @@ -0,0 +1,226 @@ +.SUFFIXES: + +#--------------------------------------------------------------------------------- +# Environment Setup +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPRO") +endif + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# Directory Setup +#--------------------------------------------------------------------------------- +BUILD := build +OUTPUT := output +RESOURCES := resources +DATA := data +ROMFS := romfs +SOURCES := source +INCLUDES := $(SOURCES) include + +#--------------------------------------------------------------------------------- +# Resource Setup +#--------------------------------------------------------------------------------- +APP_INFO := $(RESOURCES)/AppInfo +BANNER_AUDIO := $(RESOURCES)/audio +BANNER_IMAGE := $(RESOURCES)/banner +ICON := $(RESOURCES)/icon.png + +#--------------------------------------------------------------------------------- +# Build Setup +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard + +COMMON_FLAGS := -g -Wall -Wno-strict-aliasing -O3 -mword-relocations -fomit-frame-pointer -ffast-math $(ARCH) $(INCLUDE) -DARM11 -D_3DS $(BUILD_FLAGS) -fpermissive +CFLAGS := $(COMMON_FLAGS) -std=gnu99 +CXXFLAGS := $(COMMON_FLAGS) -std=c++11 +ifeq ($(ENABLE_EXCEPTIONS),) + CXXFLAGS += -fno-rtti -fno-exceptions +endif + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lcitro3d -lctru -lm +LIBDIRS := $(PORTLIBS) $(CTRULIB) ./lib + +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +# Build Variable Setup +#--------------------------------------------------------------------------------- +recurse = $(shell find $2 -type $1 -name '$3' 2> /dev/null) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(call recurse,f,$(dir),*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(call recurse,f,$(dir),*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(call recurse,f,$(dir),*.s))) +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(call recurse,f,$(dir),*.pica))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(call recurse,f,$(dir),*.*))) + +export OFILES := $(addsuffix .o,$(BINFILES)) $(PICAFILES:.pica=.shbin.o) $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) $(foreach dir,$(LIBDIRS),-I$(dir)/include) -I$(CURDIR)/$(BUILD) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export DEPSDIR := $(CURDIR)/$(BUILD) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir) $(call recurse,d,$(CURDIR)/$(dir),*)) $(foreach dir,$(DATA),$(CURDIR)/$(dir) $(call recurse,d,$(CURDIR)/$(dir),*)) + +export TOPDIR := $(CURDIR) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +# Initial Targets +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT) + +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +# Build Information Setup +#--------------------------------------------------------------------------------- +DEPENDS := $(OFILES:.o=.d) + +include $(TOPDIR)/$(APP_INFO) +APP_TITLE := $(shell echo "$(APP_TITLE)" | cut -c1-128) +APP_DESCRIPTION := $(shell echo "$(APP_DESCRIPTION)" | cut -c1-256) +APP_AUTHOR := $(shell echo "$(APP_AUTHOR)" | cut -c1-128) +APP_PRODUCT_CODE := $(shell echo $(APP_PRODUCT_CODE) | cut -c1-16) +APP_UNIQUE_ID := $(shell echo $(APP_UNIQUE_ID) | cut -c1-7) +ifneq ("$(wildcard $(TOPDIR)/$(BANNER_IMAGE).cgfx)","") + BANNER_IMAGE_FILE := $(TOPDIR)/$(BANNER_IMAGE).cgfx + BANNER_IMAGE_ARG := -ci $(BANNER_IMAGE_FILE) +else + BANNER_IMAGE_FILE := $(TOPDIR)/$(BANNER_IMAGE).png + BANNER_IMAGE_ARG := -i $(BANNER_IMAGE_FILE) +endif + +ifneq ("$(wildcard $(TOPDIR)/$(BANNER_AUDIO).cwav)","") + BANNER_AUDIO_FILE := $(TOPDIR)/$(BANNER_AUDIO).cwav + BANNER_AUDIO_ARG := -ca $(BANNER_AUDIO_FILE) +else + BANNER_AUDIO_FILE := $(TOPDIR)/$(BANNER_AUDIO).wav + BANNER_AUDIO_ARG := -a $(BANNER_AUDIO_FILE) +endif + +EMPTY := +SPACE := $(EMPTY) $(EMPTY) +OUTPUT_NAME := $(subst $(SPACE),,$(APP_TITLE)) +OUTPUT_DIR := $(TOPDIR)/$(OUTPUT) +OUTPUT_FILE := $(OUTPUT_DIR)/$(OUTPUT_NAME) + +APP_ICON := $(TOPDIR)/$(ICON) +APP_ROMFS := $(TOPDIR)/$(ROMFS) + +RSF := $(TOPDIR)/tools/template.rsf +COMMON_MAKEROM_PARAMS := -rsf $(RSF) -target t -exefslogo -elf $(OUTPUT_FILE).elf -icon icon.icn -banner banner.bnr -DAPP_TITLE="$(APP_TITLE)" -DAPP_PRODUCT_CODE="$(APP_PRODUCT_CODE)" -DAPP_UNIQUE_ID="$(APP_UNIQUE_ID)" -DAPP_ROMFS="$(APP_ROMFS)" -DAPP_SYSTEM_MODE="64MB" -DAPP_SYSTEM_MODE_EXT="Legacy" + +ifeq ($(OS),Windows_NT) + MAKEROM = $(TOPDIR)/tools/makerom.exe + BANNERTOOL = $(TOPDIR)/tools/bannertool.exe +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + MAKEROM = $(TOPDIR)/tools/makerom-linux + BANNERTOOL = $(TOPDIR)/tools/bannertool-linux + endif + ifeq ($(UNAME_S),Darwin) + MAKEROM = $(TOPDIR)/tools/makerom-mac + BANNERTOOL = $(TOPDIR)/tools/bannertool-mac + endif +endif + +ifeq ($(OS),Windows_NT) + ZIP_CMD = $(TOPDIR)/tools/7za.exe a -tzip $(OUTPUT_FILE).zip $(OUTPUT_NAME).elf $(OUTPUT_NAME).3ds $(OUTPUT_NAME).cia + DEL_CMD = rm -r $(TOPDIR)/output/3ds +else + ZIP_CMD = zip -r $(OUTPUT_FILE).zip $(OUTPUT_NAME).elf $(OUTPUT_NAME).3ds $(OUTPUT_NAME).cia 3ds > /dev/null; \ + DEL_CMD = rm -r 3ds +endif + +_3DSXFLAGS += --smdh=$(OUTPUT_FILE).smdh +ifneq ("$(wildcard $(TOPDIR)/$(ROMFS))","") + _3DSXFLAGS += --romfs=$(TOPDIR)/$(ROMFS) +endif + +#--------------------------------------------------------------------------------- +# Main Targets +#--------------------------------------------------------------------------------- +.PHONY: all +all: $(OUTPUT_FILE).zip + +$(OUTPUT_DIR): + @[ -d $@ ] || mkdir -p $@ + +banner.bnr: $(BANNER_IMAGE_FILE) $(BANNER_AUDIO_FILE) + @$(BANNERTOOL) makebanner $(BANNER_IMAGE_ARG) $(BANNER_AUDIO_ARG) -o banner.bnr > /dev/null + +icon.icn: $(TOPDIR)/$(ICON) + @$(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_TITLE)" -p "$(APP_AUTHOR)" -i $(TOPDIR)/$(ICON) -o icon.icn > /dev/null + +$(OUTPUT_FILE).elf: $(OFILES) + +$(OUTPUT_FILE).3dsx: $(OUTPUT_FILE).elf + +$(OUTPUT_FILE).3ds: $(OUTPUT_FILE).elf banner.bnr icon.icn + @$(MAKEROM) -f cci -o $(OUTPUT_FILE).3ds -DAPP_ENCRYPTED=true $(COMMON_MAKEROM_PARAMS) + @echo "built ... $(notdir $@)" + +$(OUTPUT_FILE).cia: $(OUTPUT_FILE).elf banner.bnr icon.icn + @$(MAKEROM) -f cia -o $(OUTPUT_FILE).cia -DAPP_ENCRYPTED=false $(COMMON_MAKEROM_PARAMS) + @echo "built ... $(notdir $@)" + +$(OUTPUT_FILE).zip: $(OUTPUT_DIR) $(OUTPUT_FILE).elf $(OUTPUT_FILE).smdh $(OUTPUT_FILE).3dsx $(OUTPUT_FILE).3ds $(OUTPUT_FILE).cia + @cd $(OUTPUT_DIR); \ + mkdir -p 3ds/$(OUTPUT_NAME); \ + cp $(OUTPUT_FILE).3dsx 3ds/$(OUTPUT_NAME); \ + cp $(OUTPUT_FILE).smdh 3ds/$(OUTPUT_NAME); \ + $(ZIP_CMD) + $(DEL_CMD) + @echo "built ... $(notdir $@)" + +#--------------------------------------------------------------------------------- +# Binary Data Rules +#--------------------------------------------------------------------------------- +%.bin.o: %.bin + @echo $(notdir $<) + @$(bin2o) + +%.shbin.o: %.pica + @echo $(notdir $<) + $(eval CURBIN := $(patsubst %.pica,%.shbin,$(notdir $<))) + $(eval CURH := $(patsubst %.pica,%.psh.h,$(notdir $<))) + @picasso -h $(CURH) -o $(CURBIN) $< + @bin2s $(CURBIN) | $(AS) -o $@ + @echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h + @echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h + @echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb9aeb7 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# CtrOrganizer + +(This is a project from February 2016) + +A 3DS File Manager. + +Last tested library commits: + +* *ctrulib* [`7d42b57`](https://github.com/fincs/citro3d/tree/7d42b57289483fd59b770cdfa0f2923086508014) + +* *citro3d* [`bb98537`](https://github.com/smealum/ctrulib/tree/bb9853798bfa36dfe84a9ca9ed2ed7ad82192d65) + +# Building +Since this project uses the [3DS Homebrew Template](https://github.com/thedax/3DSHomebrewTemplate) all of the required makerom and bannertool tools are already provided if you don't have them. + +`make` + +After you run make check the output directory for your binary files. + +# TODO +These things are planned currently: +* ~~GPU Rendering~~ +* Hex view ([similar to CtrXplorer](https://i.imgur.com/wWe72jf.png)) +* Image view +* UI Improvements + * Status bar + * Options menu + * Size and free space +* ~~State managment~~ + * ~~Make it easier to add states~~ + * ~~Inherit from base class so we can hold states in an array~~ + +# Credits +* Thanks to the [devkitPro](http://devkitpro.org/) team and the [contributors of ctrulib](https://github.com/smealum/ctrulib/graphs/contributors) for [3ds-examples](https://github.com/devkitPro/3ds-examples) and the [amazing ctrulib library](https://github.com/smealum/ctrulib). diff --git a/include/browse_entry.h b/include/browse_entry.h new file mode 100644 index 0000000..f58dc27 --- /dev/null +++ b/include/browse_entry.h @@ -0,0 +1,7 @@ +#pragma once + +class BrowseEntry { +public: + std::string name; + bool isDirectory = false; +}; \ No newline at end of file diff --git a/include/settingsviewer.h b/include/settingsviewer.h new file mode 100644 index 0000000..f2f9a57 --- /dev/null +++ b/include/settingsviewer.h @@ -0,0 +1,14 @@ +#pragma once +#include "viewer.h" +#include "textengine.h" +#include +#include + +class SettingsViewer : public Viewer { +public: + void Init(); + void Cleanup(); + + bool Update(u32 key); + void Draw(TextEngine* t); +}; \ No newline at end of file diff --git a/include/textengine.h b/include/textengine.h new file mode 100644 index 0000000..ca3edb8 --- /dev/null +++ b/include/textengine.h @@ -0,0 +1,49 @@ +#pragma once +#include <3ds.h> +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vshader_v_shbin.h" +#include + +#define CLEAR_COLOR 0x000000FF + +#define DISPLAY_TRANSFER_FLAGS \ + (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \ + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \ + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) + +#define TEXT_VTX_ARRAY_COUNT (4*1024) + +typedef struct { float position[3]; float texcoord[2]; } textVertex_s; + +class TextEngine { +public: + void Initialize(gfxScreen_t scn); + void setTextColor(u32 color); + void renderText(float x, float y, float scaleX, float scaleY, bool baseline, const char* text); + void Cleanup(); + void Update(); + + C3D_RenderTarget* target; + +private: + void addTextVertex(float vx, float vy, float tx, float ty); + + DVLB_s* vshader_dvlb; + shaderProgram_s program; + + textVertex_s* textVtxArray; + + C3D_Mtx projection; + C3D_Tex* glyphSheets; + + int uLoc_projection; + int textVtxArrayPos; +}; \ No newline at end of file diff --git a/include/textviewer.h b/include/textviewer.h new file mode 100644 index 0000000..ec60cff --- /dev/null +++ b/include/textviewer.h @@ -0,0 +1,20 @@ +#pragma once +#include "viewer.h" +#include "textengine.h" +#include +#include + +class TextViewer : public Viewer { +public: + void Init(); + void Cleanup(); + + bool Update(u32 key); + void Draw(TextEngine* t); + +private: + int currentindex = 0; + int textoffset = 0; + + std::vector lineEntries; +}; \ No newline at end of file diff --git a/include/utility.h b/include/utility.h new file mode 100644 index 0000000..625afae --- /dev/null +++ b/include/utility.h @@ -0,0 +1,11 @@ +#pragma once +#include + +//Taken from http://stackoverflow.com/a/5590404 +#define SSTR( x ) static_cast< std::ostringstream & >( \ + ( std::ostringstream() << std::dec << x ) ).str() + +class Utility { +public: + static std::string truncate(std::string str, size_t width, bool show_ellipsis = true); +}; \ No newline at end of file diff --git a/include/viewer.h b/include/viewer.h new file mode 100644 index 0000000..fa29c91 --- /dev/null +++ b/include/viewer.h @@ -0,0 +1,18 @@ +#pragma once +#include "textengine.h" + +#ifndef VIEWER_H +#define VIEWER_H + +class Viewer { +public: + virtual void Init() = 0; + virtual void Cleanup() = 0; + + virtual bool Update(u32 key) = 0; + virtual void Draw(TextEngine* t) = 0; + + std::string path; +}; + +#endif \ No newline at end of file diff --git a/resources/AppInfo b/resources/AppInfo new file mode 100644 index 0000000..8073197 --- /dev/null +++ b/resources/AppInfo @@ -0,0 +1,5 @@ +APP_TITLE = CtrOrganizer +APP_DESCRIPTION = File manager. +APP_AUTHOR = boo +APP_PRODUCT_CODE = CTR-ORGANIZER +APP_UNIQUE_ID = 0x1815 \ No newline at end of file diff --git a/resources/audio.wav b/resources/audio.wav new file mode 100644 index 0000000..8bdb0e1 Binary files /dev/null and b/resources/audio.wav differ diff --git a/resources/banner.png b/resources/banner.png new file mode 100644 index 0000000..380faa7 Binary files /dev/null and b/resources/banner.png differ diff --git a/resources/icon.png b/resources/icon.png new file mode 100644 index 0000000..5941c8d Binary files /dev/null and b/resources/icon.png differ diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..42309ba --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,326 @@ +#include <3ds.h> +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "browse_entry.h" +#include "vshader_v_shbin.h" +#include "textengine.h" +#include "utility.h" +#include "viewer.h" +#include "textviewer.h" +#include "settingsviewer.h" + +enum FileMode { + SELECT, + DELETE, + COPY, + PASTE +}mode; + +Viewer* currentViewer; + +int currentSelection = 0; +int viewOffset = 0; + +std::vector entries; +std::map selectedEntries; + +std::string currentPath = "/"; + +PrintConsole bottomScreen; +u32 kDown; + +TextEngine* te; +TextEngine* tb; + +void cleanViewer() { + currentViewer->Cleanup(); + delete currentViewer; + currentViewer = NULL; +} + +void initDirectory() { + currentSelection = 0; + viewOffset = 0; + + entries.clear(); + + DIR *dpdf; + struct dirent *epdf; + + dpdf = opendir(currentPath.c_str()); + if (dpdf != NULL){ + while (epdf = readdir(dpdf)){ + BrowseEntry *ent = new BrowseEntry; + ent->name = std::string(epdf->d_name); + + if(epdf->d_type == DT_REG) { + ent->isDirectory = false; + } else { + ent->isDirectory = true; + } + + entries.push_back(ent); + } + } + + //Sort directories first (TODO: sort alphabetically) + std::sort(entries.begin(), entries.end(), [] (BrowseEntry* const& a, BrowseEntry* const& b) { + if(a->isDirectory && !b->isDirectory) { + return true; + } else { + return false; + } + }); + +} + +void OpenDirectory(std::string path, bool isrel) { + if(isrel) { + currentPath += path + "/"; + } + + initDirectory(); +} + +//Sometimes going up a directory will permentantly make the directory contents blank, never happened on printf so it's probably a GPU-related issue +void UpDirectory() { + size_t found; + + currentPath = currentPath.substr(0, currentPath.length() - 1); + found=currentPath.find_last_of("/\\"); + + currentPath = currentPath.substr(0, found + 1); + + initDirectory(); +} + +void ViewFile(std::string filename) { + //Re-implement extension checking when we can support something more than plain-text text files + std::string ext = filename.substr(filename.find_last_of(".") + 1); + + currentViewer = new TextViewer(); + currentViewer->path = currentPath + filename; + currentViewer->Init(); +} + +std::string GetModeString() { + switch(mode) { + case SELECT: + return "SELECT"; + break; + case DELETE: + return "DELETE"; + break; + case COPY: + return "COPY"; + break; + case PASTE: + return "PASTE"; + break; + } +} + +void drawFileBrowser() { + te->renderText(8.0f, 8.0f, 0.5f, 0.5f, false, std::string("Current Path:" + currentPath + " View Offset: " + SSTR(viewOffset) + " Cur: " + SSTR(currentSelection) + " Mode: " + GetModeString()).c_str()); + + for(int i = viewOffset; i < entries.size(); i++) { + if(selectedEntries[(currentPath + entries[i]->name)]) { + te->setTextColor(0x68B0D8FF); + } else { + te->setTextColor(0xFFFFFFFF); + } + + if(i == currentSelection) { + if(entries[i]->isDirectory) { + te->renderText(8.0f, (16.0f * i) - (viewOffset * 16.0f) + 16.0f, 0.5f, 0.5f, false, std::string(">[" + Utility::truncate(entries[i]->name, 65, true) + "]").c_str()); + } else { + te->renderText(8.0f, (16.0f * i) - (viewOffset * 16.0f) + 16.0f, 0.5f, 0.5f, false, std::string(">" + Utility::truncate(entries[i]->name, 65, true)).c_str()); + } + } else { + if(entries[i]->isDirectory) { + te->renderText(8.0f, (16.0f * i) - (viewOffset * 16.0f) + 16.0f, 0.5f, 0.5f, false, std::string("[" + Utility::truncate(entries[i]->name, 65, true) + "]").c_str()); + } else { + te->renderText(8.0f, (16.0f * i) - (viewOffset * 16.0f) + 16.0f, 0.5f, 0.5f, false, std::string(Utility::truncate(entries[i]->name, 65, true)).c_str()); + } + } + } + + if(entries.size() <= 0) { + te->renderText(8.0f, 32.0f, 0.5f, 0.5f, false, "Directory is empty!"); + } +} + +void updateFilebrowser() { + if(kDown & KEY_SELECT) { + currentViewer = new SettingsViewer(); + currentViewer->Init(); + } + + //Find a better way to cycle through these + if(kDown & KEY_L) { + switch(mode) { + case SELECT: + mode = DELETE; + break; + case DELETE: + mode = COPY; + break; + case COPY: + mode = PASTE; + break; + case PASTE: + mode = SELECT; + break; + } + } + + //Mode action + if(kDown & KEY_X) { + switch(mode) { + case SELECT: + { + selectedEntries[(currentPath + entries[currentSelection]->name)] = !selectedEntries[(currentPath + entries[currentSelection]->name)]; + printf("Toggled selection for: %s\n", (currentPath + entries[currentSelection]->name).c_str()); + } + break; + } + } + + if(kDown & KEY_UP) { + //Easy fix until i'm bothered to fix the issue + if(currentSelection < 0 | viewOffset < 0) { + currentSelection = 0; + viewOffset = 0; + } + + if(viewOffset != 0) { + viewOffset -= 1; + currentSelection -= 1; + } + + if(currentSelection != 0) { + currentSelection -= 1; + } + } + + if(kDown & KEY_DOWN) { + //Failsafe to prevent crashing + if(currentSelection > entries.size()) { + currentSelection = 0; + viewOffset = 0; + } + + if(currentSelection == 13 + viewOffset) { + viewOffset += 1; + } + + if(currentSelection != entries.size() - 1) { + currentSelection += 1; + } + } + + if(kDown & KEY_A) { + if(!entries[currentSelection]->isDirectory) { + ViewFile(entries[currentSelection]->name); + } else { + OpenDirectory(entries[currentSelection]->name, true); + } + } + + if(kDown & KEY_B) { + if(currentPath != "./") + UpDirectory(); + } +} + +void update() { + if(currentViewer == NULL) { + updateFilebrowser(); + } else { + if(!currentViewer->Update(kDown)) { + cleanViewer(); + return; + } + } +} + +void drawTopScreen() { + if(currentViewer == NULL) { + drawFileBrowser(); + } else { + currentViewer->Draw(te); + } +} + +void drawBottomScreen() { + if(currentViewer == NULL) { + tb->renderText(8.0f, 8.0f, 0.5f, 0.5f, false, std::string("A - View file").c_str()); + tb->renderText(8.0f, 16.0f, 0.5f, 0.5f, false, std::string("X - Mode action").c_str()); + tb->renderText(8.0f, 32.0f, 0.5f, 0.5f, false, std::string("L - Switch mode").c_str()); + tb->renderText(8.0f, 50.0f, 0.5f, 0.5f, false, std::string("Start - Exit").c_str()); + } +} + +void Cleanup() { + te->Cleanup(); + + entries.clear(); + + if(currentViewer != NULL) { + cleanViewer(); + } + + C3D_Fini(); + gfxExit(); +} + +int main(int argc, char **argv) +{ + gfxInitDefault(); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + + te = new TextEngine(); + te->Initialize(GFX_TOP); + + tb = new TextEngine(); + tb->Initialize(GFX_BOTTOM); + + //TODO: Thanks to TheCruel (xerpi) for the solution to the GPU freezing issue on cia builds + //aptHook(&apt_hook_cookie, apt_hook_func, NULL); + + initDirectory(); + + while (aptMainLoop()) + { + hidScanInput(); + kDown = hidKeysDown(); + + update(); + + if (kDown & KEY_START) + break; + + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + te->Update(); + C3D_FrameDrawOn(te->target); + drawTopScreen(); + + tb->Update(); + C3D_FrameDrawOn(tb->target); + drawBottomScreen(); + C3D_FrameEnd(0); + } + + Cleanup(); + return 0; +} diff --git a/source/settingsviewer.cpp b/source/settingsviewer.cpp new file mode 100644 index 0000000..6db0800 --- /dev/null +++ b/source/settingsviewer.cpp @@ -0,0 +1,21 @@ +#include "settingsviewer.h" + +void SettingsViewer::Draw(TextEngine* t) { + t->renderText(8.0f, 8.0f, 0.5f, 0.5f, false, std::string("Options").c_str()); + t->renderText(8.0f, 18.0f, 0.5f, 0.5f, false, std::string("There is nothing here...").c_str()); +} + +bool SettingsViewer::Update(u32 key) { + //Exit viewer + if(key & KEY_B) { + return false; + } +} + +void SettingsViewer::Init() { + +} + +void SettingsViewer::Cleanup() { + +} diff --git a/source/textengine.cpp b/source/textengine.cpp new file mode 100644 index 0000000..ace545e --- /dev/null +++ b/source/textengine.cpp @@ -0,0 +1,156 @@ +#include "textengine.h" + +/* +* Font rendering used from https://github.com/devkitPro/3ds-examples +*/ + +void TextEngine::addTextVertex(float vx, float vy, float tx, float ty) +{ + textVertex_s* vtx = &textVtxArray[textVtxArrayPos++]; + vtx->position[0] = vx; + vtx->position[1] = vy; + vtx->position[2] = 0.5f; + vtx->texcoord[0] = tx; + vtx->texcoord[1] = ty; +} + +void TextEngine::setTextColor(u32 color) +{ + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvSrc(env, C3D_RGB, GPU_CONSTANT, 0, 0); + C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_CONSTANT, 0); + C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE); + C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE); + C3D_TexEnvColor(env, color); +} + +void TextEngine::renderText(float x, float y, float scaleX, float scaleY, bool baseline, const char* text) +{ + ssize_t units; + uint32_t code; + + // Configure buffers + C3D_BufInfo* bufInfo = C3D_GetBufInfo(); + BufInfo_Init(bufInfo); + BufInfo_Add(bufInfo, textVtxArray, sizeof(textVertex_s), 2, 0x10); + + const uint8_t* p = (const uint8_t*)text; + float firstX = x; + u32 flags = GLYPH_POS_CALC_VTXCOORD | (baseline ? GLYPH_POS_AT_BASELINE : 0); + int lastSheet = -1; + do + { + if (!*p) break; + units = decode_utf8(&code, p); + if (units == -1) + break; + p += units; + if (code == '\n') + { + x = firstX; + y += scaleY*fontGetInfo()->lineFeed; + } + else if (code > 0) + { + int glyphIdx = fontGlyphIndexFromCodePoint(code); + fontGlyphPos_s data; + fontCalcGlyphPos(&data, glyphIdx, flags, scaleX, scaleY); + + // Bind the correct texture sheet + if (data.sheetIndex != lastSheet) + { + lastSheet = data.sheetIndex; + C3D_TexBind(0, &glyphSheets[lastSheet]); + } + + int arrayIndex = textVtxArrayPos; + if ((arrayIndex+4) >= TEXT_VTX_ARRAY_COUNT) + break; // We can't render more characters + + // Add the vertices to the array + addTextVertex(x+data.vtxcoord.left, y+data.vtxcoord.bottom, data.texcoord.left, data.texcoord.bottom); + addTextVertex(x+data.vtxcoord.right, y+data.vtxcoord.bottom, data.texcoord.right, data.texcoord.bottom); + addTextVertex(x+data.vtxcoord.left, y+data.vtxcoord.top, data.texcoord.left, data.texcoord.top); + addTextVertex(x+data.vtxcoord.right, y+data.vtxcoord.top, data.texcoord.right, data.texcoord.top); + + // Draw the glyph + C3D_DrawArrays(GPU_TRIANGLE_STRIP, arrayIndex, 4); + + x += data.xAdvance; + + } + } while (code > 0); +} + +void TextEngine::Initialize(gfxScreen_t scn) { + //The two screens are different sizes + if(scn == GFX_TOP) { + target = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + } else { + target = C3D_RenderTargetCreate(240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + } + C3D_RenderTargetSetClear(target, C3D_CLEAR_ALL, CLEAR_COLOR, 0); + C3D_RenderTargetSetOutput(target, scn, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + + Result res = fontEnsureMapped(); + + if (R_FAILED(res)) + printf("fontEnsureMapped: %08lX\n", res); + + vshader_dvlb = DVLB_ParseFile((u32*)vshader_v_shbin, vshader_v_shbin_size); + shaderProgramInit(&program); + shaderProgramSetVsh(&program, &vshader_dvlb->DVLE[0]); + C3D_BindProgram(&program); + + // Get the location of the uniforms + uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection"); + + // Configure attributes for use with the vertex shader + C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); + AttrInfo_Init(attrInfo); + AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position + AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord + + if(scn == GFX_TOP) { + // Compute the projection matrix + Mtx_OrthoTilt(&projection, 0.0, 400.0, 240.0, 0.0, 0.0, 1.0); + } else { + Mtx_OrthoTilt(&projection, 0.0, 320.0, 240.0, 0.0, 0.0, 1.0); + } + + // Configure depth test to overwrite pixels with the same depth (needed to draw overlapping glyphs) + C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL); + + // Load the glyph texture sheets + int i; + TGLP_s* glyphInfo = fontGetGlyphInfo(); + glyphSheets = malloc(sizeof(C3D_Tex)*glyphInfo->nSheets); + for (i = 0; i < glyphInfo->nSheets; i ++) + { + C3D_Tex* tex = &glyphSheets[i]; + tex->data = fontGetGlyphSheetTex(i); + tex->fmt = glyphInfo->sheetFmt; + tex->size = glyphInfo->sheetSize; + tex->width = glyphInfo->sheetWidth; + tex->height = glyphInfo->sheetHeight; + tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) + | GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE); + } + + // Create the text vertex array + textVtxArray = (textVertex_s*)linearAlloc(sizeof(textVertex_s)*TEXT_VTX_ARRAY_COUNT); +} + +void TextEngine::Update() { + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + textVtxArrayPos = 0; +} + +void TextEngine::Cleanup() { + free(glyphSheets); + + // Free the shader program + shaderProgramFree(&program); + DVLB_Free(vshader_dvlb); +} \ No newline at end of file diff --git a/source/textviewer.cpp b/source/textviewer.cpp new file mode 100644 index 0000000..c06d2fd --- /dev/null +++ b/source/textviewer.cpp @@ -0,0 +1,64 @@ +#include "textviewer.h" + +void TextViewer::Draw(TextEngine* t) { + t->renderText(8.0f, 8.0f, 0.5f, 0.5f, false, std::string("Current File:" + path).c_str()); + + for(int i = textoffset; i < lineEntries.size(); i++) { + if(i == currentindex) { + t->renderText(8.0f, (16.0f * i) - (textoffset * 16.0f) + 16.0f, 0.5f, 0.5f, false, std::string(">" + lineEntries[i]).c_str()); + } else { + t->renderText(8.0f, (16.0f * i) - (textoffset * 16.0f) + 16.0f, 0.5f, 0.5f, false, lineEntries[i].c_str()); + } + } + + if(lineEntries.size() <= 0) { + t->renderText(8.0f, 32.0f, 0.5f, 0.5f, false, "File is empty!"); + } +} + +bool TextViewer::Update(u32 key) { + //Exit viewer + if(key & KEY_B) { + return false; + } + + if(key & KEY_UP) { + //Easy fix until i'm bothered to fix the issue + if(currentindex < 0 | textoffset < 0) { + currentindex = 0; + textoffset = 0; + } + + if(textoffset != 0) { + textoffset -= 1; + currentindex -= 1; + } + + if(currentindex != 0) { + currentindex -= 1; + } + } + + if(key & KEY_DOWN) { + if(currentindex == 13 + textoffset) { + textoffset += 1; + } + + if(currentindex != lineEntries.size() - 1) { + currentindex += 1; + } + } + return true; +} + +void TextViewer::Init() { + std::ifstream input( path ); + for( std::string line; getline( input, line ); ) + { + lineEntries.push_back(line); + } +} + +void TextViewer::Cleanup() { + lineEntries.clear(); +} \ No newline at end of file diff --git a/source/utility.cpp b/source/utility.cpp new file mode 100644 index 0000000..0fb704c --- /dev/null +++ b/source/utility.cpp @@ -0,0 +1,11 @@ +#include "utility.h" + +std::string Utility::truncate(std::string str, size_t width, bool show_ellipsis) +{ + if (str.length() > width) + if (show_ellipsis) + return str.substr(0, width) + "..."; + else + return str.substr(0, width); + return str; +} \ No newline at end of file diff --git a/source/vshader.v.pica b/source/vshader.v.pica new file mode 100644 index 0000000..7986f11 --- /dev/null +++ b/source/vshader.v.pica @@ -0,0 +1,42 @@ +; Example PICA200 vertex shader + +; Uniforms +.fvec projection[4] + +; Constants +.constf myconst(0.0, 1.0, -1.0, 0.1) +.constf myconst2(0.3, 0.0, 0.0, 0.0) +.alias zeros myconst.xxxx ; Vector full of zeros +.alias ones myconst.yyyy ; Vector full of ones + +; Outputs +.out outpos position +.out outclr color +.out outtc0 texcoord0 + +; Inputs (defined as aliases for convenience) +.alias inpos v0 +.alias intex v1 + +.bool test + +.proc main + ; Force the w component of inpos to be 1.0 + mov r0.xyz, inpos + mov r0.w, ones + + ; outpos = projectionMatrix * inpos + dp4 outpos.x, projection[0], r0 + dp4 outpos.y, projection[1], r0 + dp4 outpos.z, projection[2], r0 + dp4 outpos.w, projection[3], r0 + + ; outclr = white + mov outclr, ones + + ; outtc0 = intex + mov outtc0, intex + + ; We're finished + end +.end diff --git a/tools/7za.dll b/tools/7za.dll new file mode 100644 index 0000000..46f6e8e Binary files /dev/null and b/tools/7za.dll differ diff --git a/tools/7za.exe b/tools/7za.exe new file mode 100644 index 0000000..710bda2 Binary files /dev/null and b/tools/7za.exe differ diff --git a/tools/7zxa.dll b/tools/7zxa.dll new file mode 100644 index 0000000..afa96d4 Binary files /dev/null and b/tools/7zxa.dll differ diff --git a/tools/bannertool-linux b/tools/bannertool-linux new file mode 100755 index 0000000..9006089 Binary files /dev/null and b/tools/bannertool-linux differ diff --git a/tools/bannertool-mac b/tools/bannertool-mac new file mode 100644 index 0000000..407bd90 Binary files /dev/null and b/tools/bannertool-mac differ diff --git a/tools/bannertool.exe b/tools/bannertool.exe new file mode 100644 index 0000000..1c61d24 Binary files /dev/null and b/tools/bannertool.exe differ diff --git a/tools/makerom-linux b/tools/makerom-linux new file mode 100755 index 0000000..db8a2bb Binary files /dev/null and b/tools/makerom-linux differ diff --git a/tools/makerom-mac b/tools/makerom-mac new file mode 100644 index 0000000..1312331 Binary files /dev/null and b/tools/makerom-mac differ diff --git a/tools/makerom.exe b/tools/makerom.exe new file mode 100644 index 0000000..a1ab919 Binary files /dev/null and b/tools/makerom.exe differ diff --git a/tools/template.rsf b/tools/template.rsf new file mode 100644 index 0000000..aad12d6 --- /dev/null +++ b/tools/template.rsf @@ -0,0 +1,219 @@ +BasicInfo: + Title : $(APP_TITLE) + ProductCode : $(APP_PRODUCT_CODE) + Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem + +#RomFs: + # Specifies the root path of the read only file system to include in the ROM. + #RootPath : $(APP_ROMFS) + +TitleInfo: + Category : Application + UniqueId : $(APP_UNIQUE_ID) + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : $(APP_ENCRYPTED) # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses where applicable (currently only exefs:/.code) + +AccessControlInfo: + CoreVersion : 2 + + # Exheader Format Version + DescVersion : 2 + + # Minimum Required Kernel Version (below is for 4.5.0) + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + + # ExtData + UseExtSaveData : false # enables ExtData + #ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId + + # FS:USER Archive Access Permissions + # Uncomment as required + FileSystemAccess: + #- CategorySystemApplication + #- CategoryHardwareCheck + - CategoryFileSystemTool + #- Debug + #- TwlCardBackup + #- TwlNandData + #- Boss + - DirectSdmc + #- Core + #- CtrNandRo + #- CtrNandRw + #- CtrNandRoWrite + #- CategorySystemSettings + #- CardBoard + #- ExportImportIvs + #- DirectSdmcWrite + #- SwitchCleanup + #- SaveDataMove + #- Shop + #- Shell + #- CategoryHomeMenu + + # Process Settings + MemoryType : Application # Application/System/Base + SystemMode : $(APP_SYSTEM_MODE) # 64MB(Default)/96MB/80MB/72MB/32MB + IdealProcessor : 0 + AffinityMask : 1 + Priority : 16 + MaxCpu : 0x9E # Default + HandleTableSize : 0x200 + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : true + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : true + PermitMainFunctionArgument : true + CanShareDeviceMemory : true + RunnableOnSleep : false + SpecialMemoryArrange : true + + # New3DS Exclusive Process Settings + SystemModeExt : $(APP_SYSTEM_MODE_EXT) # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 256MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true + + # Virtual Address Mappings + IORegisterMapping: + - 1ff00000-1ff7ffff # DSP memory + MemoryMapping: + - 1f000000-1f5fffff:r # VRAM + + # Accessible SVCs, : + SystemCallAccess: + ArbitrateAddress: 34 + Backdoor: 123 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + ControlProcessMemory: 112 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreateSemaphore: 21 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetProcessIdealProcessor: 6 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + + # Service List + # Maximum 34 services (32 if firmware is prior to 9.6.0) + ServiceAccessControl: + - APT:U + - ac:u + - am:net + - boss:U + - cam:u + - cecd:u + - cfg:nor + - cfg:u + - csnd:SND + - dsp::DSP + - frd:u + - fs:USER + - gsp::Gpu + - hid:USER + - http:C + - ir:rst + - ir:u + - ir:USER + - mic:u + - ndm:u + - news:u + - nwm::UDS + - ptm:u + - pxi:dev + - soc:U + - ssl:C + - y2r:u + + +SystemControlInfo: + SaveDataSize: 0KB # Change if the app uses savedata + RemasterVersion: 2 + StackSize: 0x40000 + + # Modules that run services listed above should be included below + # Maximum 48 dependencies + # : + Dependency: + ac: 0x0004013000002402 + act: 0x0004013000003802 + am: 0x0004013000001502 + boss: 0x0004013000003402 + camera: 0x0004013000001602 + cecd: 0x0004013000002602 + cfg: 0x0004013000001702 + codec: 0x0004013000001802 + csnd: 0x0004013000002702 + dlp: 0x0004013000002802 + dsp: 0x0004013000001a02 + friends: 0x0004013000003202 + gpio: 0x0004013000001b02 + gsp: 0x0004013000001c02 + hid: 0x0004013000001d02 + http: 0x0004013000002902 + i2c: 0x0004013000001e02 + ir: 0x0004013000003302 + mcu: 0x0004013000001f02 + mic: 0x0004013000002002 + ndm: 0x0004013000002b02 + news: 0x0004013000003502 + nfc: 0x0004013000004002 + nim: 0x0004013000002c02 + nwm: 0x0004013000002d02 + pdn: 0x0004013000002102 + ps: 0x0004013000003102 + ptm: 0x0004013000002202 + qtm: 0x0004013020004202 + ro: 0x0004013000003702 + socket: 0x0004013000002e02 + spi: 0x0004013000002302 + ssl: 0x0004013000002f02