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 0000000..b4613d9 Binary files /dev/null and b/gfx/allow.png differ diff --git a/gfx/folder.png b/gfx/folder.png new file mode 100644 index 0000000..9155b8a Binary files /dev/null and b/gfx/folder.png differ diff --git a/gfx/mii.png b/gfx/mii.png new file mode 100644 index 0000000..7fa481f Binary files /dev/null and b/gfx/mii.png differ 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 0000000..0162e05 Binary files /dev/null and b/gfx/top.png differ diff --git a/gfx/unknown.png b/gfx/unknown.png new file mode 100644 index 0000000..612b1a6 Binary files /dev/null and b/gfx/unknown.png differ diff --git a/source/crc16.cpp b/source/crc16.cpp new file mode 100644 index 0000000..254d478 --- /dev/null +++ b/source/crc16.cpp @@ -0,0 +1,14 @@ +unsigned short getCrc(unsigned char*buf,int size){ + unsigned int crc = 0x0000; + int byteIndex,bitIndex,counter; + for (byteIndex = 0; byteIndex < size; byteIndex++) { + for (bitIndex = 7; bitIndex >= 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