1
Fork 0

Add old files

This commit is contained in:
Joshua Goins 2024-01-03 15:59:26 -05:00
commit 5d8ba66682
30 changed files with 1281 additions and 0 deletions

39
.gitignore vendored Normal file
View file

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

19
LICENSE Normal file
View file

@ -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.

226
Makefile Normal file
View file

@ -0,0 +1,226 @@
.SUFFIXES:
#---------------------------------------------------------------------------------
# Environment Setup
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPRO")
endif
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
#---------------------------------------------------------------------------------

34
README.md Normal file
View file

@ -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).

7
include/browse_entry.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
class BrowseEntry {
public:
std::string name;
bool isDirectory = false;
};

14
include/settingsviewer.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include "viewer.h"
#include "textengine.h"
#include <vector>
#include <string>
class SettingsViewer : public Viewer {
public:
void Init();
void Cleanup();
bool Update(u32 key);
void Draw(TextEngine* t);
};

49
include/textengine.h Normal file
View file

@ -0,0 +1,49 @@
#pragma once
#include <3ds.h>
#include <stdio.h>
#include <dirent.h>
#include <vector>
#include <string>
#include <browse_entry.h>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <citro3d.h>
#include "vshader_v_shbin.h"
#include <sstream>
#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;
};

20
include/textviewer.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#include "viewer.h"
#include "textengine.h"
#include <vector>
#include <string>
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<std::string> lineEntries;
};

11
include/utility.h Normal file
View file

@ -0,0 +1,11 @@
#pragma once
#include <string>
//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);
};

18
include/viewer.h Normal file
View file

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

5
resources/AppInfo Normal file
View file

@ -0,0 +1,5 @@
APP_TITLE = CtrOrganizer
APP_DESCRIPTION = File manager.
APP_AUTHOR = boo
APP_PRODUCT_CODE = CTR-ORGANIZER
APP_UNIQUE_ID = 0x1815

BIN
resources/audio.wav Normal file

Binary file not shown.

BIN
resources/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

326
source/main.cpp Normal file
View file

@ -0,0 +1,326 @@
#include <3ds.h>
#include <stdio.h>
#include <dirent.h>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <citro3d.h>
#include <sstream>
#include <map>
#include <iostream>
#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<BrowseEntry*> entries;
std::map<std::string,bool> 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;
}

21
source/settingsviewer.cpp Normal file
View file

@ -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() {
}

156
source/textengine.cpp Normal file
View file

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

64
source/textviewer.cpp Normal file
View file

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

11
source/utility.cpp Normal file
View file

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

42
source/vshader.v.pica Normal file
View file

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

BIN
tools/7za.dll Normal file

Binary file not shown.

BIN
tools/7za.exe Normal file

Binary file not shown.

BIN
tools/7zxa.dll Normal file

Binary file not shown.

BIN
tools/bannertool-linux Executable file

Binary file not shown.

BIN
tools/bannertool-mac Normal file

Binary file not shown.

BIN
tools/bannertool.exe Normal file

Binary file not shown.

BIN
tools/makerom-linux Executable file

Binary file not shown.

BIN
tools/makerom-mac Normal file

Binary file not shown.

BIN
tools/makerom.exe Normal file

Binary file not shown.

219
tools/template.rsf Normal file
View file

@ -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, <Name>:<ID>
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
# <module name>:<module titleid>
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