From 3062716f8cff861729e0e1fb83def617aa7ad83e Mon Sep 17 00:00:00 2001 From: Kazuki Date: Sat, 11 Jul 2020 17:34:32 +0900 Subject: [PATCH] first commit --- .gitignore | 5 + Makefile | 258 ++++++++++++++++++++++++++++++++++++++ README.md | 14 +++ gfx/allow.png | Bin 0 -> 167 bytes gfx/folder.png | Bin 0 -> 277 bytes gfx/mii.png | Bin 0 -> 453 bytes gfx/sprites.t3s | 6 + gfx/top.png | Bin 0 -> 21554 bytes gfx/unknown.png | Bin 0 -> 357 bytes source/crc16.cpp | 14 +++ source/extdata.cpp | 53 ++++++++ source/extdata.hpp | 18 +++ source/filemanager.cpp | 63 ++++++++++ source/filemanager.hpp | 23 ++++ source/main.cpp | 197 +++++++++++++++++++++++++++++ source/mii.cpp | 275 +++++++++++++++++++++++++++++++++++++++++ source/mii.hpp | 40 ++++++ 17 files changed, 966 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 gfx/allow.png create mode 100644 gfx/folder.png create mode 100644 gfx/mii.png create mode 100644 gfx/sprites.t3s create mode 100644 gfx/top.png create mode 100644 gfx/unknown.png create mode 100644 source/crc16.cpp create mode 100644 source/extdata.cpp create mode 100644 source/extdata.hpp create mode 100644 source/filemanager.cpp create mode 100644 source/filemanager.hpp create mode 100644 source/main.cpp create mode 100644 source/mii.cpp create mode 100644 source/mii.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4d57e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/ +romfs/ +*.3dsx +*.elf +*.smdh \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3789b9e --- /dev/null +++ b/Makefile @@ -0,0 +1,258 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# GRAPHICS is a list of directories containing graphics files +# GFXBUILD is the directory where converted graphics files will be placed +# If set to $(BUILD), it will statically link in the converted +# files as if they were data files. +# +# NO_SMDH: if set to anything, no SMDH file is generated. +# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) +# APP_TITLE is the name of the app stored in the SMDH file (Optional) +# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) +# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) +# ICON is the filename of the icon (.png), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .png +# - icon.png +# - /default_icon.png +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include +GRAPHICS := gfx +#GFXBUILD := $(BUILD) +ROMFS := romfs +GFXBUILD := $(ROMFS)/gfx +APP_TITLE := Mii Manager +APP_AUTHOR := Kazuki +APP_DESCRIPTION := Dump your Miis as .3dsmii and import .3dsmii files + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft + +CFLAGS := -g -Wall -O2 -mword-relocations \ + -fomit-frame-pointer -ffunction-sections \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lcitro2d -lcitro3d -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) +SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) +GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +ifeq ($(GFXBUILD),$(BUILD)) +#--------------------------------------------------------------------------------- +export T3XFILES := $(GFXFILES:.t3s=.t3x) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- +export ROMFS_T3XFILES := $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES)) +export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ + $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ + $(addsuffix .o,$(T3XFILES)) + +export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) + +export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ + $(addsuffix .h,$(subst .,_,$(BINFILES))) \ + $(GFXFILES:.t3s=.h) + +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) + +export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.png) + ifneq (,$(findstring $(TARGET).png,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).png + else + ifneq (,$(findstring icon.png,$(icons))) + export APP_ICON := $(TOPDIR)/icon.png + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_SMDH)),) + export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh +endif + +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) +endif + +.PHONY: all clean + +#--------------------------------------------------------------------------------- +all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +$(BUILD): + @mkdir -p $@ + +ifneq ($(GFXBUILD),$(BUILD)) +$(GFXBUILD): + @mkdir -p $@ +endif + +ifneq ($(DEPSDIR),$(BUILD)) +$(DEPSDIR): + @mkdir -p $@ +endif + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(GFXBUILD) + +#--------------------------------------------------------------------------------- +$(GFXBUILD)/%.t3x $(BUILD)/%.h : %.t3s +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x + +#--------------------------------------------------------------------------------- +else + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS) + +$(OFILES_SOURCES) : $(HFILES) + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +.PRECIOUS : %.t3x +#--------------------------------------------------------------------------------- +%.t3x.o %_t3x.h : %.t3x +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +# rules for assembling GPU shaders +#--------------------------------------------------------------------------------- +define shader-as + $(eval CURBIN := $*.shbin) + $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) + echo "$(CURBIN).o: $< $1" > $(DEPSFILE) + 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 + picasso -o $(CURBIN) $1 + bin2s $(CURBIN) | $(AS) -o $*.shbin.o +endef + +%.shbin.o %_shbin.h : %.v.pica %.g.pica + @echo $(notdir $^) + @$(call shader-as,$^) + +%.shbin.o %_shbin.h : %.v.pica + @echo $(notdir $<) + @$(call shader-as,$<) + +%.shbin.o %_shbin.h : %.shlist + @echo $(notdir $<) + @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) + +#--------------------------------------------------------------------------------- +%.t3x %.h : %.t3s +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @tex3ds -i $< -H $*.h -d $*.d -o $*.t3x + +-include $(DEPSDIR)/*.d + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..45ff26f --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# Mii Manager +![SnapShot](https://cdn.discordapp.com/attachments/671391178280665118/731422743290970144/screenshot.png) + +3DSからMiiを.3dsmiiファイルとしてダンプしたり、.3dsmiiファイルをインポートしたりできるツールです。 +# 使い方 +A:フォルダーを開く/Miiのインポート + +B:1つ上のフォルダーに戻る + +X:Miiを「自分のMii」としてインポート(この機能は正常に動きません) + +Y:Miiを現在のフォルダーにダンプ + +START:HBLに戻る \ No newline at end of file diff --git a/gfx/allow.png b/gfx/allow.png new file mode 100644 index 0000000000000000000000000000000000000000..b4613d90b91a3c7ba5872298329ab22dd00d0503 GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^{2?!pUk~X*1Z8cn_|UOyLk_Zgwzq?}c7PGI!f zl=ftXLX`2g7s@LnXkl&- z7#O~yD_~?{VQIFt)~PWw*1+nzi*JbV7;I2R}TePjiv&fa2RWMgA#4i2!Z@p3c6 z7d9Aj$(cP2IQ>k>BuSO9QO$$`30a=<0ApZaXy)bRszFcNgk16R)q4g|#$aV&U|=Dn b7@Ir*LBmspuZ%$F00000NkvXXu0mjfV7+;1 literal 0 HcmV?d00001 diff --git a/gfx/mii.png b/gfx/mii.png new file mode 100644 index 0000000000000000000000000000000000000000..7fa481f66979429b56a902e52e3548a0a6182aef GIT binary patch literal 453 zcmV;$0XqJPP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0a{5!K~y+TeUm*) z!$26v|GBgl8!0XYY0<&r%jO~|IJgN)K`1yn=?8Ff5*)?B58ww7)J@z(T@>6)5Roc1 z)GCNCSkg2vmv~7|V#vkc4fpVW_}}v!&z-cqwe1xY1=OYZMPjQzmxCk$wnJe#HqV5} zM8>zS1FJi`o`}QqOYh+9!n@ZruEgR2a zHHpyNe0>SpU8^qQrhd!SBtkQh2rTLZaV8@Ny9l8ZKGuvp#L!i(mzxzVqbgh@qMYksxO-jVoE8TKRKW9SLyfx_UWTjn;b3`00000NkvXXu0mjfY&ya% literal 0 HcmV?d00001 diff --git a/gfx/sprites.t3s b/gfx/sprites.t3s new file mode 100644 index 0000000..3e637c9 --- /dev/null +++ b/gfx/sprites.t3s @@ -0,0 +1,6 @@ +--atlas -f rgba8888 -z auto +top.png +allow.png +folder.png +mii.png +unknown.png \ No newline at end of file diff --git a/gfx/top.png b/gfx/top.png new file mode 100644 index 0000000000000000000000000000000000000000..0162e0535d5a5b571c3ec0641dedc251d52cbba6 GIT binary patch literal 21554 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 literal 0 HcmV?d00001 diff --git a/gfx/unknown.png b/gfx/unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..612b1a66cafdcd70c8bbdcde3d4ed5754db395a3 GIT binary patch literal 357 zcmV-r0h<1aP))kNaP(1@JmRq@U0maNs3fAlOKf=x92HSRr-ODo=h5^s>eWFR5)B6H-^MG6T1}w`$Rn_S*nM{%d zj^kjlSU?oTc(^84iM=hb2im9!}E~wr!&*ici>eUDS0w?W?jZ139Et zs}#&0mkaiykdBR#_~ zBs!i~RdoSi`_= 0; bitIndex--) { + crc = (((crc << 1) | ((buf[byteIndex] >> bitIndex) & 0x1)) ^ + (((crc & 0x8000) != 0) ? 0x1021 : 0)); + } + } + for (counter = 16; counter > 0; counter--) { + crc = ((crc << 1) ^ (((crc & 0x8000) != 0) ? 0x1021 : 0)); + } + return (unsigned short)(crc & 0xFFFF); +} \ No newline at end of file diff --git a/source/extdata.cpp b/source/extdata.cpp new file mode 100644 index 0000000..e7cbef9 --- /dev/null +++ b/source/extdata.cpp @@ -0,0 +1,53 @@ +#include "extdata.hpp" + +Archive::Archive(unsigned int id){ + FS_ArchiveID dataType; + uint32_t path[3]; + path[1] = id; + if(id < 0x80000000){ + path[0] = MEDIATYPE_SD; + path[2] = 0; + dataType = ARCHIVE_EXTDATA; + }else{ + path[0] = MEDIATYPE_NAND; + path[2] = 0x00048000; + dataType = ARCHIVE_SHARED_EXTDATA; + } + FS_Path binPath = {PATH_BINARY,0xC,path}; + Result ret = FSUSER_OpenArchive(&extdata_archive,dataType, binPath); + if(ret){ + valid = false; + }else{ + valid = true; + } +} + +Result Archive::Open(Handle*filehandle,char*path,u32 openFlag){ + return FSUSER_OpenFile(filehandle, extdata_archive, fsMakePath(PATH_ASCII, path),openFlag, 0); +} + +unsigned long long Archive::GetFileSize(Handle filehandle){ + unsigned long long size = 0; + if(FSFILE_GetSize(filehandle,&size)){ + return 0; + } + return size; +} + +Result Archive::Read(Handle filehandle,u32*readsize,u64 offset,void*buf,u32 size){ + return FSFILE_Read(filehandle,readsize,offset,buf,size); +} + +Result Archive::Write(Handle filehandle,u32*writesize,u64 offset,void*buf,u32 size){ + return FSFILE_Write(filehandle,writesize,offset,buf,size,FS_WRITE_FLUSH); +} + +Result Archive::Close(Handle filehandle){ + return FSFILE_Close(filehandle); +} + +Archive::~Archive(){ + if(valid){ + FSUSER_CloseArchive(extdata_archive); + } +} \ No newline at end of file diff --git a/source/extdata.hpp b/source/extdata.hpp new file mode 100644 index 0000000..f932bf1 --- /dev/null +++ b/source/extdata.hpp @@ -0,0 +1,18 @@ +#ifndef _EXTDATA_HPP_ +#define _EXTDATA_HPP_ +#include <3ds.h> + +class Archive{ + FS_Archive extdata_archive; + public: + bool valid; + Archive(unsigned int); + Result Open(Handle*,char*,u32); + unsigned long long GetFileSize(Handle filehandle); + Result Read(Handle,u32*,u64,void*,u32); + Result Write(Handle,u32*,u64,void*,u32); + Result Close(Handle); + ~Archive(); +}; + +#endif //_EXTDATA_HPP_ \ No newline at end of file diff --git a/source/filemanager.cpp b/source/filemanager.cpp new file mode 100644 index 0000000..1c53388 --- /dev/null +++ b/source/filemanager.cpp @@ -0,0 +1,63 @@ +#include "filemanager.hpp" + +int FileManager::OpenPath(string s){ + struct dirent *d; + DIR *pdir = opendir(s.c_str()); + char *tmpName; + entry tmpEntry; + if(!pdir)return -1; + curPath = s; + entries.clear(); + entries.shrink_to_fit(); + while(1){ + d = readdir(pdir); + if(!d)break; + tmpName = d->d_name; + if(!(strcmp(tmpName,".") && strcmp(tmpName,"..")))continue; + tmpEntry.name = string(tmpName); + if(d->d_type == DT_DIR){ + tmpEntry.isDir = true; + }else{ + tmpEntry.isDir = false; + } + entries.push_back(tmpEntry); + } + closedir(pdir); + return 0; +} + +FileManager::FileManager(string s){ + if(!OpenPath(s)){ + valid = true; + }else{ + valid = false; + } +} + +int FileManager::Reload(){ + return OpenPath(curPath); +} + +int FileManager::Back(){ + int rootIndex = 0; + int index = curPath.size() - 1; + if(curPath[index] == '/')index--; + while(curPath[index] != '/'){ + if(index < 1)return -1; + index--; + } + while(curPath[rootIndex] != '/'){ + rootIndex++; + } + if(rootIndex == index){ + return OpenPath(curPath.erase(index + 1)); + } + return OpenPath(curPath.erase(index)); +} + +string FileManager::getFullPath(int i){ + if(curPath[curPath.size() - 1] == '/'){ + return curPath + (entries[i]).name; + } + return curPath + "/" + (entries[i]).name; +} \ No newline at end of file diff --git a/source/filemanager.hpp b/source/filemanager.hpp new file mode 100644 index 0000000..689b66f --- /dev/null +++ b/source/filemanager.hpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +using namespace std; + +typedef struct { + string name; + bool isDir; +} entry; + +class FileManager{ + public: + bool valid; + string curPath; + vector entries; + FileManager(string); + int OpenPath(string); + int Reload(void); + int Back(void); + string getFullPath(int); +}; \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..0b5bf81 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,197 @@ +#include +#include +#include +#include <3ds.h> +#include +#include "filemanager.hpp" +#include "mii.hpp" + +#define MAX_SHOW_FILE_NUM 10 + +using namespace std; + +C2D_TextBuf g_dynamicBuf,g_footer; +u8 *top_fb; +static C2D_SpriteSheet spriteSheet; +bool initErr = false; +char doneMsg[128]; +int miiNum; + +bool is3dsmiiFile(string s){ + if(s.size() < 7)return false; + string tmpString = s.substr(s.size() - 7); + if(tmpString == ".3dsmii" || tmpString == ".3DSMII"){ + return true; + } + return false; +} + +static void sceneInit(void) +{ + g_dynamicBuf = C2D_TextBufNew(4096); + g_footer = C2D_TextBufNew(4096); +} + +static void sceneExit(void) +{ + // Delete the text buffers + C2D_TextBufDelete(g_dynamicBuf); + C2D_TextBufDelete(g_footer); +} + +int main() +{ + char *msg,*defaultFooterMsg = " Install Mii /  Install as personal Mii\n Dump Mii / START Exit"; + string defaultDir = "sdmc:/3DSMIIs"; + int index = 0,page,i,j,stat; + romfsInit(); + C2D_Text entriesString[MAX_SHOW_FILE_NUM]; + C2D_Text curPathString,footer; + char doneMsg[128]; + memset(doneMsg,0,128); + C2D_Sprite topSprite; + C2D_Sprite allowSprite; + C2D_Sprite entrySprites[MAX_SHOW_FILE_NUM][3]; + DIR*pdir = opendir(defaultDir.c_str()); + if(pdir){ + closedir(pdir); + }else{ + mkdir(defaultDir.c_str(),0777); + } + FileManager fm(defaultDir); + if(!fm.valid){ + initErr = true; + msg = "Error cannot open sdmc:/3DSMIIs"; + memcpy(doneMsg,msg,strlen(msg)); + goto error; + } + error: + if(!initErr){ + memcpy(doneMsg,defaultFooterMsg,strlen(defaultFooterMsg)); + } + + // Initialize the libs + gfxInitDefault(); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + C2D_Init(C2D_DEFAULT_MAX_OBJECTS); + C2D_Prepare(); + + // Create screen + C3D_RenderTarget* bottom = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT); + C3D_RenderTarget* top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); + // Load graphics + spriteSheet = C2D_SpriteSheetLoad("romfs:/gfx/sprites.t3x"); + if (!spriteSheet) svcBreak(USERBREAK_PANIC); + C2D_SpriteFromSheet(&topSprite, spriteSheet,0); + C2D_SpriteFromSheet(&allowSprite, spriteSheet,1); + allowSprite.params.pos.x = 8.0f; + for(i = 0;i < MAX_SHOW_FILE_NUM;i++){ + for(j = 0;j < 3;j++){ + C2D_SpriteFromSheet(&entrySprites[i][j], spriteSheet,2 + j); + (entrySprites[i][j]).params.pos.y = (float)(i * 15 + 38); + (entrySprites[i][j]).params.pos.x = 23.0f; + } + } + // Initialize the scene + sceneInit(); + + // Main loop + while (aptMainLoop()) + { + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(bottom, C2D_Color32(0x68, 0xB0, 0xD8, 0xFF)); + C2D_SceneBegin(bottom); + hidScanInput(); + // Respond to user input + u32 kDown = hidKeysDown(); + if (kDown & KEY_START) + break; // break in order to return to hbmenu + if(!initErr){ + if(kDown & KEY_A){ + if(fm.entries[index].isDir){ + if(!fm.OpenPath(fm.getFullPath(index)))index = 0; + }else if(is3dsmiiFile(fm.entries[index].name)){ + memset(doneMsg,0,128); + installMii(fm.getFullPath(index).c_str(),false,doneMsg); + } + }else if(kDown & KEY_B){ + if(!fm.Back()){ + index = 0; + memset(doneMsg,0,128); + memcpy(doneMsg,defaultFooterMsg,strlen(defaultFooterMsg)); + } + }else if(kDown & KEY_Y){ + memset(doneMsg,0,128); + stat = dumpMii(fm.curPath,doneMsg); + if(stat == 1){ + memcpy(doneMsg,defaultFooterMsg,strlen(defaultFooterMsg)); + }else if(stat == 0){ + index = 0; + fm.Reload(); + } + }else if(kDown & KEY_X){ + if(is3dsmiiFile(fm.entries[index].name)){ + memset(doneMsg,0,128); + installMii(fm.getFullPath(index).c_str(),true,doneMsg); + } + }else if(kDown & KEY_UP){ + if(index > 0){ + index--; + memset(doneMsg,0,128); + memcpy(doneMsg,defaultFooterMsg,strlen(defaultFooterMsg)); + } + }else if(kDown & KEY_DOWN){ + if(index < fm.entries.size() - 1){ + index++; + memset(doneMsg,0,128); + memcpy(doneMsg,defaultFooterMsg,strlen(defaultFooterMsg)); + } + } + page = index / MAX_SHOW_FILE_NUM; + C2D_TextBufClear(g_dynamicBuf); + C2D_TextBufClear(g_footer); + for(i = 0;i < MAX_SHOW_FILE_NUM;i++){ + if(page * MAX_SHOW_FILE_NUM + i < fm.entries.size()){ + C2D_TextParse(&entriesString[i], g_dynamicBuf,fm.entries[page * MAX_SHOW_FILE_NUM + i].name.c_str()); + if((fm.entries[page * MAX_SHOW_FILE_NUM + i]).isDir){ + C2D_DrawSprite(&entrySprites[i][0]); + }else if(is3dsmiiFile(fm.entries[page * MAX_SHOW_FILE_NUM + i].name)){ + C2D_DrawSprite(&entrySprites[i][1]); + }else{ + C2D_DrawSprite(&entrySprites[i][2]); + } + }else{ + C2D_TextParse(&entriesString[i], g_dynamicBuf,""); + } + C2D_TextOptimize(&entriesString[i]); + C2D_DrawText(&entriesString[i], 0,38.0f,(float)((i + 2) * 15 + 8), 0.5f, 0.5f, 0.5f); + } + C2D_TextParse(&curPathString, g_dynamicBuf,fm.curPath.c_str()); + C2D_TextOptimize(&curPathString); + C2D_DrawText(&curPathString, 0, 8.0f,8.0f, 0.5f, 0.5f, 0.5f); + if(fm.entries.size() > 0){ + allowSprite.params.pos.y = (float)((index - page * MAX_SHOW_FILE_NUM) * 15 + 38); + C2D_DrawSprite(&allowSprite); + } + } + C2D_TextParse(&footer, g_footer,doneMsg); + C2D_TextOptimize(&footer); + C2D_DrawText(&footer,0,8.0f,202.0f,0.5f, 0.5f, 0.5f); + C2D_TargetClear(top, C2D_Color32(0x68, 0xB0, 0xD8, 0xFF)); + C2D_SceneBegin(top); + C2D_DrawSprite(&topSprite); + + C3D_FrameEnd(0); + } + + // Deinitialize the scene + sceneExit(); + //free(raw.data); + C2D_SpriteSheetFree(spriteSheet); + // Deinitialize the libs + C2D_Fini(); + C3D_Fini(); + romfsExit(); + gfxExit(); + return 0; +} diff --git a/source/mii.cpp b/source/mii.cpp new file mode 100644 index 0000000..b1ead4d --- /dev/null +++ b/source/mii.cpp @@ -0,0 +1,275 @@ +#include "mii.hpp" + +const unsigned int TARGET_ID = 0xf000000b; +char TARGET_PATH[] = "/CFL_DB.dat"; +char DAT_MAGIC[] = "CFOG"; + +int readMiis(mii*dest,char *msgBuf){ + char* msg; + int miiNum = 0,miiSlot = 0; + Handle fh; + u32 actSize,fileSize; + char headBuf[5] = {}; + Archive arc(TARGET_ID); + if(!arc.valid){ + msg = "Error:Archive::Archive"; + memcpy(msgBuf,msg,strlen(msg)); + return -1; + } + if(arc.Open(&fh,TARGET_PATH,FS_OPEN_READ)){ + msg = "Error:Archive::Open"; + memcpy(msgBuf,msg,strlen(msg)); + return -1; + } + fileSize = arc.GetFileSize(fh); + if(fileSize < DAT_MIN_SIZE){ + msg = "Error:CFL_DB.dat is too small"; + memcpy(msgBuf,msg,strlen(msg)); + arc.Close(fh); + return -1; + } + unsigned char *DatFileBuf; //CFL_DB.datを格納するバッファ + DatFileBuf = (unsigned char*)calloc(fileSize,sizeof(unsigned char)); + if(!DatFileBuf){ + msg = "Error:calloc"; + memcpy(msgBuf,msg,strlen(msg)); + arc.Close(fh); + return -1; + } + arc.Read(fh,&actSize,0,DatFileBuf,fileSize); + arc.Close(fh); + memcpy(headBuf,DatFileBuf,4); + if(strcmp(headBuf,DAT_MAGIC)){ + msg = "Error:magic doesn't match"; + memcpy(msgBuf,msg,strlen(msg)); + free(DatFileBuf); + return -1; + } + while(miiSlot < MAX_MII_NUM){ + memcpy((dest + miiNum)->rawData,DatFileBuf + 8 + miiSlot * MII_FILE_SIZE,MII_FILE_SIZE); + miiSlot++; + if(miiRawDataCheck((dest + miiNum)->rawData))continue; + miiNum++; + } + if(miiNum == 0){ + msg = "Error:There is NO Miis"; + memcpy(msgBuf,msg,strlen(msg)); + } + allGetMiiInfo(dest,miiNum); + return miiNum; +} + +int miiRawDataCheck(unsigned char*src){ + int i; + for(i = 0;i < MII_FILE_SIZE;i++){ + if(src[i] != 0)return 0; + } + return -1; +} + +int installMii(const char *fn,bool installAsPersonal,char *msgBuf){ + bool MiiShownSlot[MAX_MII_NUM]; + char* msg; + unsigned short crc; + unsigned char MiiFileBuf[MII_FILE_SIZE]; + unsigned char tmpMiiID[4]; + int miiSlot = 0,curMiiShownSlot,miiInstallSlot = -1,fd; + Handle fh; + FILE *f; + u32 actSize,fileSize; + char headBuf[5] = {}; + Archive arc(TARGET_ID); + fd = open(fn,O_RDONLY); + if(fd < 0){ + msg = "Error:open"; + memcpy(msgBuf,msg,strlen(msg)); + return -1; + } + if((int)getFileSize(fd) != MII_FILE_SIZE){ + msg = "Error:invalid file"; + memcpy(msgBuf,msg,strlen(msg)); + close(fd); + return -1; + } + f = fdopen(fd,"rb"); + if(!f){ + msg = "Error:fdopen"; + memcpy(msgBuf,msg,strlen(msg)); + close(fd); + return -1; + } + fread(MiiFileBuf,sizeof(unsigned char),MII_FILE_SIZE,f); + if(MiiFileBuf[0] != 3 || MiiFileBuf[0x16] != 0 || MiiFileBuf[0x17] != 0){ + msg = "Error:invalid file"; + memcpy(msgBuf,msg,strlen(msg)); + fclose(f); + return -1; + } + if(!arc.valid){ + msg = "Error:Archive::Archive"; + memcpy(msgBuf,msg,strlen(msg)); + return -1; + } + if(arc.Open(&fh,TARGET_PATH,FS_OPEN_WRITE)){ + msg = "Error:Archive::Open"; + memcpy(msgBuf,msg,strlen(msg)); + return -1; + } + fileSize = arc.GetFileSize(fh); + if(fileSize < DAT_MIN_SIZE){ + msg = "Error:CFL_DB.dat is too small"; + memcpy(msgBuf,msg,strlen(msg)); + arc.Close(fh); + return -1; + } + unsigned char *DatFileBuf; //CFL_DB.datを格納するバッファ + DatFileBuf = (unsigned char*)calloc(fileSize,sizeof(unsigned char)); + if(!DatFileBuf){ + msg = "Error:calloc"; + memcpy(msgBuf,msg,strlen(msg)); + arc.Close(fh); + return -1; + } + arc.Read(fh,&actSize,0,DatFileBuf,fileSize); + memcpy(headBuf,DatFileBuf,4); + if(strcmp(headBuf,DAT_MAGIC)){ + msg = "Error:magic doesn't match"; + memcpy(msgBuf,msg,strlen(msg)); + arc.Close(fh); + free(DatFileBuf); + return -1; + } + while(miiSlot < MAX_MII_NUM){ + if(miiRawDataCheck(DatFileBuf + 8 + miiSlot * MII_FILE_SIZE)){ + if(miiInstallSlot == -1){ + miiInstallSlot = miiSlot; + } + }else{ + curMiiShownSlot = (DatFileBuf[8 + miiSlot * MII_FILE_SIZE + 2] & 0xf) * 10 + ((DatFileBuf[8 + miiSlot * MII_FILE_SIZE + 2] >> 4) & 0xf); + MiiShownSlot[curMiiShownSlot] = true; + } + miiSlot++; + } + if(miiInstallSlot == -1){ + msg = "Error:Mii is full"; + memcpy(msgBuf,msg,strlen(msg)); + free(DatFileBuf); + arc.Close(fh); + return -1; + } + curMiiShownSlot = 0; + while(curMiiShownSlot < MAX_MII_NUM){ + if(!MiiShownSlot[curMiiShownSlot])break; + curMiiShownSlot++; + } + MiiFileBuf[2] = (curMiiShownSlot / 10) + ((curMiiShownSlot % 10) << 4); + if(installAsPersonal){ + memcpy(MiiFileBuf + 4,DatFileBuf + 8 + 4,8); + memcpy(DatFileBuf + 8 + miiInstallSlot * MII_FILE_SIZE,DatFileBuf + 8,MII_FILE_SIZE); + memcpy(DatFileBuf + 8,MiiFileBuf,MII_FILE_SIZE); + }else{ + memcpy(DatFileBuf + 8 + miiInstallSlot * MII_FILE_SIZE,MiiFileBuf,MII_FILE_SIZE); + } + crc = getCrc(DatFileBuf,DAT_MIN_SIZE - 2); + DatFileBuf[DAT_MIN_SIZE - 2] = (unsigned char)(crc >> 8); + DatFileBuf[DAT_MIN_SIZE - 1] = (unsigned char)(crc & 0xff); + arc.Write(fh,&actSize,0LL,DatFileBuf,fileSize); + msg = "Mii was successfully installed"; + memcpy(msgBuf,msg,strlen(msg)); + free(DatFileBuf); + arc.Close(fh); + return 0; +} + +int miiFileWrite(mii *Miis,int index,const char *dir,char *msgBuf){ + char path[128] = {}; + char *msg; + FILE *f; + sprintf(path,"%s/%08d.3dsmii",dir,index + 1); + f = fopen(path,"wb"); + if(!f){ + msg = "Error:fopen"; + memcpy(msgBuf,msg,sizeof(msg)); + return -1; + } + fwrite((Miis[index]).rawData,sizeof(unsigned char),MII_FILE_SIZE,f); + fclose(f); + sprintf(msgBuf,"%s was dumped to \n%s",Miis[index].name,path); + return 0; +} + +void getMiiInfo(mii *pmii){ + int i,curIdx = 0; + unsigned short curCh; + memset(pmii->name,0,MII_NAME_LENGTH * 3 + 1); + for(i = 0;i < MII_NAME_LENGTH;i++){ + curCh = (pmii->rawData[0x1B + 2 * i] << 8) + pmii->rawData[0x1A + 2 * i]; + if(curCh == 0)break; + if(curCh < 128){ + pmii->name[curIdx] = (char)curCh; + curIdx++; + }else if(curCh < 2048){ + pmii->name[curIdx] = (char)((curCh >> 6) + 0xc0); + curIdx++; + pmii->name[curIdx] = (char)((curCh & 0x3f) + 0x80); + curIdx++; + }else{ + pmii->name[curIdx] = (char)((curCh >> 12) + 0xe0); + curIdx++; + pmii->name[curIdx] = (char)(((curCh >> 6) & 0x3f) + 0x80); + curIdx++; + pmii->name[curIdx] = (char)((curCh & 0x3f) + 0x80); + curIdx++; + } + } + pmii->shownSlot = (pmii->rawData[2] & 0xf) * 10 + ((pmii->rawData[2] >> 4) & 0xf); + return; +} + +void allGetMiiInfo(mii*Miis,int num){ + int i; + for(i = 0;i < num;i++){ + getMiiInfo(Miis + i); + } +} + +int selectMii(mii* Miis,int miiNum){ + MiiSelectorConf msConf; + MiiSelectorReturn msRet; + int i,shownSlot; + miiSelectorInit(&msConf); + miiSelectorSetTitle(&msConf, "Select Mii you want to dump"); + miiSelectorSetOptions(&msConf, MIISELECTOR_CANCEL); + miiSelectorLaunch(&msConf, &msRet); + if (msRet.no_mii_selected || !miiSelectorChecksumIsValid(&msRet))return -1; + shownSlot = msRet.mii.mii_pos.page_index * 10 + msRet.mii.mii_pos.slot_index; + for(i = 0;i < miiNum;i++){ + if(shownSlot == Miis[i].shownSlot)return i; + } + return -1; +} + +long getFileSize(int fd){ + struct stat stbuf; + if(fstat(fd,&stbuf) == -1){ + printf("Error:fstat\n"); + close(fd); + return -1L; + } + return stbuf.st_size; +} + +int dumpMii(string dir,char *msgBuf){ + mii Miis[MAX_MII_NUM]; + int miiSlot; + int miiNum = readMiis(Miis,msgBuf); + if(miiNum < 1){ + return -1; + } + miiSlot = selectMii(Miis,miiNum); + if(miiSlot == -1){ + return 1; + } + if(dir[dir.size() - 1] == '/')dir.pop_back(); + return miiFileWrite(Miis,miiSlot,dir.c_str(),msgBuf); +} \ No newline at end of file diff --git a/source/mii.hpp b/source/mii.hpp new file mode 100644 index 0000000..c05ab83 --- /dev/null +++ b/source/mii.hpp @@ -0,0 +1,40 @@ +#ifndef _MII_H_ +#define _MII_H_ +#define DAT_MIN_SIZE 0xC820 +#define MII_FILE_SIZE 0x5C +#define MAX_MII_NUM 100 +#define MII_NAME_LENGTH 10 +#define SHOW_MII_NUM 10 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "extdata.hpp" + +using namespace std; + +typedef struct { + unsigned char rawData[MII_FILE_SIZE]; + char name[3 * MII_NAME_LENGTH + 1]; + int shownSlot; +} mii; + +int readMiis(mii*,char*); +int miiRawDataCheck(unsigned char*); +int installMii(const char*,bool,char*); +int miiFileWrite(mii*,int,const char*,char*); +void getMiiInfo(mii*); +void allGetMiiInfo(mii*,int); +int selectMii(mii*,int); +long getFileSize(int); +int dumpMii(string,char*); +unsigned short getCrc(unsigned char*,int); + +#endif //_MII_H_ \ No newline at end of file