├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README ├── meta ├── meta.xml └── saviine.png ├── saviine.elf ├── server ├── dump.bat ├── inject.bat ├── saviine_server.exe └── src │ ├── .vs │ └── cafiine_server │ │ └── v14 │ │ └── .suo │ ├── DumpDialog.Designer.cs │ ├── DumpDialog.cs │ ├── DumpDialog.resx │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── SaveSelectorDialog.Designer.cs │ ├── SaveSelectorDialog.cs │ ├── SaveSelectorDialog.resx │ ├── System │ └── IO │ │ ├── EndianBinaryReader.cs │ │ └── EndianBinaryWriter.cs │ ├── app.config │ ├── cafiine_server.v12.suo │ ├── obj │ └── x86 │ │ └── Debug │ │ ├── saviine_server.DumpDialog.resources │ │ └── saviine_server.SaveSelectorDialog.resources │ ├── saviine_server.csproj │ ├── saviine_server.csproj.user │ ├── saviine_server.sln │ ├── saviine_server.suo │ └── saviine_server.v12.suo └── src ├── common ├── common.h ├── fs_defs.h ├── kernel_defs.h ├── loader_defs.h ├── os_defs.h └── types.h ├── entry.c ├── game ├── memory_area_table.c ├── memory_area_table.h ├── rpx_rpl_table.c └── rpx_rpl_table.h ├── kernel ├── kernel_functions.c ├── kernel_functions.h ├── kernel_hooks.S ├── syscalls.c ├── syscalls.h └── syscalls_asm.S ├── link.ld ├── main.c ├── main.h ├── patcher ├── function_hooks.c └── function_hooks.h ├── saviine.c ├── saviine.h ├── system ├── memory.c └── memory.h └── utils ├── logger.c ├── logger.h ├── net.c ├── net.h └── utils.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | build/* 45 | server/src/bin/logs/* 46 | server/src/bin/saviine_root/* 47 | server/src/bin/saviine_server.pdb 48 | server/src/bin/saviine_server.vshost.exe.manifest 49 | server/src/obj/x86/Debug/saviine_server.exe 50 | server/src/obj/x86/Debug/saviine_server.pdb 51 | server/src/bin/saviine_server.vshost.exe 52 | server/src/bin/saviine_server.exe.config 53 | server/src/bin/saviine_server.vshost.exe.config 54 | server/src/obj/x86/Debug/DesignTimeResolveAssemblyReferences.cache 55 | server/src/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache 56 | server/src/obj/x86/Debug/saviine_server.csprojResolveAssemblyReference.cache 57 | server/src/obj/x86/Debug/saviine_server.csproj.FileListAbsolute.txt 58 | server/src/obj/x86/Debug/saviine_server.csproj.GenerateResource.Cache 59 | server/src/obj/x86/Debug/saviine_server.Form1.resources 60 | server/src/bin/saviine_server.exe 61 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/dynamic_libs"] 2 | path = src/dynamic_libs 3 | url = https://github.com/Maschell/dynamic_libs 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | # Clear the implicit built in rules 3 | #--------------------------------------------------------------------------------- 4 | .SUFFIXES: 5 | #--------------------------------------------------------------------------------- 6 | ifeq ($(strip $(DEVKITPPC)),) 7 | $(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") 8 | endif 9 | ifeq ($(strip $(DEVKITPRO)),) 10 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPRO") 11 | endif 12 | export PATH := $(DEVKITPPC)/bin:$(PORTLIBS)/bin:$(PATH) 13 | export LIBOGC_INC := $(DEVKITPRO)/libogc/include 14 | export LIBOGC_LIB := $(DEVKITPRO)/libogc/lib/wii 15 | export PORTLIBS := $(DEVKITPRO)/portlibs/ppc 16 | 17 | PREFIX := powerpc-eabi- 18 | 19 | export AS := $(PREFIX)as 20 | export CC := $(PREFIX)gcc 21 | export CXX := $(PREFIX)g++ 22 | export AR := $(PREFIX)ar 23 | export OBJCOPY := $(PREFIX)objcopy 24 | 25 | #--------------------------------------------------------------------------------- 26 | # TARGET is the name of the output 27 | # BUILD is the directory where object files & intermediate files will be placed 28 | # SOURCES is a list of directories containing source code 29 | # INCLUDES is a list of directories containing extra header files 30 | #--------------------------------------------------------------------------------- 31 | TARGET := saviine 32 | BUILD := build 33 | BUILD_DBG := $(TARGET)_dbg 34 | SOURCES := src \ 35 | src/dynamic_libs \ 36 | src/game \ 37 | src/kernel \ 38 | src/patcher \ 39 | src/system \ 40 | src/utils 41 | DATA := 42 | 43 | INCLUDES := src 44 | 45 | #--------------------------------------------------------------------------------- 46 | # options for code generation 47 | #--------------------------------------------------------------------------------- 48 | CFLAGS := -std=gnu11 -mrvl -mcpu=750 -meabi -mhard-float -ffast-math \ 49 | -O3 -Wall -Wextra -Wno-unused-parameter -Wno-strict-aliasing $(INCLUDE) 50 | CXXFLAGS := -std=gnu++11 -mrvl -mcpu=750 -meabi -mhard-float -ffast-math \ 51 | -O3 -Wall -Wextra -Wno-unused-parameter -Wno-strict-aliasing $(INCLUDE) 52 | ASFLAGS := -mregnames 53 | LDFLAGS := -nostartfiles -Wl,-Map,$(notdir $@).map,-wrap,malloc,-wrap,free,-wrap,memalign,-wrap,calloc,-wrap,realloc,-wrap,malloc_usable_size,-wrap,_malloc_r,-wrap,_free_r,-wrap,_realloc_r,-wrap,_calloc_r,-wrap,_memalign_r,-wrap,_malloc_usable_size_r,-wrap,valloc,-wrap,_valloc_r,-wrap,_pvalloc_r,--gc-sections 54 | 55 | #--------------------------------------------------------------------------------- 56 | Q := @ 57 | MAKEFLAGS += --no-print-directory 58 | #--------------------------------------------------------------------------------- 59 | # any extra libraries we wish to link with the project 60 | #--------------------------------------------------------------------------------- 61 | LIBS := 62 | 63 | #--------------------------------------------------------------------------------- 64 | # list of directories containing libraries, this must be the top level containing 65 | # include and lib 66 | #--------------------------------------------------------------------------------- 67 | LIBDIRS := $(CURDIR) \ 68 | $(DEVKITPPC)/lib \ 69 | $(DEVKITPPC)/lib/gcc/powerpc-eabi/4.8.2 70 | 71 | 72 | #--------------------------------------------------------------------------------- 73 | # no real need to edit anything past this point unless you need to add additional 74 | # rules for different file extensions 75 | #--------------------------------------------------------------------------------- 76 | ifneq ($(BUILD),$(notdir $(CURDIR))) 77 | #--------------------------------------------------------------------------------- 78 | export PROJECTDIR := $(CURDIR) 79 | export OUTPUT := $(CURDIR)/$(TARGETDIR)/$(TARGET) 80 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 81 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 82 | export DEPSDIR := $(CURDIR)/$(BUILD) 83 | 84 | #--------------------------------------------------------------------------------- 85 | # automatically build a list of object files for our project 86 | #--------------------------------------------------------------------------------- 87 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 88 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 89 | sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 90 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) 91 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 92 | TTFFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.ttf))) 93 | PNGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.png))) 94 | 95 | #--------------------------------------------------------------------------------- 96 | # use CXX for linking C++ projects, CC for standard C 97 | #--------------------------------------------------------------------------------- 98 | ifeq ($(strip $(CPPFILES)),) 99 | export LD := $(CC) 100 | else 101 | export LD := $(CXX) 102 | endif 103 | 104 | export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ 105 | $(sFILES:.s=.o) $(SFILES:.S=.o) \ 106 | $(PNGFILES:.png=.png.o) $(addsuffix .o,$(BINFILES)) 107 | 108 | #--------------------------------------------------------------------------------- 109 | # build a list of include paths 110 | #--------------------------------------------------------------------------------- 111 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 112 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 113 | -I$(CURDIR)/$(BUILD) -I$(LIBOGC_INC) \ 114 | -I$(PORTLIBS)/include -I$(PORTLIBS)/include/freetype2 115 | 116 | #--------------------------------------------------------------------------------- 117 | # build a list of library paths 118 | #--------------------------------------------------------------------------------- 119 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ 120 | -L$(LIBOGC_LIB) -L$(PORTLIBS)/lib 121 | 122 | export OUTPUT := $(CURDIR)/$(TARGET) 123 | .PHONY: $(BUILD) clean install 124 | 125 | #--------------------------------------------------------------------------------- 126 | $(BUILD): 127 | @[ -d $@ ] || mkdir -p $@ 128 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 129 | 130 | #--------------------------------------------------------------------------------- 131 | clean: 132 | @echo clean ... 133 | @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).bin $(BUILD_DBG).elf 134 | 135 | #--------------------------------------------------------------------------------- 136 | else 137 | 138 | DEPENDS := $(OFILES:.o=.d) 139 | 140 | #--------------------------------------------------------------------------------- 141 | # main targets 142 | #--------------------------------------------------------------------------------- 143 | $(OUTPUT).elf: $(OFILES) 144 | 145 | #--------------------------------------------------------------------------------- 146 | # This rule links in binary data with the .jpg extension 147 | #--------------------------------------------------------------------------------- 148 | %.elf: link.ld $(OFILES) 149 | @echo "linking ... $(TARGET).elf" 150 | $(Q)$(LD) -n -T $^ $(LDFLAGS) -o ../$(BUILD_DBG).elf $(LIBPATHS) $(LIBS) 151 | $(Q)$(OBJCOPY) -S -R .comment -R .gnu.attributes ../$(BUILD_DBG).elf $@ 152 | 153 | ../data/loader.bin: 154 | $(MAKE) -C ../loader clean 155 | $(MAKE) -C ../loader 156 | #--------------------------------------------------------------------------------- 157 | %.a: 158 | #--------------------------------------------------------------------------------- 159 | @echo $(notdir $@) 160 | @rm -f $@ 161 | @$(AR) -rc $@ $^ 162 | 163 | #--------------------------------------------------------------------------------- 164 | %.o: %.cpp 165 | @echo $(notdir $<) 166 | @$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER) 167 | 168 | #--------------------------------------------------------------------------------- 169 | %.o: %.c 170 | @echo $(notdir $<) 171 | @$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) -c $< -o $@ $(ERROR_FILTER) 172 | 173 | #--------------------------------------------------------------------------------- 174 | %.o: %.S 175 | @echo $(notdir $<) 176 | @$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER) 177 | 178 | #--------------------------------------------------------------------------------- 179 | %.png.o : %.png 180 | @echo $(notdir $<) 181 | @bin2s -a 32 $< | $(AS) -o $(@) 182 | 183 | #--------------------------------------------------------------------------------- 184 | %.jpg.o : %.jpg 185 | @echo $(notdir $<) 186 | @bin2s -a 32 $< | $(AS) -o $(@) 187 | 188 | #--------------------------------------------------------------------------------- 189 | %.ttf.o : %.ttf 190 | @echo $(notdir $<) 191 | @bin2s -a 32 $< | $(AS) -o $(@) 192 | 193 | #--------------------------------------------------------------------------------- 194 | %.bin.o : %.bin 195 | @echo $(notdir $<) 196 | @bin2s -a 32 $< | $(AS) -o $(@) 197 | 198 | #--------------------------------------------------------------------------------- 199 | %.wav.o : %.wav 200 | @echo $(notdir $<) 201 | @bin2s -a 32 $< | $(AS) -o $(@) 202 | 203 | #--------------------------------------------------------------------------------- 204 | %.mp3.o : %.mp3 205 | @echo $(notdir $<) 206 | @bin2s -a 32 $< | $(AS) -o $(@) 207 | 208 | #--------------------------------------------------------------------------------- 209 | %.ogg.o : %.ogg 210 | @echo $(notdir $<) 211 | @bin2s -a 32 $< | $(AS) -o $(@) 212 | 213 | -include $(DEPENDS) 214 | 215 | #--------------------------------------------------------------------------------- 216 | endif 217 | #--------------------------------------------------------------------------------- 218 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Saviine 1.1 (02.04.2016) 2 | 3 | Dumps and injects (experimental) WiiU Saves. 4 | Works with homebrew launcher (https://github.com/dimok789/homebrew_launcher), compatible with FW 5.0.0, 5.1,0, 5.3.2, 5.4.0, 5.5.x (But requires a kernel exploit) 5 | 6 | Compilation : 7 | - just compile it with "make" and put the saviine.elf on your sd card 8 | 9 | Pre-made version : 10 | - you can fine a release package under the "releases" tab 11 | 12 | Note : 13 | - make sure you already have a save of the game on you console before trying to dump/inject them 14 | - you can change the server ip directly in the application. 15 | - start the saviine in folder with a short path, some savedata files have some really long names (170+ chracters) 16 | - backup your savedata before dumping it!!! I'm not responsible for any lost data. 17 | - make sure that your game version and the game version from injecting save are the same 18 | - e.g. injecting PAL saves into NTSC one is untested! 19 | - this new version only runs with the homebrew_launcher. 20 | 21 | NOTE FOR SUPER SMASH BROS FOR WIIU the common data is too big to inject at one time. The data will may be flushed at some time. automaticly restoring the data after flushing is not possible. MAKE SURE TO MAKE A BACKUP! This may also applies to other gaames 22 | 23 | The WiiU has mutiple save folder. One folder for infos that are shared with all accounts (common) and a folder for each account (e.g 8000000a) 24 | At the moment it is only possible to dump the common folder your own folder. Not all games use the common folder. 25 | 26 | Folder structur: 27 | logs/ <-- logs 28 | saviine_root/dump <-- the save files will be dumped here. A sub folder for each game will be created (name is the game title id) 29 | saviine_root/inject <-- place the saves here if you want to inject them 30 | 31 | How to use : 32 | 33 | temporary installing saviine: 34 | - put the elf into the app path on your SD Card (e.g. /wiiu/apps/saviine/saviine.elf) and insert the sd card into your WiiU (You can also use the sendelf method) 35 | - launch the homebrew launcher to install the kernel exploit 36 | - relaunch browser and launch the homebrew launcher again. This time the homebrew launcher will start. 37 | - select saviine and start it. 38 | - set saviine server ip in the application with up/down/left/right, and press A to install saviine. if you see crap on the screen, press up/down to fix it 39 | -> browser should exit 40 | 41 | dumping: 42 | --- MAKE SURE THAT THE SAVIINE SERVER IN A FOLDER WITH A SHORT PATH! some gamefiles have some really really long names. e.g. D:/saviine/ --- 43 | - open the dump.bat or start the saviine_server.exe with the argument "dump" 44 | - launch a game 45 | - a dialog should appear, select if you want to dump the user and/or the common data 46 | - the data will be dumped to saviine_root/dump/[game_title_id]/80000000x (user data) and/or saviine_root/dump/[game_title_id]/common (common) 47 | - close the game and dump an other 48 | 49 | injecting: 50 | --- INJECTING FILES IS STILL IN TESTING! MAKE SURE TO SAVE YOUR SAVES BEFORE TRYING TO INJECT SOMETHING --- 51 | --- EXISTING SAVE DATA ON THAT WILL BE GONE AFTER THE INJECTION --- 52 | 53 | - open the inject.bat or start the saviine_server.exe with the argument "inject" 54 | - place the save data you want to inject into the "saviine_root/inject" folder. You need to have a folder for each game, named after the games title id (e.g saviine_root\inject\00050000-1010ED00). 55 | this folder will be scanned for 8000000x or common folders. 56 | - launch a game 57 | - a dialog should appear, select if the profile you want to inject and choose if you want to inject the common files too. 58 | For the commmon folder you have two options: 59 | 1. inject. 60 | this adds the data into the existing common folder, existing files will be overwritten. (I think this way you can add replays to your Mario Kart 8) 61 | 2. clean and inject 62 | deletes all existing files in the existing common and injects the new one. 63 | 64 | - on errors during the injection, the wii tries to restore the old data. but it can't guarantee that it works, make sure to make backups! 65 | 66 | Thanks to 67 | dimok - for the homebrew launcher, loadiine gx 2, and ddd with was the base environment for the current version. =) 68 | chadderz - for cafiine. Still using some of this old funcions =) -------------------------------------------------------------------------------- /meta/meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Saviine 4 | Maschell 5 | 1.1b 6 | 20160512120000 7 | Savegame tool 8 | This application dumps and injects savegames of any title that is entered over network to a PC application. 9 | 10 | 11 | -------------------------------------------------------------------------------- /meta/saviine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/meta/saviine.png -------------------------------------------------------------------------------- /saviine.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/saviine.elf -------------------------------------------------------------------------------- /server/dump.bat: -------------------------------------------------------------------------------- 1 | saviine_server.exe dump -------------------------------------------------------------------------------- /server/inject.bat: -------------------------------------------------------------------------------- 1 | saviine_server.exe inject -------------------------------------------------------------------------------- /server/saviine_server.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/server/saviine_server.exe -------------------------------------------------------------------------------- /server/src/.vs/cafiine_server/v14/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/server/src/.vs/cafiine_server/v14/.suo -------------------------------------------------------------------------------- /server/src/DumpDialog.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace saviine_server 2 | { 3 | partial class DumpDialog 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.btn_ok = new System.Windows.Forms.Button(); 32 | this.btn_cancel = new System.Windows.Forms.Button(); 33 | this.lbl_message = new System.Windows.Forms.Label(); 34 | this.label1 = new System.Windows.Forms.Label(); 35 | this.Inj = new System.Windows.Forms.Label(); 36 | this.checkBoxUser = new System.Windows.Forms.CheckBox(); 37 | this.checkBoxCommon = new System.Windows.Forms.CheckBox(); 38 | this.SuspendLayout(); 39 | // 40 | // btn_ok 41 | // 42 | this.btn_ok.DialogResult = System.Windows.Forms.DialogResult.OK; 43 | this.btn_ok.Location = new System.Drawing.Point(15, 133); 44 | this.btn_ok.Name = "btn_ok"; 45 | this.btn_ok.Size = new System.Drawing.Size(81, 21); 46 | this.btn_ok.TabIndex = 0; 47 | this.btn_ok.Text = "OK"; 48 | this.btn_ok.UseVisualStyleBackColor = true; 49 | this.btn_ok.Click += new System.EventHandler(this.btn_ok_Click); 50 | // 51 | // btn_cancel 52 | // 53 | this.btn_cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 54 | this.btn_cancel.Location = new System.Drawing.Point(148, 133); 55 | this.btn_cancel.Name = "btn_cancel"; 56 | this.btn_cancel.Size = new System.Drawing.Size(81, 21); 57 | this.btn_cancel.TabIndex = 1; 58 | this.btn_cancel.Text = "Cancel"; 59 | this.btn_cancel.UseVisualStyleBackColor = true; 60 | // 61 | // lbl_message 62 | // 63 | this.lbl_message.AutoSize = true; 64 | this.lbl_message.Location = new System.Drawing.Point(12, 9); 65 | this.lbl_message.Name = "lbl_message"; 66 | this.lbl_message.Size = new System.Drawing.Size(217, 13); 67 | this.lbl_message.TabIndex = 3; 68 | this.lbl_message.Text = "Got a dump request"; 69 | // 70 | // label1 71 | // 72 | this.label1.AutoSize = true; 73 | this.label1.Location = new System.Drawing.Point(12, 51); 74 | this.label1.Name = "label1"; 75 | this.label1.Size = new System.Drawing.Size(84, 13); 76 | this.label1.TabIndex = 5; 77 | this.label1.Text = "Dump user save"; 78 | // 79 | // Inj 80 | // 81 | this.Inj.AutoSize = true; 82 | this.Inj.Location = new System.Drawing.Point(12, 86); 83 | this.Inj.Name = "Inj"; 84 | this.Inj.Size = new System.Drawing.Size(104, 13); 85 | this.Inj.TabIndex = 7; 86 | this.Inj.Text = "Dump common save"; 87 | // 88 | // checkBoxUser 89 | // 90 | this.checkBoxUser.AutoSize = true; 91 | this.checkBoxUser.Checked = true; 92 | this.checkBoxUser.CheckState = System.Windows.Forms.CheckState.Checked; 93 | this.checkBoxUser.Location = new System.Drawing.Point(202, 50); 94 | this.checkBoxUser.Name = "checkBoxUser"; 95 | this.checkBoxUser.Size = new System.Drawing.Size(15, 14); 96 | this.checkBoxUser.TabIndex = 9; 97 | this.checkBoxUser.UseVisualStyleBackColor = true; 98 | // 99 | // checkBoxCommon 100 | // 101 | this.checkBoxCommon.AutoSize = true; 102 | this.checkBoxCommon.Checked = true; 103 | this.checkBoxCommon.CheckState = System.Windows.Forms.CheckState.Checked; 104 | this.checkBoxCommon.Location = new System.Drawing.Point(202, 85); 105 | this.checkBoxCommon.Name = "checkBoxCommon"; 106 | this.checkBoxCommon.Size = new System.Drawing.Size(15, 14); 107 | this.checkBoxCommon.TabIndex = 10; 108 | this.checkBoxCommon.UseVisualStyleBackColor = true; 109 | // 110 | // DumpDialog 111 | // 112 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 113 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 114 | this.ClientSize = new System.Drawing.Size(257, 166); 115 | this.Controls.Add(this.checkBoxCommon); 116 | this.Controls.Add(this.checkBoxUser); 117 | this.Controls.Add(this.Inj); 118 | this.Controls.Add(this.label1); 119 | this.Controls.Add(this.lbl_message); 120 | this.Controls.Add(this.btn_cancel); 121 | this.Controls.Add(this.btn_ok); 122 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 123 | this.Name = "DumpDialog"; 124 | this.Text = "Dump request"; 125 | this.ResumeLayout(false); 126 | this.PerformLayout(); 127 | 128 | } 129 | 130 | #endregion 131 | 132 | private System.Windows.Forms.Button btn_ok; 133 | private System.Windows.Forms.Button btn_cancel; 134 | private System.Windows.Forms.Label lbl_message; 135 | private System.Windows.Forms.Label label1; 136 | private System.Windows.Forms.Label Inj; 137 | private System.Windows.Forms.CheckBox checkBoxUser; 138 | private System.Windows.Forms.CheckBox checkBoxCommon; 139 | } 140 | } -------------------------------------------------------------------------------- /server/src/DumpDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | 11 | namespace saviine_server 12 | { 13 | public partial class DumpDialog : Form 14 | { 15 | private Boolean dumpUser = false; 16 | private Boolean dumpCommon = false; 17 | 18 | public Boolean DumpCommon 19 | { 20 | get { return dumpCommon; } 21 | } 22 | public Boolean DumpUser 23 | { 24 | get { return dumpUser; } 25 | } 26 | private static string savePath; 27 | public DumpDialog(string title_id, long persistentID) 28 | { 29 | InitializeComponent(); 30 | 31 | } 32 | 33 | 34 | private void btn_ok_Click(object sender, EventArgs e) 35 | { 36 | dumpUser = checkBoxUser.Checked; 37 | dumpCommon = checkBoxCommon.Checked; 38 | 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/src/DumpDialog.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /server/src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("cafiine_server")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("ChadSoft")] 12 | [assembly: AssemblyProduct("cafiine_server")] 13 | [assembly: AssemblyCopyright("Copyright © ChadSoft 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("fc022709-becf-498f-9a2a-dc3543457b0d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /server/src/SaveSelectorDialog.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace saviine_server 2 | { 3 | partial class SaveSelectorDialog 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.btn_ok = new System.Windows.Forms.Button(); 32 | this.btn_cancel = new System.Windows.Forms.Button(); 33 | this.lbl_message = new System.Windows.Forms.Label(); 34 | this.comBoxIDList = new System.Windows.Forms.ComboBox(); 35 | this.label1 = new System.Windows.Forms.Label(); 36 | this.Inj = new System.Windows.Forms.Label(); 37 | this.comBoxCommon = new System.Windows.Forms.ComboBox(); 38 | this.SuspendLayout(); 39 | // 40 | // btn_ok 41 | // 42 | this.btn_ok.DialogResult = System.Windows.Forms.DialogResult.OK; 43 | this.btn_ok.Location = new System.Drawing.Point(62, 133); 44 | this.btn_ok.Name = "btn_ok"; 45 | this.btn_ok.Size = new System.Drawing.Size(81, 21); 46 | this.btn_ok.TabIndex = 0; 47 | this.btn_ok.Text = "OK"; 48 | this.btn_ok.UseVisualStyleBackColor = true; 49 | this.btn_ok.Click += new System.EventHandler(this.btn_ok_Click); 50 | // 51 | // btn_cancel 52 | // 53 | this.btn_cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 54 | this.btn_cancel.Location = new System.Drawing.Point(198, 133); 55 | this.btn_cancel.Name = "btn_cancel"; 56 | this.btn_cancel.Size = new System.Drawing.Size(81, 21); 57 | this.btn_cancel.TabIndex = 1; 58 | this.btn_cancel.Text = "Cancel"; 59 | this.btn_cancel.UseVisualStyleBackColor = true; 60 | // 61 | // lbl_message 62 | // 63 | this.lbl_message.AutoSize = true; 64 | this.lbl_message.Location = new System.Drawing.Point(12, 10); 65 | this.lbl_message.Name = "lbl_message"; 66 | this.lbl_message.Size = new System.Drawing.Size(236, 13); 67 | this.lbl_message.TabIndex = 3; 68 | this.lbl_message.Text = "Got an injection request for 00050000-10157F00"; 69 | // 70 | // comBoxIDList 71 | // 72 | this.comBoxIDList.FormattingEnabled = true; 73 | this.comBoxIDList.Location = new System.Drawing.Point(186, 48); 74 | this.comBoxIDList.Name = "comBoxIDList"; 75 | this.comBoxIDList.Size = new System.Drawing.Size(93, 21); 76 | this.comBoxIDList.TabIndex = 4; 77 | // 78 | // label1 79 | // 80 | this.label1.AutoSize = true; 81 | this.label1.Location = new System.Drawing.Point(12, 51); 82 | this.label1.Name = "label1"; 83 | this.label1.Size = new System.Drawing.Size(81, 13); 84 | this.label1.TabIndex = 5; 85 | this.label1.Text = "Select userdata"; 86 | this.label1.Click += new System.EventHandler(this.label1_Click); 87 | // 88 | // Inj 89 | // 90 | this.Inj.AutoSize = true; 91 | this.Inj.Location = new System.Drawing.Point(12, 86); 92 | this.Inj.Name = "Inj"; 93 | this.Inj.Size = new System.Drawing.Size(108, 13); 94 | this.Inj.TabIndex = 7; 95 | this.Inj.Text = "Inject common save?"; 96 | this.Inj.Click += new System.EventHandler(this.Inj_Click); 97 | // 98 | // comBoxCommon 99 | // 100 | this.comBoxCommon.FormattingEnabled = true; 101 | this.comBoxCommon.Items.AddRange(new object[] { 102 | "no", 103 | "inject", 104 | "clean and inject (deleting existing common)"}); 105 | this.comBoxCommon.Location = new System.Drawing.Point(126, 83); 106 | this.comBoxCommon.Name = "comBoxCommon"; 107 | this.comBoxCommon.Size = new System.Drawing.Size(153, 21); 108 | this.comBoxCommon.TabIndex = 8; 109 | // 110 | // SaveSelectorDialog 111 | // 112 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 113 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 114 | this.ClientSize = new System.Drawing.Size(310, 166); 115 | this.Controls.Add(this.comBoxCommon); 116 | this.Controls.Add(this.Inj); 117 | this.Controls.Add(this.label1); 118 | this.Controls.Add(this.comBoxIDList); 119 | this.Controls.Add(this.lbl_message); 120 | this.Controls.Add(this.btn_cancel); 121 | this.Controls.Add(this.btn_ok); 122 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 123 | this.Name = "SaveSelectorDialog"; 124 | this.Text = "Injection request"; 125 | this.Load += new System.EventHandler(this.Form1_Load); 126 | this.ResumeLayout(false); 127 | this.PerformLayout(); 128 | 129 | } 130 | 131 | #endregion 132 | 133 | private System.Windows.Forms.Button btn_ok; 134 | private System.Windows.Forms.Button btn_cancel; 135 | private System.Windows.Forms.Label lbl_message; 136 | private System.Windows.Forms.ComboBox comBoxIDList; 137 | private System.Windows.Forms.Label label1; 138 | private System.Windows.Forms.Label Inj; 139 | private System.Windows.Forms.ComboBox comBoxCommon; 140 | } 141 | } -------------------------------------------------------------------------------- /server/src/SaveSelectorDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | 11 | namespace saviine_server 12 | { 13 | public partial class SaveSelectorDialog : Form 14 | { 15 | private long newPersistentID = 0; 16 | private int dumpCommon = 0; 17 | 18 | public long NewPersistentID 19 | { 20 | get { return newPersistentID; } 21 | } 22 | public int DumpCommon 23 | { 24 | get { return dumpCommon; } 25 | } 26 | private static string savePath; 27 | public SaveSelectorDialog(string title_id,long persistentID) 28 | { 29 | InitializeComponent(); 30 | comBoxCommon.SelectedIndex = 0; 31 | savePath = Program.root + "/" + Program.injectfolder;; 32 | this.lbl_message.Text = "Got an injection request for " + title_id; 33 | savePath += "/" + title_id; 34 | string[] subdirectoryEntries; 35 | if (Directory.Exists(savePath)) 36 | { 37 | // Recurse into subdirectories of this directory. 38 | subdirectoryEntries = Directory.GetDirectories(savePath); 39 | this.comBoxIDList.Items.Add("---none---"); 40 | comBoxIDList.SelectedIndex = 0; 41 | foreach (string subdirectory in subdirectoryEntries) 42 | { 43 | string filename = Path.GetFileName(subdirectory); 44 | long id; 45 | try{ 46 | id = Convert.ToUInt32(filename, 16); 47 | }catch (Exception){ 48 | id = 0; 49 | } 50 | 51 | 52 | if (id >= 0x80000000 && id <= 0x81000000) 53 | { 54 | 55 | this.comBoxIDList.Items.Add(filename); 56 | } 57 | } 58 | if (comBoxIDList.Items.Count == 1) 59 | { 60 | this.comBoxIDList.Enabled = false; 61 | 62 | } 63 | if (!Directory.Exists(savePath + "/" + Program.common)) 64 | { 65 | comBoxCommon.Enabled = false; 66 | } 67 | } 68 | 69 | 70 | } 71 | 72 | private void Form1_Load(object sender, EventArgs e) 73 | { 74 | 75 | } 76 | 77 | private void btn_ok_Click(object sender, EventArgs e) 78 | { 79 | long id; 80 | try 81 | { 82 | id = Convert.ToUInt32(this.comBoxIDList.SelectedItem.ToString(), 16); 83 | } 84 | catch (Exception) 85 | { 86 | id = 0; 87 | } 88 | newPersistentID = id; 89 | dumpCommon = comBoxCommon.SelectedIndex; 90 | Console.WriteLine(dumpCommon); 91 | 92 | } 93 | private void btn_cancel_Click(object sender, EventArgs e) 94 | { 95 | 96 | } 97 | 98 | private void Inj_Click(object sender, EventArgs e) 99 | { 100 | 101 | } 102 | 103 | private void label1_Click(object sender, EventArgs e) 104 | { 105 | 106 | } 107 | 108 | 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /server/src/SaveSelectorDialog.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /server/src/System/IO/EndianBinaryReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace System.IO 6 | { 7 | sealed class EndianBinaryReader : IDisposable 8 | { 9 | private bool disposed; 10 | private byte[] buffer; 11 | 12 | private delegate void ArrayReverse(byte[] array, int count); 13 | private static readonly ArrayReverse[] fastReverse = new ArrayReverse[] { null, null, ArrayReverse2, null, ArrayReverse4, null, null, null, ArrayReverse8 }; 14 | 15 | private static Dictionary>> parserCache = new Dictionary>>(); 16 | 17 | public Stream BaseStream { get; private set; } 18 | public Endianness Endianness { get; set; } 19 | public static Endianness SystemEndianness { get { return BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; } } 20 | 21 | private bool Reverse { get { return SystemEndianness != Endianness; } } 22 | 23 | public EndianBinaryReader(Stream baseStream) 24 | : this(baseStream, Endianness.BigEndian) 25 | { } 26 | 27 | public EndianBinaryReader(Stream baseStream, Endianness endianness) 28 | { 29 | if (baseStream == null) throw new ArgumentNullException("baseStream"); 30 | if (!baseStream.CanRead) throw new ArgumentException("base stream is not readable.", "baseStream"); 31 | 32 | BaseStream = baseStream; 33 | Endianness = endianness; 34 | } 35 | 36 | ~EndianBinaryReader() 37 | { 38 | Dispose(false); 39 | } 40 | 41 | /// 42 | /// Fills the buffer with bytes bytes, possibly reversing every stride. Bytes must be a multiple of stide. Stide must be 1 or 2 or 4 or 8. 43 | /// 44 | /// 45 | /// 46 | private void FillBuffer(int bytes, int stride) 47 | { 48 | if (buffer == null || buffer.Length < bytes) 49 | buffer = new byte[bytes]; 50 | 51 | for (int i = 0, read = 0; i < bytes; i += read) 52 | { 53 | read = BaseStream.Read(buffer, i, bytes - i); 54 | if (read <= 0) throw new EndOfStreamException(); 55 | } 56 | 57 | if (Reverse) 58 | { 59 | if (fastReverse[stride] != null) 60 | fastReverse[stride](buffer, bytes); 61 | else 62 | for (int i = 0; i < bytes; i += stride) 63 | { 64 | Array.Reverse(buffer, i, stride); 65 | } 66 | } 67 | } 68 | 69 | private static void ArrayReverse2(byte[] array, int arrayLength) 70 | { 71 | byte temp; 72 | 73 | while (arrayLength > 0) 74 | { 75 | temp = array[arrayLength - 2]; 76 | array[arrayLength - 2] = array[arrayLength - 1]; 77 | array[arrayLength - 1] = temp; 78 | arrayLength -= 2; 79 | } 80 | } 81 | 82 | private static void ArrayReverse4(byte[] array, int arrayLength) 83 | { 84 | byte temp; 85 | 86 | while (arrayLength > 0) 87 | { 88 | temp = array[arrayLength - 3]; 89 | array[arrayLength - 3] = array[arrayLength - 2]; 90 | array[arrayLength - 2] = temp; 91 | temp = array[arrayLength - 4]; 92 | array[arrayLength - 4] = array[arrayLength - 1]; 93 | array[arrayLength - 1] = temp; 94 | arrayLength -= 4; 95 | } 96 | } 97 | 98 | private static void ArrayReverse8(byte[] array, int arrayLength) 99 | { 100 | byte temp; 101 | 102 | while (arrayLength > 0) 103 | { 104 | temp = array[arrayLength - 5]; 105 | array[arrayLength - 5] = array[arrayLength - 4]; 106 | array[arrayLength - 4] = temp; 107 | temp = array[arrayLength - 6]; 108 | array[arrayLength - 6] = array[arrayLength - 3]; 109 | array[arrayLength - 3] = temp; 110 | temp = array[arrayLength - 7]; 111 | array[arrayLength - 7] = array[arrayLength - 2]; 112 | array[arrayLength - 2] = temp; 113 | temp = array[arrayLength - 8]; 114 | array[arrayLength - 8] = array[arrayLength - 1]; 115 | array[arrayLength - 1] = temp; 116 | arrayLength -= 8; 117 | } 118 | } 119 | 120 | public byte ReadByte() 121 | { 122 | FillBuffer(1, 1); 123 | 124 | return buffer[0]; 125 | } 126 | 127 | public byte[] ReadBytes(int count) 128 | { 129 | byte[] temp; 130 | 131 | FillBuffer(count, 1); 132 | temp = new byte[count]; 133 | Array.Copy(buffer, 0, temp, 0, count); 134 | return temp; 135 | } 136 | 137 | public sbyte ReadSByte() 138 | { 139 | FillBuffer(1, 1); 140 | 141 | unchecked 142 | { 143 | return (sbyte)buffer[0]; 144 | } 145 | } 146 | 147 | public sbyte[] ReadSBytes(int count) 148 | { 149 | sbyte[] temp; 150 | 151 | temp = new sbyte[count]; 152 | FillBuffer(count, 1); 153 | 154 | unchecked 155 | { 156 | for (int i = 0; i < count; i++) 157 | { 158 | temp[i] = (sbyte)buffer[i]; 159 | } 160 | } 161 | 162 | return temp; 163 | } 164 | 165 | public char ReadChar(Encoding encoding) 166 | { 167 | int size; 168 | 169 | if (encoding == null) throw new ArgumentNullException("encoding"); 170 | 171 | size = GetEncodingSize(encoding); 172 | FillBuffer(size, size); 173 | return encoding.GetChars(buffer, 0, size)[0]; 174 | } 175 | 176 | public char[] ReadChars(Encoding encoding, int count) 177 | { 178 | int size; 179 | 180 | if (encoding == null) throw new ArgumentNullException("encoding"); 181 | 182 | size = GetEncodingSize(encoding); 183 | FillBuffer(size * count, size); 184 | return encoding.GetChars(buffer, 0, size * count); 185 | } 186 | 187 | private static int GetEncodingSize(Encoding encoding) 188 | { 189 | if (encoding == Encoding.UTF8 || encoding == Encoding.ASCII) 190 | return 1; 191 | else if (encoding == Encoding.Unicode || encoding == Encoding.BigEndianUnicode) 192 | return 2; 193 | 194 | return 1; 195 | } 196 | 197 | public string ReadStringNT(Encoding encoding) 198 | { 199 | string text; 200 | 201 | text = ""; 202 | 203 | do 204 | { 205 | text += ReadChar(encoding); 206 | } while (!text.EndsWith("\0", StringComparison.Ordinal)); 207 | 208 | return text.Remove(text.Length - 1); 209 | } 210 | 211 | public string ReadString(Encoding encoding, int count) 212 | { 213 | return new string(ReadChars(encoding, count)); 214 | } 215 | 216 | public double ReadDouble() 217 | { 218 | const int size = sizeof(double); 219 | FillBuffer(size, size); 220 | return BitConverter.ToDouble(buffer, 0); 221 | } 222 | 223 | public double[] ReadDoubles(int count) 224 | { 225 | const int size = sizeof(double); 226 | double[] temp; 227 | 228 | temp = new double[count]; 229 | FillBuffer(size * count, size); 230 | 231 | for (int i = 0; i < count; i++) 232 | { 233 | temp[i] = BitConverter.ToDouble(buffer, size * i); 234 | } 235 | return temp; 236 | } 237 | 238 | public Single ReadSingle() 239 | { 240 | const int size = sizeof(Single); 241 | FillBuffer(size, size); 242 | return BitConverter.ToSingle(buffer, 0); 243 | } 244 | 245 | public Single[] ReadSingles(int count) 246 | { 247 | const int size = sizeof(Single); 248 | Single[] temp; 249 | 250 | temp = new Single[count]; 251 | FillBuffer(size * count, size); 252 | 253 | for (int i = 0; i < count; i++) 254 | { 255 | temp[i] = BitConverter.ToSingle(buffer, size * i); 256 | } 257 | return temp; 258 | } 259 | 260 | public Int32 ReadInt32() 261 | { 262 | const int size = sizeof(Int32); 263 | FillBuffer(size, size); 264 | return BitConverter.ToInt32(buffer, 0); 265 | } 266 | 267 | public Int32[] ReadInt32s(int count) 268 | { 269 | const int size = sizeof(Int32); 270 | Int32[] temp; 271 | 272 | temp = new Int32[count]; 273 | FillBuffer(size * count, size); 274 | 275 | for (int i = 0; i < count; i++) 276 | { 277 | temp[i] = BitConverter.ToInt32(buffer, size * i); 278 | } 279 | return temp; 280 | } 281 | 282 | public Int64 ReadInt64() 283 | { 284 | const int size = sizeof(Int64); 285 | FillBuffer(size, size); 286 | return BitConverter.ToInt64(buffer, 0); 287 | } 288 | 289 | public Int64[] ReadInt64s(int count) 290 | { 291 | const int size = sizeof(Int64); 292 | Int64[] temp; 293 | 294 | temp = new Int64[count]; 295 | FillBuffer(size * count, size); 296 | 297 | for (int i = 0; i < count; i++) 298 | { 299 | temp[i] = BitConverter.ToInt64(buffer, size * i); 300 | } 301 | return temp; 302 | } 303 | 304 | public Int16 ReadInt16() 305 | { 306 | const int size = sizeof(Int16); 307 | FillBuffer(size, size); 308 | return BitConverter.ToInt16(buffer, 0); 309 | } 310 | 311 | public Int16[] ReadInt16s(int count) 312 | { 313 | const int size = sizeof(Int16); 314 | Int16[] temp; 315 | 316 | temp = new Int16[count]; 317 | FillBuffer(size * count, size); 318 | 319 | for (int i = 0; i < count; i++) 320 | { 321 | temp[i] = BitConverter.ToInt16(buffer, size * i); 322 | } 323 | return temp; 324 | } 325 | 326 | public UInt16 ReadUInt16() 327 | { 328 | const int size = sizeof(UInt16); 329 | FillBuffer(size, size); 330 | return BitConverter.ToUInt16(buffer, 0); 331 | } 332 | 333 | public UInt16[] ReadUInt16s(int count) 334 | { 335 | const int size = sizeof(UInt16); 336 | UInt16[] temp; 337 | 338 | temp = new UInt16[count]; 339 | FillBuffer(size * count, size); 340 | 341 | for (int i = 0; i < count; i++) 342 | { 343 | temp[i] = BitConverter.ToUInt16(buffer, size * i); 344 | } 345 | return temp; 346 | } 347 | 348 | public UInt32 ReadUInt32() 349 | { 350 | const int size = sizeof(UInt32); 351 | FillBuffer(size, size); 352 | return BitConverter.ToUInt32(buffer, 0); 353 | } 354 | 355 | public UInt32[] ReadUInt32s(int count) 356 | { 357 | const int size = sizeof(UInt32); 358 | UInt32[] temp; 359 | 360 | temp = new UInt32[count]; 361 | FillBuffer(size * count, size); 362 | 363 | for (int i = 0; i < count; i++) 364 | { 365 | temp[i] = BitConverter.ToUInt32(buffer, size * i); 366 | } 367 | return temp; 368 | } 369 | 370 | public UInt64 ReadUInt64() 371 | { 372 | const int size = sizeof(UInt64); 373 | FillBuffer(size, size); 374 | return BitConverter.ToUInt64(buffer, 0); 375 | } 376 | 377 | public UInt64[] ReadUInt64s(int count) 378 | { 379 | const int size = sizeof(UInt64); 380 | UInt64[] temp; 381 | 382 | temp = new UInt64[count]; 383 | FillBuffer(size * count, size); 384 | 385 | for (int i = 0; i < count; i++) 386 | { 387 | temp[i] = BitConverter.ToUInt64(buffer, size * i); 388 | } 389 | return temp; 390 | } 391 | 392 | private List> GetParser(Type type) 393 | { 394 | List> parser; 395 | 396 | /* A parser describes how to read in a type in an Endian 397 | * appropriate manner. Basically it describes as series of calls to 398 | * the Read* methods above to parse the structure. 399 | * The parser runs through each element in the list in order. If 400 | * the TypeCode is not Empty then it reads an array of values 401 | * according to the integer. Otherwise it skips a number of bytes. */ 402 | 403 | try 404 | { 405 | parser = parserCache[type]; 406 | } 407 | catch (KeyNotFoundException) 408 | { 409 | parser = new List>(); 410 | 411 | if (Endianness != SystemEndianness) 412 | { 413 | int pos, sz; 414 | 415 | pos = 0; 416 | foreach (var item in type.GetFields()) 417 | { 418 | int off = Marshal.OffsetOf(type, item.Name).ToInt32(); 419 | if (off != pos) 420 | { 421 | parser.Add(new Tuple(off - pos, TypeCode.Empty)); 422 | pos = off; 423 | } 424 | switch (Type.GetTypeCode(item.FieldType)) 425 | { 426 | case TypeCode.Byte: 427 | case TypeCode.SByte: 428 | pos += 1; 429 | parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); 430 | break; 431 | case TypeCode.Int16: 432 | case TypeCode.UInt16: 433 | pos += 2; 434 | parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); 435 | break; 436 | case TypeCode.Int32: 437 | case TypeCode.UInt32: 438 | case TypeCode.Single: 439 | pos += 4; 440 | parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); 441 | break; 442 | case TypeCode.Int64: 443 | case TypeCode.UInt64: 444 | case TypeCode.Double: 445 | pos += 8; 446 | parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); 447 | break; 448 | case TypeCode.Object: 449 | if (item.FieldType.IsArray) 450 | { 451 | /* array */ 452 | Type elementType; 453 | MarshalAsAttribute[] attrs; 454 | MarshalAsAttribute attr; 455 | 456 | attrs = (MarshalAsAttribute[])item.GetCustomAttributes(typeof(MarshalAsAttribute), false); 457 | if (attrs.Length != 1) 458 | throw new ArgumentException(String.Format("Field `{0}' is an array without a MarshalAs attribute.", item.Name), "type"); 459 | 460 | attr = attrs[0]; 461 | if (attr.Value != UnmanagedType.ByValArray) 462 | throw new ArgumentException(String.Format("Field `{0}' is not a ByValArray.", item.Name), "type"); 463 | 464 | elementType = item.FieldType.GetElementType(); 465 | switch (Type.GetTypeCode(elementType)) 466 | { 467 | case TypeCode.Byte: 468 | case TypeCode.SByte: 469 | pos += 1 * attr.SizeConst; 470 | parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); 471 | break; 472 | case TypeCode.Int16: 473 | case TypeCode.UInt16: 474 | pos += 2 * attr.SizeConst; 475 | parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); 476 | break; 477 | case TypeCode.Int32: 478 | case TypeCode.UInt32: 479 | case TypeCode.Single: 480 | pos += 4 * attr.SizeConst; 481 | parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); 482 | break; 483 | case TypeCode.Int64: 484 | case TypeCode.UInt64: 485 | case TypeCode.Double: 486 | pos += 8 * attr.SizeConst; 487 | parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); 488 | break; 489 | case TypeCode.Object: 490 | /* nested structure */ 491 | for (int i = 0; i < attr.SizeConst; i++) 492 | { 493 | pos += Marshal.SizeOf(elementType); 494 | parser.AddRange(GetParser(elementType)); 495 | } 496 | break; 497 | default: 498 | break; 499 | } 500 | } 501 | else 502 | { 503 | /* nested structure */ 504 | pos += Marshal.SizeOf(item.FieldType); 505 | parser.AddRange(GetParser(item.FieldType)); 506 | } 507 | break; 508 | default: 509 | throw new NotImplementedException(); 510 | } 511 | } 512 | 513 | sz = Marshal.SizeOf(type); 514 | if (sz != pos) 515 | { 516 | parser.Add(new Tuple(sz - pos, TypeCode.Empty)); 517 | } 518 | } 519 | else 520 | { 521 | int sz; 522 | 523 | sz = Marshal.SizeOf(type); 524 | parser.Add(new Tuple(sz, TypeCode.Byte)); 525 | } 526 | parserCache.Add(type, parser); 527 | } 528 | 529 | return parser; 530 | } 531 | 532 | private void RunParser(List> parser, BinaryWriter wr) 533 | { 534 | foreach (var item in parser) 535 | { 536 | /* Assumption: Types of the same size can be interchanged. */ 537 | switch (item.Item2) 538 | { 539 | case TypeCode.Byte: 540 | case TypeCode.SByte: 541 | wr.Write(ReadBytes(item.Item1), 0, item.Item1); 542 | break; 543 | case TypeCode.Int16: 544 | case TypeCode.UInt16: 545 | foreach (var val in ReadInt16s(item.Item1)) 546 | wr.Write(val); 547 | break; 548 | case TypeCode.Int32: 549 | case TypeCode.UInt32: 550 | case TypeCode.Single: 551 | foreach (var val in ReadInt32s(item.Item1)) 552 | wr.Write(val); 553 | break; 554 | case TypeCode.Int64: 555 | case TypeCode.UInt64: 556 | case TypeCode.Double: 557 | foreach (var val in ReadInt64s(item.Item1)) 558 | wr.Write(val); 559 | break; 560 | case TypeCode.Empty: 561 | BaseStream.Seek(item.Item1, SeekOrigin.Current); 562 | wr.BaseStream.Seek(item.Item1, SeekOrigin.Current); 563 | break; 564 | default: 565 | throw new NotImplementedException(); 566 | } 567 | } 568 | } 569 | 570 | public object ReadStructure(Type type) 571 | { 572 | List> parser; 573 | object result; 574 | 575 | parser = GetParser(type); 576 | 577 | using (var ms = new MemoryStream()) 578 | { 579 | using (var wr = new BinaryWriter(ms)) 580 | { 581 | RunParser(parser, wr); 582 | } 583 | result = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(ms.ToArray(), 0), type); 584 | } 585 | return result; 586 | } 587 | 588 | public Array ReadStructures(Type type, int count) 589 | { 590 | List> parser; 591 | Array result; 592 | 593 | parser = GetParser(type); 594 | 595 | result = Array.CreateInstance(type, count); 596 | 597 | using (var ms = new MemoryStream()) 598 | { 599 | using (var wr = new BinaryWriter(ms)) 600 | { 601 | for (int i = 0; i < count; i++) 602 | { 603 | ms.Seek(0, SeekOrigin.Begin); 604 | RunParser(parser, wr); 605 | result.SetValue(Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(ms.ToArray(), 0), type), i); 606 | } 607 | } 608 | } 609 | return result; 610 | } 611 | 612 | public void Close() 613 | { 614 | BaseStream.Close(); 615 | } 616 | 617 | public void Dispose() 618 | { 619 | Dispose(true); 620 | GC.SuppressFinalize(this); 621 | } 622 | 623 | private void Dispose(bool disposing) 624 | { 625 | if (!disposed) 626 | { 627 | if (disposing) 628 | { 629 | } 630 | 631 | BaseStream = null; 632 | buffer = null; 633 | 634 | disposed = true; 635 | } 636 | } 637 | } 638 | 639 | enum Endianness 640 | { 641 | BigEndian, 642 | LittleEndian, 643 | } 644 | } 645 | -------------------------------------------------------------------------------- /server/src/System/IO/EndianBinaryWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace System.IO 6 | { 7 | sealed class EndianBinaryWriter : IDisposable 8 | { 9 | private bool disposed; 10 | private byte[] buffer; 11 | 12 | private delegate void ArrayReverse(byte[] array, int count); 13 | private static readonly ArrayReverse[] fastReverse = new ArrayReverse[] { null, null, ArrayReverse2, null, ArrayReverse4, null, null, null, ArrayReverse8 }; 14 | 15 | private static Dictionary>> parserCache = new Dictionary>>(); 16 | 17 | public Stream BaseStream { get; private set; } 18 | public Endianness Endianness { get; set; } 19 | public static Endianness SystemEndianness { get { return BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; } } 20 | 21 | private bool Reverse { get { return SystemEndianness != Endianness; } } 22 | 23 | public EndianBinaryWriter(Stream baseStream) 24 | : this(baseStream, Endianness.BigEndian) 25 | { } 26 | 27 | public EndianBinaryWriter(Stream baseStream, Endianness endianness) 28 | { 29 | if (baseStream == null) throw new ArgumentNullException("baseStream"); 30 | if (!baseStream.CanWrite) throw new ArgumentException("base stream is not writeable", "baseStream"); 31 | 32 | BaseStream = baseStream; 33 | Endianness = endianness; 34 | } 35 | 36 | ~EndianBinaryWriter() 37 | { 38 | Dispose(false); 39 | } 40 | 41 | private void WriteBuffer(int bytes, int stride) 42 | { 43 | if (Reverse) 44 | { 45 | if (fastReverse[stride] != null) 46 | fastReverse[stride](buffer, bytes); 47 | else 48 | for (int i = 0; i < bytes; i += stride) 49 | { 50 | Array.Reverse(buffer, i, stride); 51 | } 52 | } 53 | 54 | BaseStream.Write(buffer, 0, bytes); 55 | } 56 | 57 | private static void ArrayReverse2(byte[] array, int arrayLength) 58 | { 59 | byte temp; 60 | 61 | while (arrayLength > 0) 62 | { 63 | temp = array[arrayLength - 2]; 64 | array[arrayLength - 2] = array[arrayLength - 1]; 65 | array[arrayLength - 1] = temp; 66 | arrayLength -= 2; 67 | } 68 | } 69 | 70 | private static void ArrayReverse4(byte[] array, int arrayLength) 71 | { 72 | byte temp; 73 | 74 | while (arrayLength > 0) 75 | { 76 | temp = array[arrayLength - 3]; 77 | array[arrayLength - 3] = array[arrayLength - 2]; 78 | array[arrayLength - 2] = temp; 79 | temp = array[arrayLength - 4]; 80 | array[arrayLength - 4] = array[arrayLength - 1]; 81 | array[arrayLength - 1] = temp; 82 | arrayLength -= 4; 83 | } 84 | } 85 | 86 | private static void ArrayReverse8(byte[] array, int arrayLength) 87 | { 88 | byte temp; 89 | 90 | while (arrayLength > 0) 91 | { 92 | temp = array[arrayLength - 5]; 93 | array[arrayLength - 5] = array[arrayLength - 4]; 94 | array[arrayLength - 4] = temp; 95 | temp = array[arrayLength - 6]; 96 | array[arrayLength - 6] = array[arrayLength - 3]; 97 | array[arrayLength - 3] = temp; 98 | temp = array[arrayLength - 7]; 99 | array[arrayLength - 7] = array[arrayLength - 2]; 100 | array[arrayLength - 2] = temp; 101 | temp = array[arrayLength - 8]; 102 | array[arrayLength - 8] = array[arrayLength - 1]; 103 | array[arrayLength - 1] = temp; 104 | arrayLength -= 8; 105 | } 106 | } 107 | 108 | private void CreateBuffer(int size) 109 | { 110 | if (buffer == null || buffer.Length < size) 111 | buffer = new byte[size]; 112 | } 113 | 114 | public void Write(byte value) 115 | { 116 | CreateBuffer(1); 117 | buffer[0] = value; 118 | WriteBuffer(1, 1); 119 | } 120 | 121 | public void Write(byte[] value, int offset, int count) 122 | { 123 | CreateBuffer(count); 124 | Array.Copy(value, offset, buffer, 0, count); 125 | WriteBuffer(count, 1); 126 | } 127 | 128 | public void Write(sbyte value) 129 | { 130 | CreateBuffer(1); 131 | unchecked 132 | { 133 | buffer[0] = (byte)value; 134 | } 135 | WriteBuffer(1, 1); 136 | } 137 | 138 | public void Write(sbyte[] value, int offset, int count) 139 | { 140 | CreateBuffer(count); 141 | 142 | unchecked 143 | { 144 | for (int i = 0; i < count; i++) 145 | { 146 | buffer[i] = (byte)value[i + offset]; 147 | } 148 | } 149 | 150 | WriteBuffer(count, 1); 151 | } 152 | 153 | public void Write(char value, Encoding encoding) 154 | { 155 | int size; 156 | 157 | if (encoding == null) throw new ArgumentNullException("encoding"); 158 | 159 | size = GetEncodingSize(encoding); 160 | CreateBuffer(size); 161 | Array.Copy(encoding.GetBytes(new string(value, 1)), 0, buffer, 0, size); 162 | WriteBuffer(size, size); 163 | } 164 | 165 | public void Write(char[] value, int offset, int count, Encoding encoding) 166 | { 167 | int size; 168 | 169 | if (encoding == null) throw new ArgumentNullException("encoding"); 170 | 171 | size = GetEncodingSize(encoding); 172 | CreateBuffer(size * count); 173 | Array.Copy(encoding.GetBytes(value, offset, count), 0, buffer, 0, count * size); 174 | WriteBuffer(size * count, size); 175 | } 176 | 177 | private static int GetEncodingSize(Encoding encoding) 178 | { 179 | if (encoding == Encoding.UTF8 || encoding == Encoding.ASCII) 180 | return 1; 181 | else if (encoding == Encoding.Unicode || encoding == Encoding.BigEndianUnicode) 182 | return 2; 183 | 184 | return 1; 185 | } 186 | 187 | public void Write(string value,Encoding encoding, bool nullTerminated) 188 | { 189 | Write(value.ToCharArray(), 0, value.Length, encoding); 190 | if (nullTerminated) 191 | Write('\0', encoding); 192 | } 193 | 194 | public void Write(double value) 195 | { 196 | const int size = sizeof(double); 197 | 198 | CreateBuffer(size); 199 | Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); 200 | WriteBuffer(size, size); 201 | } 202 | 203 | public void Write(double[] value, int offset, int count) 204 | { 205 | const int size = sizeof(double); 206 | 207 | CreateBuffer(size * count); 208 | for (int i = 0; i < count; i++) 209 | { 210 | Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); 211 | } 212 | 213 | WriteBuffer(size * count, size); 214 | } 215 | 216 | public void Write(Single value) 217 | { 218 | const int size = sizeof(Single); 219 | 220 | CreateBuffer(size); 221 | Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); 222 | WriteBuffer(size, size); 223 | } 224 | 225 | public void Write(Single[] value, int offset, int count) 226 | { 227 | const int size = sizeof(Single); 228 | 229 | CreateBuffer(size * count); 230 | for (int i = 0; i < count; i++) 231 | { 232 | Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); 233 | } 234 | 235 | WriteBuffer(size * count, size); 236 | } 237 | 238 | public void Write(Int32 value) 239 | { 240 | const int size = sizeof(Int32); 241 | 242 | CreateBuffer(size); 243 | Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); 244 | WriteBuffer(size, size); 245 | } 246 | 247 | public void Write(Int32[] value, int offset, int count) 248 | { 249 | const int size = sizeof(Int32); 250 | 251 | CreateBuffer(size * count); 252 | for (int i = 0; i < count; i++) 253 | { 254 | Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); 255 | } 256 | 257 | WriteBuffer(size * count, size); 258 | } 259 | 260 | public void Write(Int64 value) 261 | { 262 | const int size = sizeof(Int64); 263 | 264 | CreateBuffer(size); 265 | Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); 266 | WriteBuffer(size, size); 267 | } 268 | 269 | public void Write(Int64[] value, int offset, int count) 270 | { 271 | const int size = sizeof(Int64); 272 | 273 | CreateBuffer(size * count); 274 | for (int i = 0; i < count; i++) 275 | { 276 | Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); 277 | } 278 | 279 | WriteBuffer(size * count, size); 280 | } 281 | 282 | public void Write(Int16 value) 283 | { 284 | const int size = sizeof(Int16); 285 | 286 | CreateBuffer(size); 287 | Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); 288 | WriteBuffer(size, size); 289 | } 290 | 291 | public void Write(Int16[] value, int offset, int count) 292 | { 293 | const int size = sizeof(Int16); 294 | 295 | CreateBuffer(size * count); 296 | for (int i = 0; i < count; i++) 297 | { 298 | Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); 299 | } 300 | 301 | WriteBuffer(size * count, size); 302 | } 303 | 304 | public void Write(UInt16 value) 305 | { 306 | const int size = sizeof(UInt16); 307 | 308 | CreateBuffer(size); 309 | Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); 310 | WriteBuffer(size, size); 311 | } 312 | 313 | public void Write(UInt16[] value, int offset, int count) 314 | { 315 | const int size = sizeof(UInt16); 316 | 317 | CreateBuffer(size * count); 318 | for (int i = 0; i < count; i++) 319 | { 320 | Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); 321 | } 322 | 323 | WriteBuffer(size * count, size); 324 | } 325 | 326 | public void Write(UInt32 value) 327 | { 328 | const int size = sizeof(UInt32); 329 | 330 | CreateBuffer(size); 331 | Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); 332 | WriteBuffer(size, size); 333 | } 334 | 335 | public void Write(UInt32[] value, int offset, int count) 336 | { 337 | const int size = sizeof(UInt32); 338 | 339 | CreateBuffer(size * count); 340 | for (int i = 0; i < count; i++) 341 | { 342 | Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); 343 | } 344 | 345 | WriteBuffer(size * count, size); 346 | } 347 | 348 | public void Write(UInt64 value) 349 | { 350 | const int size = sizeof(UInt64); 351 | 352 | CreateBuffer(size); 353 | Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); 354 | WriteBuffer(size, size); 355 | } 356 | 357 | public void Write(UInt64[] value, int offset, int count) 358 | { 359 | const int size = sizeof(UInt64); 360 | 361 | CreateBuffer(size * count); 362 | for (int i = 0; i < count; i++) 363 | { 364 | Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); 365 | } 366 | 367 | WriteBuffer(size * count, size); 368 | } 369 | 370 | public void WritePadding(int multiple, byte padding) 371 | { 372 | int length = (int)(BaseStream.Position % multiple); 373 | 374 | if (length != 0) 375 | while (length != multiple) 376 | { 377 | BaseStream.WriteByte(padding); 378 | length++; 379 | } 380 | } 381 | 382 | public void WritePadding(int multiple, byte padding, long from, int offset) 383 | { 384 | int length = (int)((BaseStream.Position - from) % multiple); 385 | length = (length + offset) % multiple; 386 | 387 | if (length != 0) 388 | while (length != multiple) 389 | { 390 | BaseStream.WriteByte(padding); 391 | length++; 392 | } 393 | } 394 | 395 | private List> GetParser(Type type) 396 | { 397 | List> parser; 398 | 399 | /* A parser describes how to read in a type in an Endian 400 | * appropriate manner. Basically it describes as series of calls to 401 | * the Read* methods above to parse the structure. 402 | * The parser runs through each element in the list in order. If 403 | * the TypeCode is not Empty then it reads an array of values 404 | * according to the integer. Otherwise it skips a number of bytes. */ 405 | 406 | try 407 | { 408 | parser = parserCache[type]; 409 | } 410 | catch (KeyNotFoundException) 411 | { 412 | parser = new List>(); 413 | 414 | if (Endianness != SystemEndianness) 415 | { 416 | int pos, sz; 417 | 418 | pos = 0; 419 | foreach (var item in type.GetFields()) 420 | { 421 | int off = Marshal.OffsetOf(type, item.Name).ToInt32(); 422 | if (off != pos) 423 | { 424 | parser.Add(new Tuple(off - pos, TypeCode.Empty)); 425 | pos = off; 426 | } 427 | switch (Type.GetTypeCode(item.FieldType)) 428 | { 429 | case TypeCode.Byte: 430 | case TypeCode.SByte: 431 | pos += 1; 432 | parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); 433 | break; 434 | case TypeCode.Int16: 435 | case TypeCode.UInt16: 436 | pos += 2; 437 | parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); 438 | break; 439 | case TypeCode.Int32: 440 | case TypeCode.UInt32: 441 | case TypeCode.Single: 442 | pos += 4; 443 | parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); 444 | break; 445 | case TypeCode.Int64: 446 | case TypeCode.UInt64: 447 | case TypeCode.Double: 448 | pos += 8; 449 | parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); 450 | break; 451 | case TypeCode.Object: 452 | if (item.FieldType.IsArray) 453 | { 454 | /* array */ 455 | Type elementType; 456 | MarshalAsAttribute[] attrs; 457 | MarshalAsAttribute attr; 458 | 459 | attrs = (MarshalAsAttribute[])item.GetCustomAttributes(typeof(MarshalAsAttribute), false); 460 | if (attrs.Length != 1) 461 | throw new ArgumentException(String.Format("Field `{0}' is an array without a MarshalAs attribute.", item.Name), "type"); 462 | 463 | attr = attrs[0]; 464 | if (attr.Value != UnmanagedType.ByValArray) 465 | throw new ArgumentException(String.Format("Field `{0}' is not a ByValArray.", item.Name), "type"); 466 | 467 | elementType = item.FieldType.GetElementType(); 468 | switch (Type.GetTypeCode(elementType)) 469 | { 470 | case TypeCode.Byte: 471 | case TypeCode.SByte: 472 | pos += 1 * attr.SizeConst; 473 | parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); 474 | break; 475 | case TypeCode.Int16: 476 | case TypeCode.UInt16: 477 | pos += 2 * attr.SizeConst; 478 | parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); 479 | break; 480 | case TypeCode.Int32: 481 | case TypeCode.UInt32: 482 | case TypeCode.Single: 483 | pos += 4 * attr.SizeConst; 484 | parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); 485 | break; 486 | case TypeCode.Int64: 487 | case TypeCode.UInt64: 488 | case TypeCode.Double: 489 | pos += 8 * attr.SizeConst; 490 | parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); 491 | break; 492 | case TypeCode.Object: 493 | /* nested structure */ 494 | for (int i = 0; i < attr.SizeConst; i++) 495 | { 496 | pos += Marshal.SizeOf(elementType); 497 | parser.AddRange(GetParser(elementType)); 498 | } 499 | break; 500 | default: 501 | break; 502 | } 503 | } 504 | else 505 | { 506 | /* nested structure */ 507 | pos += Marshal.SizeOf(item.FieldType); 508 | parser.AddRange(GetParser(item.FieldType)); 509 | } 510 | break; 511 | default: 512 | throw new NotImplementedException(); 513 | } 514 | } 515 | 516 | sz = Marshal.SizeOf(type); 517 | if (sz != pos) 518 | { 519 | parser.Add(new Tuple(sz - pos, TypeCode.Empty)); 520 | } 521 | } 522 | else 523 | { 524 | int sz; 525 | 526 | sz = Marshal.SizeOf(type); 527 | parser.Add(new Tuple(sz, TypeCode.Byte)); 528 | } 529 | parserCache.Add(type, parser); 530 | } 531 | 532 | return parser; 533 | } 534 | 535 | private void RunParser(List> parser, BinaryReader rd) 536 | { 537 | foreach (var item in parser) 538 | { 539 | /* Assumption: Types of the same size can be interchanged. */ 540 | switch (item.Item2) 541 | { 542 | case TypeCode.Byte: 543 | case TypeCode.SByte: 544 | Write(rd.ReadBytes(item.Item1), 0, item.Item1); 545 | break; 546 | case TypeCode.Int16: 547 | case TypeCode.UInt16: 548 | for (int i = 0; i < item.Item1; i++) 549 | Write(rd.ReadInt16()); 550 | break; 551 | case TypeCode.Int32: 552 | case TypeCode.UInt32: 553 | case TypeCode.Single: 554 | for (int i = 0; i < item.Item1; i++) 555 | Write(rd.ReadInt32()); 556 | break; 557 | case TypeCode.Int64: 558 | case TypeCode.UInt64: 559 | case TypeCode.Double: 560 | for (int i = 0; i < item.Item1; i++) 561 | Write(rd.ReadInt64()); 562 | break; 563 | case TypeCode.Empty: 564 | rd.BaseStream.Seek(item.Item1, SeekOrigin.Current); 565 | BaseStream.Seek(item.Item1, SeekOrigin.Current); 566 | break; 567 | default: 568 | throw new NotImplementedException(); 569 | } 570 | } 571 | } 572 | 573 | public void Write(object structure) 574 | { 575 | List> parser; 576 | Type type; 577 | byte[] data; 578 | 579 | type = structure.GetType(); 580 | parser = GetParser(type); 581 | data = new byte[Marshal.SizeOf(type)]; 582 | 583 | using (var ms = new MemoryStream(data)) 584 | { 585 | using (var rd = new BinaryReader(ms)) 586 | { 587 | Marshal.StructureToPtr(structure, Marshal.UnsafeAddrOfPinnedArrayElement(data, 0), true); 588 | RunParser(parser, rd); 589 | } 590 | } 591 | } 592 | 593 | public void Write(Array structures) 594 | { 595 | List> parser; 596 | Type type; 597 | byte[] data; 598 | 599 | type = structures.GetType().GetElementType(); 600 | parser = GetParser(type); 601 | data = new byte[Marshal.SizeOf(type)]; 602 | 603 | using (var ms = new MemoryStream(data)) 604 | { 605 | using (var rd = new BinaryReader(ms)) 606 | { 607 | foreach (var structure in structures) 608 | { 609 | ms.Seek(0, SeekOrigin.Begin); 610 | Marshal.StructureToPtr(structure, Marshal.UnsafeAddrOfPinnedArrayElement(data, 0), true); 611 | RunParser(parser, rd); 612 | } 613 | } 614 | } 615 | } 616 | 617 | public void Close() 618 | { 619 | BaseStream.Close(); 620 | } 621 | 622 | public void Dispose() 623 | { 624 | Dispose(true); 625 | GC.SuppressFinalize(this); 626 | } 627 | 628 | private void Dispose(bool disposing) 629 | { 630 | if (!disposed) 631 | { 632 | if (disposing) 633 | { 634 | } 635 | 636 | BaseStream = null; 637 | buffer = null; 638 | 639 | disposed = true; 640 | } 641 | } 642 | } 643 | } 644 | -------------------------------------------------------------------------------- /server/src/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /server/src/cafiine_server.v12.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/server/src/cafiine_server.v12.suo -------------------------------------------------------------------------------- /server/src/obj/x86/Debug/saviine_server.DumpDialog.resources: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/server/src/obj/x86/Debug/saviine_server.DumpDialog.resources -------------------------------------------------------------------------------- /server/src/obj/x86/Debug/saviine_server.SaveSelectorDialog.resources: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/server/src/obj/x86/Debug/saviine_server.SaveSelectorDialog.resources -------------------------------------------------------------------------------- /server/src/saviine_server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {84FB17D5-D67B-4B9E-9041-00424036BF2E} 9 | Exe 10 | Properties 11 | saviine_server 12 | saviine_server 13 | v4.0 14 | Client 15 | 512 16 | publish\ 17 | true 18 | Disk 19 | false 20 | Foreground 21 | 7 22 | Days 23 | false 24 | false 25 | true 26 | 0 27 | 1.0.0.%2a 28 | false 29 | false 30 | true 31 | 32 | 33 | x86 34 | true 35 | full 36 | false 37 | bin\ 38 | DEBUG;TRACE 39 | prompt 40 | 4 41 | 42 | 43 | x86 44 | pdbonly 45 | true 46 | bin\Release\ 47 | TRACE 48 | prompt 49 | 4 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Form 62 | 63 | 64 | DumpDialog.cs 65 | 66 | 67 | Form 68 | 69 | 70 | SaveSelectorDialog.cs 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | False 83 | Microsoft .NET Framework 4 Client Profile %28x86 und x64%29 84 | true 85 | 86 | 87 | False 88 | .NET Framework 3.5 SP1 Client Profile 89 | false 90 | 91 | 92 | False 93 | .NET Framework 3.5 SP1 94 | false 95 | 96 | 97 | False 98 | Windows Installer 4.5 99 | true 100 | 101 | 102 | 103 | 104 | DumpDialog.cs 105 | 106 | 107 | SaveSelectorDialog.cs 108 | 109 | 110 | 111 | 118 | -------------------------------------------------------------------------------- /server/src/saviine_server.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | publish\ 5 | 6 | 7 | 8 | 9 | 10 | de-DE 11 | false 12 | 13 | -------------------------------------------------------------------------------- /server/src/saviine_server.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Express 2013 for Windows Desktop 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "saviine_server", "saviine_server.csproj", "{84FB17D5-D67B-4B9E-9041-00424036BF2E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x86 = Debug|x86 11 | Release|x86 = Release|x86 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {84FB17D5-D67B-4B9E-9041-00424036BF2E}.Debug|x86.ActiveCfg = Debug|x86 15 | {84FB17D5-D67B-4B9E-9041-00424036BF2E}.Debug|x86.Build.0 = Debug|x86 16 | {84FB17D5-D67B-4B9E-9041-00424036BF2E}.Release|x86.ActiveCfg = Release|x86 17 | {84FB17D5-D67B-4B9E-9041-00424036BF2E}.Release|x86.Build.0 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /server/src/saviine_server.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/server/src/saviine_server.suo -------------------------------------------------------------------------------- /server/src/saviine_server.v12.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Maschell/saviine/c06e6ae7f3e2aa23d554b2bd4766169025fecf14/server/src/saviine_server.v12.suo -------------------------------------------------------------------------------- /src/common/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "os_defs.h" 9 | 10 | #define CAFE_OS_SD_PATH "/vol/external01" 11 | #define SD_PATH "sd:" 12 | #define WIIU_PATH "/wiiu" 13 | 14 | /* Macros for libs */ 15 | #define LIB_CORE_INIT 0 16 | #define LIB_NSYSNET 1 17 | #define LIB_GX2 2 18 | // none dynamic libs 19 | #define LIB_LOADER 0x1001 20 | 21 | 22 | #ifndef MEM_BASE 23 | #define MEM_BASE (0x00800000) 24 | #endif 25 | 26 | #define ELF_DATA_ADDR (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x00)) 27 | #define ELF_DATA_SIZE (*(volatile unsigned int*)(MEM_BASE + 0x1300 + 0x04)) 28 | #define MAIN_ENTRY_ADDR (*(volatile unsigned int*)(MEM_BASE + 0x1400 + 0x00)) 29 | #define OS_FIRMWARE (*(volatile unsigned int*)(MEM_BASE + 0x1400 + 0x04)) 30 | 31 | #define OS_SPECIFICS ((OsSpecifics*)(MEM_BASE + 0x1500)) 32 | 33 | #ifndef EXIT_SUCCESS 34 | #define EXIT_SUCCESS 0 35 | #endif 36 | #define EXIT_HBL_EXIT 0xFFFFFFFE 37 | #define EXIT_RELAUNCH_ON_LOAD 0xFFFFFFFD 38 | 39 | #define RESTORE_INSTR_MAGIC 0xC001C0DE 40 | #define RESTORE_INSTR_ADDR ((restore_instructions_t*)(MEM_BASE + 0x1600)) 41 | 42 | typedef struct _restore_instructions_t { 43 | unsigned int magic; 44 | unsigned int instr_count; 45 | struct { 46 | unsigned int addr; 47 | unsigned int instr; 48 | } data[0]; 49 | } restore_instructions_t; 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | 55 | #endif /* COMMON_H */ 56 | 57 | -------------------------------------------------------------------------------- /src/common/fs_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef FS_DEFS_H 2 | #define FS_DEFS_H 3 | 4 | #include "types.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | 11 | /* FS defines and types */ 12 | #define FS_MAX_LOCALPATH_SIZE 511 13 | #define FS_MAX_MOUNTPATH_SIZE 128 14 | #define FS_MAX_FULLPATH_SIZE (FS_MAX_LOCALPATH_SIZE + FS_MAX_MOUNTPATH_SIZE) 15 | #define FS_MAX_ARGPATH_SIZE FS_MAX_FULLPATH_SIZE 16 | 17 | #define FS_STATUS_OK 0 18 | #define FS_STATUS_EXISTS -5 19 | #define FS_STATUS_STORAGE_FULL -12 20 | #define FS_STATUS_JOURNAL_FULL -13 21 | 22 | #define FS_RET_UNSUPPORTED_CMD 0x0400 23 | #define FS_RET_NO_ERROR 0x0000 24 | #define FS_RET_ALL_ERROR (unsigned int)(-1) 25 | 26 | 27 | #define FS_STAT_FLAG_IS_DIRECTORY 0x80000000 28 | 29 | /* max length of file/dir name */ 30 | #define FS_MAX_ENTNAME_SIZE 256 31 | 32 | #define FS_SOURCETYPE_EXTERNAL 0 33 | #define FS_SOURCETYPE_HFIO 1 34 | #define FS_SOURCETYPE_HFIO 1 35 | 36 | #define FS_MOUNT_SOURCE_SIZE 0x300 37 | #define FS_CLIENT_SIZE 0x1700 38 | #define FS_CMD_BLOCK_SIZE 0xA80 39 | 40 | typedef struct 41 | { 42 | uint32_t flag; 43 | uint32_t permission; 44 | uint32_t owner_id; 45 | uint32_t group_id; 46 | uint32_t size; 47 | uint32_t alloc_size; 48 | uint64_t quota_size; 49 | uint32_t ent_id; 50 | uint64_t ctime; 51 | uint64_t mtime; 52 | uint8_t attributes[48]; 53 | } __attribute__((packed)) FSStat; 54 | 55 | typedef struct 56 | { 57 | FSStat stat; 58 | char name[FS_MAX_ENTNAME_SIZE]; 59 | } FSDirEntry; 60 | 61 | 62 | #ifdef __cplusplus 63 | } 64 | #endif 65 | 66 | #endif /* FS_DEFS_H */ 67 | 68 | -------------------------------------------------------------------------------- /src/common/kernel_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef __KERNEL_DEFS_H_ 2 | #define __KERNEL_DEFS_H_ 3 | 4 | #include "types.h" 5 | #include "fs_defs.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | // original structure in the kernel that is originally 0x1270 long 12 | typedef struct 13 | { 14 | uint32_t version_cos_xml; // version tag from cos.xml 15 | uint64_t os_version; // os_version from app.xml 16 | uint64_t title_id; // title_id tag from app.xml 17 | uint32_t app_type; // app_type tag from app.xml 18 | uint32_t cmdFlags; // unknown tag as it is always 0 (might be cmdFlags from cos.xml but i am not sure) 19 | char rpx_name[0x1000]; // rpx name from cos.xml 20 | uint32_t unknown2; // 0x050B8304 in mii maker and system menu (looks a bit like permissions complex that got masked!?) 21 | uint32_t unknown3[63]; // those were all zeros, but its probably connected with unknown2 22 | uint32_t max_size; // max_size in cos.xml which defines the maximum amount of memory reserved for the app 23 | uint32_t avail_size; // avail_size or codegen_size in cos.xml (seems to mostly be 0?) 24 | uint32_t codegen_size; // codegen_size or avail_size in cos.xml (seems to mostly be 0?) 25 | uint32_t codegen_core; // codegen_core in cos.xml (seems to mostly be 1?) 26 | uint32_t max_codesize; // max_codesize in cos.xml 27 | uint32_t overlay_arena; // overlay_arena in cos.xml 28 | uint32_t unknown4[59]; // all zeros it seems 29 | uint32_t default_stack0_size; // not sure because always 0 but very likely 30 | uint32_t default_stack1_size; // not sure because always 0 but very likely 31 | uint32_t default_stack2_size; // not sure because always 0 but very likely 32 | uint32_t default_redzone0_size; // not sure because always 0 but very likely 33 | uint32_t default_redzone1_size; // not sure because always 0 but very likely 34 | uint32_t default_redzone2_size; // not sure because always 0 but very likely 35 | uint32_t exception_stack0_size; // from cos.xml, 0x1000 on mii maker 36 | uint32_t exception_stack1_size; // from cos.xml, 0x1000 on mii maker 37 | uint32_t exception_stack2_size; // from cos.xml, 0x1000 on mii maker 38 | uint32_t sdk_version; // from app.xml, 20909 (0x51AD) on mii maker 39 | uint32_t title_version; // from app.xml, 0x32 on mii maker 40 | /* 41 | // --------------------------------------------------------------------------------------------------------------------------------------------- 42 | // the next part might be changing from title to title?! I don't think its important but nice to know maybe.... 43 | // --------------------------------------------------------------------------------------------------------------------------------------------- 44 | char mlc[4]; // string "mlc" on mii maker and sysmenu 45 | uint32_t unknown5[7]; // all zeros on mii maker and sysmenu 46 | uint32_t unknown6_one; // 0x01 on mii maker and sysmenu 47 | // --------------------------------------------------------------------------------------------------------------------------------------------- 48 | char ACP[4]; // string "ACP" on mii maker and sysmenu 49 | uint32_t unknown7[15]; // all zeros on mii maker and sysmenu 50 | uint32_t unknown8_5; // 0x05 on mii maker and sysmenu 51 | uint32_t unknown9_zero; // 0x00 on mii maker and sysmenu 52 | uint32_t unknown10_ptr; // 0xFF23DD0C pointer on mii maker and sysmenu 53 | // --------------------------------------------------------------------------------------------------------------------------------------------- 54 | char UVD[4]; // string "UVD" on mii maker and sysmenu 55 | uint32_t unknown11[15]; // all zeros on mii maker and sysmenu 56 | uint32_t unknown12_5; // 0x05 on mii maker and sysmenu 57 | uint32_t unknown13_zero; // 0x00 on mii maker and sysmenu 58 | uint32_t unknown14_ptr; // 0xFF23EFC8 pointer on mii maker and sysmenu 59 | // --------------------------------------------------------------------------------------------------------------------------------------------- 60 | char SND[4]; // string "SND" on mii maker and sysmenu 61 | uint32_t unknown15[15]; // all zeros on mii maker and sysmenu 62 | uint32_t unknown16_5; // 0x05 on mii maker and sysmenu 63 | uint32_t unknown17_zero; // 0x00 on mii maker and sysmenu 64 | uint32_t unknown18_ptr; // 0xFF23F014 pointer on mii maker and sysmenu 65 | // --------------------------------------------------------------------------------------------------------------------------------------------- 66 | uint32_t unknown19; // 0x02 on miimaker, 0x0F on system menu 67 | */ 68 | // after that only zeros follow 69 | } __attribute__((packed)) CosAppXmlInfo; 70 | 71 | 72 | // Our own cos/app.xml struct which uses only uses as much memory as really needed, since many things are just zeros in the above structure 73 | // This structure is only 0x64 bytes long + RPX name length (dynamic up to 0x1000 theoretically) 74 | typedef struct 75 | { 76 | uint32_t version_cos_xml; // version tag from cos.xml 77 | uint64_t os_version; // os_version from app.xml 78 | uint64_t title_id; // title_id tag from app.xml 79 | uint32_t app_type; // app_type tag from app.xml 80 | uint32_t cmdFlags; // unknown tag as it is always 0 (might be cmdFlags from cos.xml but i am not sure) 81 | uint32_t max_size; // max_size in cos.xml which defines the maximum amount of memory reserved for the app 82 | uint32_t avail_size; // avail_size or codegen_size in cos.xml (seems to mostly be 0?) 83 | uint32_t codegen_size; // codegen_size or avail_size in cos.xml (seems to mostly be 0?) 84 | uint32_t codegen_core; // codegen_core in cos.xml (seems to mostly be 1?) 85 | uint32_t max_codesize; // max_codesize in cos.xml 86 | uint32_t overlay_arena; // overlay_arena in cos.xml 87 | uint32_t default_stack0_size; // not sure because always 0 but very likely 88 | uint32_t default_stack1_size; // not sure because always 0 but very likely 89 | uint32_t default_stack2_size; // not sure because always 0 but very likely 90 | uint32_t default_redzone0_size; // not sure because always 0 but very likely 91 | uint32_t default_redzone1_size; // not sure because always 0 but very likely 92 | uint32_t default_redzone2_size; // not sure because always 0 but very likely 93 | uint32_t exception_stack0_size; // from cos.xml, 0x1000 on mii maker 94 | uint32_t exception_stack1_size; // from cos.xml, 0x1000 on mii maker 95 | uint32_t exception_stack2_size; // from cos.xml, 0x1000 on mii maker 96 | uint32_t sdk_version; // from app.xml, 20909 (0x51AD) on mii maker 97 | uint32_t title_version; // from app.xml, 0x32 on mii maker 98 | char rpx_name[FS_MAX_ENTNAME_SIZE]; // rpx name from cos.xml, length 256 as it can't get bigger from FS anyway 99 | } __attribute__((packed)) ReducedCosAppXmlInfo; 100 | 101 | typedef struct _bat_t 102 | { 103 | u32 h; 104 | u32 l; 105 | } bat_t; 106 | 107 | typedef struct _bat_table_t 108 | { 109 | bat_t bat[8]; 110 | } bat_table_t; 111 | 112 | #ifdef __cplusplus 113 | } 114 | #endif 115 | 116 | #endif // __KERNEL_DEFS_H_ 117 | -------------------------------------------------------------------------------- /src/common/loader_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOADER_DEFS_H_ 2 | #define __LOADER_DEFS_H_ 3 | 4 | #include "types.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | // struct holding the globals of the loader (there are actually more but we don't need others) 11 | typedef struct _loader_globals_t 12 | { 13 | int sgIsLoadingBuffer; 14 | int sgFileType; 15 | int sgProcId; 16 | int sgGotBytes; 17 | int sgFileOffset; 18 | int sgBufferNumber; 19 | int sgBounceError; 20 | char sgLoadName[0x1000]; 21 | } __attribute__((packed)) loader_globals_t; 22 | 23 | typedef struct _loader_globals_550_t 24 | { 25 | int sgFinishedLoadingBuffer; 26 | int sgFileType; 27 | int sgProcId; 28 | int sgGotBytes; 29 | int sgTotalBytes; 30 | int sgFileOffset; 31 | int sgBufferNumber; 32 | int sgBounceError; 33 | char sgLoadName[0x1000]; 34 | } __attribute__((packed)) loader_globals_550_t; 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif // __LOADER_DEFS_H_ 41 | -------------------------------------------------------------------------------- /src/common/os_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef __OS_DEFS_H_ 2 | #define __OS_DEFS_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | typedef struct _OsSpecifics 9 | { 10 | unsigned int addr_OSDynLoad_Acquire; 11 | unsigned int addr_OSDynLoad_FindExport; 12 | unsigned int addr_OSTitle_main_entry; 13 | 14 | unsigned int addr_KernSyscallTbl1; 15 | unsigned int addr_KernSyscallTbl2; 16 | unsigned int addr_KernSyscallTbl3; 17 | unsigned int addr_KernSyscallTbl4; 18 | unsigned int addr_KernSyscallTbl5; 19 | } OsSpecifics; 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif // __OS_DEFS_H_ 26 | -------------------------------------------------------------------------------- /src/common/types.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPES_H 2 | #define TYPES_H 3 | 4 | #include 5 | 6 | #endif /* TYPES_H */ 7 | 8 | -------------------------------------------------------------------------------- /src/entry.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dynamic_libs/os_functions.h" 3 | #include "dynamic_libs/sys_functions.h" 4 | #include "common/common.h" 5 | #include "utils/utils.h" 6 | #include "main.h" 7 | 8 | int __entry_menu(int argc, char **argv) 9 | { 10 | //! ******************************************************************* 11 | //! * Jump to our application * 12 | //! ******************************************************************* 13 | return Menu_Main(); 14 | } 15 | -------------------------------------------------------------------------------- /src/game/memory_area_table.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (C) 2015 Dimok 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | ****************************************************************************/ 17 | #ifndef _MEMORY_AREA_TABLE_H_ 18 | #define _MEMORY_AREA_TABLE_H_ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | /* Struct used to organize empty memory areas */ 25 | typedef struct _s_mem_area 26 | { 27 | unsigned int address; 28 | unsigned int size; 29 | struct _s_mem_area* next; 30 | } s_mem_area; 31 | 32 | void memoryInitAreaTable(); 33 | s_mem_area * memoryGetAreaTable(void); 34 | 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif // _MEMORY_AREA_TABLE_H_ 41 | -------------------------------------------------------------------------------- /src/game/rpx_rpl_table.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rpx_rpl_table.h" 3 | #include "kernel/kernel_functions.h" 4 | #include "common/common.h" 5 | 6 | //! static container holding our retain data 7 | static unsigned char ucRpxData[0xffff]; 8 | static int iRpxRplCount = 0; 9 | 10 | void rpxRplTableInit(void) 11 | { 12 | s_rpx_rpl *pRpxData = (s_rpx_rpl*)ucRpxData; 13 | //! initialize the RPL/RPX table first entry to zero + 1 byte for name zero termination 14 | //! just in case no RPL/RPX are found, though it wont boot then anyway 15 | memset(pRpxData, 0, sizeof(s_rpx_rpl) + 1); 16 | iRpxRplCount = 0; 17 | } 18 | 19 | s_rpx_rpl * rpxRplTableAddEntry(const char *name, int offset, int size, int is_rpx, int entry_index, s_mem_area* area) 20 | { 21 | // fill rpx/rpl entry 22 | s_rpx_rpl * rpx_rpl_data = (s_rpx_rpl *)(ucRpxData); 23 | // get to last entry 24 | while(rpx_rpl_data->next) { 25 | rpx_rpl_data = rpx_rpl_data->next; 26 | } 27 | 28 | // setup next entry on the previous one (only if it is not the first entry) 29 | if(entry_index > 0) { 30 | rpx_rpl_data->next = (s_rpx_rpl *)( ((u32)rpx_rpl_data) + sizeof(s_rpx_rpl) + strlen(rpx_rpl_data->name) + 1 ); 31 | rpx_rpl_data = rpx_rpl_data->next; 32 | } 33 | 34 | // setup current entry 35 | rpx_rpl_data->area = area; 36 | rpx_rpl_data->size = size; 37 | rpx_rpl_data->offset = offset; 38 | rpx_rpl_data->is_rpx = is_rpx; 39 | rpx_rpl_data->next = 0; 40 | strcpy(rpx_rpl_data->name, name); 41 | 42 | iRpxRplCount++; 43 | 44 | return rpx_rpl_data; 45 | } 46 | 47 | s_rpx_rpl* rpxRplTableGet(void) 48 | { 49 | return (s_rpx_rpl*)ucRpxData; 50 | } 51 | 52 | int rpxRplTableGetCount(void) 53 | { 54 | return iRpxRplCount; 55 | } 56 | 57 | s_mem_area *rpxRplTableGetNextFreeMemArea(u32 * mem_area_addr_start, u32 * mem_area_addr_end, u32 * mem_area_offset) 58 | { 59 | s_mem_area * mem_area = memoryGetAreaTable(); 60 | s_rpx_rpl *rpl_struct = rpxRplTableGet(); 61 | while(rpl_struct != 0) 62 | { 63 | // check if this entry was loaded into memory 64 | if(rpl_struct->size == 0) { 65 | // see if we find entries behind this one that was pre-loaded 66 | rpl_struct = rpl_struct->next; 67 | // entry was not loaded into memory -> skip it 68 | continue; 69 | } 70 | 71 | // this entry has been loaded to memory, remember it's area 72 | mem_area = rpl_struct->area; 73 | 74 | int rpl_size = rpl_struct->size; 75 | int rpl_offset = rpl_struct->offset; 76 | // find the end of the entry and switch between areas if needed 77 | while(mem_area && (u32)(rpl_offset + rpl_size) >= mem_area->size) 78 | { 79 | rpl_size -= mem_area->size - rpl_offset; 80 | rpl_offset = 0; 81 | mem_area = mem_area->next; 82 | } 83 | 84 | if(!mem_area) 85 | return NULL; 86 | 87 | // set new start, end and memory area offset 88 | *mem_area_addr_start = mem_area->address; 89 | *mem_area_addr_end = mem_area->address + mem_area->size; 90 | *mem_area_offset = rpl_offset + rpl_size; 91 | 92 | // see if we find entries behind this one that was pre-loaded 93 | rpl_struct = rpl_struct->next; 94 | } 95 | return mem_area; 96 | } 97 | 98 | int rpxRplCopyDataToMem(s_rpx_rpl *rpx_rpl_struct, u32 fileOffset, const u8 *data, u32 dataSize) 99 | { 100 | s_mem_area *mem_area = rpx_rpl_struct->area; 101 | u32 mem_area_addr_start = mem_area->address; 102 | u32 mem_area_addr_end = mem_area_addr_start + mem_area->size; 103 | u32 mem_area_offset = rpx_rpl_struct->offset; 104 | 105 | // add to offset 106 | mem_area_offset += fileOffset; 107 | 108 | // skip position to the end of the fill 109 | while ((mem_area_addr_start + mem_area_offset) >= mem_area_addr_end) // TODO: maybe >, not >= 110 | { 111 | // subtract what was in the offset left from last memory block 112 | mem_area_offset = (mem_area_addr_start + mem_area_offset) - mem_area_addr_end; 113 | mem_area = mem_area->next; 114 | if(!mem_area) 115 | return 0; 116 | 117 | mem_area_addr_start = mem_area->address; 118 | mem_area_addr_end = mem_area_addr_start + mem_area->size; 119 | } 120 | 121 | // copy to memory 122 | u32 copiedBytes = 0; 123 | while(copiedBytes < dataSize) 124 | { 125 | u32 blockSize = dataSize - copiedBytes; 126 | u32 mem_area_addr_dest = mem_area_addr_start + mem_area_offset; 127 | 128 | if((mem_area_addr_dest + blockSize) > mem_area_addr_end) 129 | blockSize = mem_area_addr_end - mem_area_addr_dest; 130 | 131 | if(blockSize == 0) 132 | { 133 | // Set next memory area 134 | mem_area = mem_area->next; 135 | if(!mem_area) 136 | return 0; 137 | 138 | mem_area_addr_start = mem_area->address; 139 | mem_area_addr_end = mem_area->address + mem_area->size; 140 | mem_area_offset = 0; 141 | continue; 142 | } 143 | 144 | SC0x25_KernelCopyData(mem_area_addr_dest, (u32)&data[copiedBytes], blockSize); 145 | mem_area_offset += blockSize; 146 | copiedBytes += blockSize; 147 | } 148 | 149 | return copiedBytes; 150 | } 151 | 152 | int rpxRplCopyDataFromMem(s_rpx_rpl *rpx_rpl_struct, u32 fileOffset, u8 *data, u32 dataSize) 153 | { 154 | s_mem_area *mem_area = rpx_rpl_struct->area; 155 | u32 mem_area_addr_start = mem_area->address; 156 | u32 mem_area_addr_end = mem_area_addr_start + mem_area->size; 157 | u32 mem_area_offset = rpx_rpl_struct->offset; 158 | 159 | if(fileOffset > rpx_rpl_struct->size) 160 | return 0; 161 | 162 | if((fileOffset + dataSize) > rpx_rpl_struct->size) 163 | dataSize = rpx_rpl_struct->size - fileOffset; 164 | 165 | // add to offset 166 | mem_area_offset += fileOffset; 167 | 168 | // skip position to the end of the fill 169 | while ((mem_area_addr_start + mem_area_offset) >= mem_area_addr_end) // TODO: maybe >, not >= 170 | { 171 | // subtract what was in the offset left from last memory block 172 | mem_area_offset = (mem_area_addr_start + mem_area_offset) - mem_area_addr_end; 173 | mem_area = mem_area->next; 174 | if(!mem_area) 175 | return 0; 176 | 177 | mem_area_addr_start = mem_area->address; 178 | mem_area_addr_end = mem_area_addr_start + mem_area->size; 179 | } 180 | 181 | // copy to memory 182 | u32 copiedBytes = 0; 183 | while(copiedBytes < dataSize) 184 | { 185 | u32 blockSize = dataSize - copiedBytes; 186 | u32 mem_area_addr_dest = mem_area_addr_start + mem_area_offset; 187 | 188 | if((mem_area_addr_dest + blockSize) > mem_area_addr_end) 189 | blockSize = mem_area_addr_end - mem_area_addr_dest; 190 | 191 | if(blockSize == 0) 192 | { 193 | // Set next memory area 194 | mem_area = mem_area->next; 195 | if(!mem_area) 196 | return 0; 197 | 198 | mem_area_addr_start = mem_area->address; 199 | mem_area_addr_end = mem_area->address + mem_area->size; 200 | mem_area_offset = 0; 201 | continue; 202 | } 203 | 204 | SC0x25_KernelCopyData((u32)&data[copiedBytes], mem_area_addr_dest, blockSize); 205 | mem_area_offset += blockSize; 206 | copiedBytes += blockSize; 207 | } 208 | 209 | return copiedBytes; 210 | } 211 | -------------------------------------------------------------------------------- /src/game/rpx_rpl_table.h: -------------------------------------------------------------------------------- 1 | #ifndef __RPX_ARRAY_H_ 2 | #define __RPX_ARRAY_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include "common/common.h" 10 | #include "memory_area_table.h" 11 | 12 | /* Struct used to organize rpx/rpl data in memory */ 13 | typedef struct _s_rpx_rpl 14 | { 15 | struct _s_rpx_rpl* next; 16 | s_mem_area* area; 17 | unsigned int offset; 18 | unsigned int size; 19 | unsigned char is_rpx; 20 | char name[0]; 21 | } s_rpx_rpl; 22 | 23 | void rpxRplTableInit(void); 24 | s_rpx_rpl* rpxRplTableAddEntry(const char *name, int offset, int size, int is_rpx, int entry_index, s_mem_area* area); 25 | s_rpx_rpl* rpxRplTableGet(void); 26 | int rpxRplTableGetCount(void); 27 | 28 | s_mem_area *rpxRplTableGetNextFreeMemArea(u32 * mem_area_addr_start, u32 * mem_area_addr_end, u32 * mem_area_offset); 29 | int rpxRplCopyDataToMem(s_rpx_rpl *rpx_rpl_struct, u32 fileOffset, const u8 *data, u32 dataSize); 30 | int rpxRplCopyDataFromMem(s_rpx_rpl *rpx_rpl_struct, u32 fileOffset, u8 *data, u32 dataSize); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/kernel/kernel_functions.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common/common.h" 3 | #include "common/kernel_defs.h" 4 | #include "kernel/kernel_functions.h" 5 | #include "kernel/syscalls.h" 6 | #include "saviine.h" 7 | 8 | /* our retain data */ 9 | ReducedCosAppXmlInfo cosAppXmlInfoStruct __attribute__((section(".data"))); 10 | /* 11 | * This function is a kernel hook function. It is called directly from kernel code at position 0xFFF18558. 12 | */ 13 | void my_PrepareTitle(CosAppXmlInfo *xmlKernelInfo) 14 | { 15 | /** 16 | * DBAT for access to our data region is setup at this point for the 0xC0000000 area. 17 | */ 18 | 19 | //! Copy all data from the XML info 20 | strncpy(cosAppXmlInfoStruct.rpx_name, xmlKernelInfo->rpx_name, FS_MAX_ENTNAME_SIZE); 21 | 22 | cosAppXmlInfoStruct.version_cos_xml = xmlKernelInfo->version_cos_xml; 23 | cosAppXmlInfoStruct.os_version = xmlKernelInfo->os_version; 24 | cosAppXmlInfoStruct.title_id = xmlKernelInfo->title_id; 25 | cosAppXmlInfoStruct.app_type = xmlKernelInfo->app_type; 26 | cosAppXmlInfoStruct.cmdFlags = xmlKernelInfo->cmdFlags; 27 | cosAppXmlInfoStruct.max_size = xmlKernelInfo->max_size; 28 | cosAppXmlInfoStruct.avail_size = xmlKernelInfo->avail_size; 29 | cosAppXmlInfoStruct.codegen_size = xmlKernelInfo->codegen_size; 30 | cosAppXmlInfoStruct.codegen_core = xmlKernelInfo->codegen_core; 31 | cosAppXmlInfoStruct.max_codesize = xmlKernelInfo->max_codesize; 32 | cosAppXmlInfoStruct.overlay_arena = xmlKernelInfo->overlay_arena; 33 | cosAppXmlInfoStruct.default_stack0_size = xmlKernelInfo->default_stack0_size; 34 | cosAppXmlInfoStruct.default_stack1_size = xmlKernelInfo->default_stack1_size; 35 | cosAppXmlInfoStruct.default_stack2_size = xmlKernelInfo->default_stack2_size; 36 | cosAppXmlInfoStruct.default_redzone0_size = xmlKernelInfo->default_redzone0_size; 37 | cosAppXmlInfoStruct.default_redzone1_size = xmlKernelInfo->default_redzone1_size; 38 | cosAppXmlInfoStruct.default_redzone2_size = xmlKernelInfo->default_redzone2_size; 39 | cosAppXmlInfoStruct.exception_stack0_size = xmlKernelInfo->exception_stack0_size; 40 | cosAppXmlInfoStruct.exception_stack1_size = xmlKernelInfo->exception_stack1_size; 41 | cosAppXmlInfoStruct.exception_stack2_size = xmlKernelInfo->exception_stack2_size; 42 | cosAppXmlInfoStruct.sdk_version = xmlKernelInfo->sdk_version; 43 | cosAppXmlInfoStruct.title_version = xmlKernelInfo->title_version; 44 | 45 | // on title switch reset the dumper 46 | ResetDumper(); 47 | } 48 | 49 | void SetupKernelCallback(void) 50 | { 51 | KernelSetupSyscalls(); 52 | } 53 | 54 | void KernelSetDBATs(bat_table_t * table) 55 | { 56 | SC0x36_KernelReadDBATs(table); 57 | bat_table_t bat_table_copy = *table; 58 | 59 | // try to use a free slot 60 | int iUse; 61 | for(iUse = 0; iUse < 7; iUse++) 62 | { 63 | // skip position 5 as it is our main DBAT for our code data 64 | if(iUse == 5) 65 | continue; 66 | 67 | if(bat_table_copy.bat[iUse].h == 0 || bat_table_copy.bat[iUse].l == 0) 68 | { 69 | break; 70 | } 71 | } 72 | 73 | bat_table_copy.bat[iUse].h = 0xC0001FFF; 74 | bat_table_copy.bat[iUse].l = 0x30000012; 75 | SC0x37_KernelWriteDBATs(&bat_table_copy); 76 | } 77 | 78 | void KernelRestoreDBATs(bat_table_t * table) 79 | { 80 | SC0x37_KernelWriteDBATs(table); 81 | } 82 | -------------------------------------------------------------------------------- /src/kernel/kernel_functions.h: -------------------------------------------------------------------------------- 1 | #ifndef __KERNEL_FUNCTIONS_H_ 2 | #define __KERNEL_FUNCTIONS_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "common/kernel_defs.h" 9 | #include "syscalls.h" 10 | 11 | extern ReducedCosAppXmlInfo cosAppXmlInfoStruct; 12 | 13 | void SetupKernelCallback(void); 14 | 15 | void KernelSetDBATs(bat_table_t * table); 16 | void KernelRestoreDBATs(bat_table_t * table); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | 22 | #endif // __KERNEL_FUNCTIONS_H_ 23 | -------------------------------------------------------------------------------- /src/kernel/kernel_hooks.S: -------------------------------------------------------------------------------- 1 | # This stuff may need a change in different kernel versions 2 | # This is only needed when launched directly through browser and not SD card. 3 | 4 | .section ".kernel_code" 5 | .globl SaveAndResetDataBATs_And_SRs_hook 6 | SaveAndResetDataBATs_And_SRs_hook: 7 | # setup CTR to the position we need to return to 8 | mflr r5 9 | mtctr r5 10 | # set link register to its original value 11 | mtlr r7 12 | # setup us a nice DBAT for our code data with same region as our code 13 | mfspr r5, 560 14 | mtspr 570, r5 15 | mfspr r5, 561 16 | mtspr 571, r5 17 | # restore the original kernel instructions that we replaced 18 | lwz r5, 0x34(r3) 19 | lwz r6, 0x38(r3) 20 | lwz r7, 0x3C(r3) 21 | lwz r8, 0x40(r3) 22 | lwz r9, 0x44(r3) 23 | lwz r10, 0x48(r3) 24 | lwz r11, 0x4C(r3) 25 | lwz r3, 0x50(r3) 26 | isync 27 | mtsr 7, r5 28 | # jump back to the position in kernel after our patch (from LR) 29 | bctr 30 | 31 | .extern my_PrepareTitle 32 | .globl my_PrepareTitle_hook 33 | my_PrepareTitle_hook: 34 | # store all registers on stack to avoid issues with the call to C functions 35 | stwu r1, -0x90(r1) 36 | # registers for our own usage 37 | # only need r31 and rest is from tests before, just leaving it for later tests 38 | stw r28, 0x20(r1) 39 | stw r29, 0x24(r1) 40 | stw r30, 0x28(r1) 41 | stw r31, 0x2C(r1) 42 | 43 | stw r3, 0x30(r1) 44 | stw r4, 0x34(r1) 45 | stw r5, 0x38(r1) 46 | stw r6, 0x3C(r1) 47 | stw r7, 0x40(r1) 48 | stw r8, 0x44(r1) 49 | stw r9, 0x48(r1) 50 | stw r10, 0x4C(r1) 51 | stw r11, 0x50(r1) 52 | stw r12, 0x54(r1) 53 | stw r13, 0x58(r1) 54 | stw r14, 0x5C(r1) 55 | stw r15, 0x60(r1) 56 | stw r16, 0x64(r1) 57 | stw r17, 0x68(r1) 58 | stw r18, 0x6C(r1) 59 | stw r19, 0x70(r1) 60 | stw r20, 0x74(r1) 61 | stw r21, 0x78(r1) 62 | stw r22, 0x7C(r1) 63 | 64 | # save original DBAT registers 65 | mfdbatu r28, 0 66 | mfdbatl r29, 0 67 | 68 | # setup access to our data memory range 69 | lis r3, 0xC000 70 | ori r3, r3, 0x1FFF 71 | mtdbatu 0, r3 72 | lis r3, 0x3000 73 | ori r3, r3, 0x0012 74 | mtdbatl 0, r3 75 | 76 | # memory barrier 77 | eieio 78 | isync 79 | 80 | # save the LR from where we came 81 | mflr r31 82 | 83 | # the cos.xml/app.xml structure is at the location 0x68 of r11 84 | # there are actually many places that can be hooked for it 85 | # e.g. 0xFFF16130 and r27 points to this structure 86 | addi r3, r11, 0x68 87 | 88 | bl my_PrepareTitle 89 | 90 | # restore original DBAT registers 91 | mtdbatu 0, r28 92 | mtdbatl 0, r29 93 | 94 | # memory barrier 95 | eieio 96 | isync 97 | 98 | # setup LR to jump back to kernel code 99 | mtlr r31 100 | 101 | # restore all original values of registers from stack 102 | lwz r28, 0x20(r1) 103 | lwz r29, 0x24(r1) 104 | lwz r30, 0x28(r1) 105 | lwz r31, 0x2C(r1) 106 | 107 | lwz r3, 0x30(r1) 108 | lwz r4, 0x34(r1) 109 | lwz r5, 0x38(r1) 110 | lwz r6, 0x3C(r1) 111 | lwz r7, 0x40(r1) 112 | lwz r8, 0x44(r1) 113 | lwz r9, 0x48(r1) 114 | lwz r10, 0x4C(r1) 115 | lwz r11, 0x50(r1) 116 | lwz r12, 0x54(r1) 117 | lwz r13, 0x58(r1) 118 | lwz r14, 0x5C(r1) 119 | lwz r15, 0x60(r1) 120 | lwz r16, 0x64(r1) 121 | lwz r17, 0x68(r1) 122 | lwz r18, 0x6C(r1) 123 | lwz r19, 0x70(r1) 124 | lwz r20, 0x74(r1) 125 | lwz r21, 0x78(r1) 126 | lwz r22, 0x7C(r1) 127 | 128 | # restore the stack 129 | addi r1, r1, 0x90 130 | 131 | # restore original instruction that we replaced in the kernel 132 | clrlwi r7, r12, 0 133 | 134 | # jump back 135 | blr 136 | -------------------------------------------------------------------------------- /src/kernel/syscalls.c: -------------------------------------------------------------------------------- 1 | #include "common/os_defs.h" 2 | #include "common/kernel_defs.h" 3 | #include "common/common.h" 4 | #include "dynamic_libs/os_functions.h" 5 | #include "utils/utils.h" 6 | #include "syscalls.h" 7 | 8 | extern void my_PrepareTitle_hook(void); 9 | 10 | static unsigned int origPrepareTitleInstr __attribute__((section(".data"))) = 0; 11 | 12 | static void KernelCopyData(unsigned int addr, unsigned int src, unsigned int len) 13 | { 14 | /* 15 | * Setup a DBAT access for our 0xC0800000 area and our 0xBC000000 area which hold our variables like GAME_LAUNCHED and our BSS/rodata section 16 | */ 17 | register int dbatu0, dbatl0, dbatu1, dbatl1; 18 | // save the original DBAT value 19 | asm volatile("mfdbatu %0, 0" : "=r" (dbatu0)); 20 | asm volatile("mfdbatl %0, 0" : "=r" (dbatl0)); 21 | asm volatile("mfdbatu %0, 1" : "=r" (dbatu1)); 22 | asm volatile("mfdbatl %0, 1" : "=r" (dbatl1)); 23 | 24 | // write our own DBATs into the array 25 | if( ((addr & 0xFFF00000) == 0xFFF00000) || ((src & 0xFFF00000) == 0xFFF00000) ) 26 | { 27 | // setup kernel code access 28 | unsigned int dbatu = 0; 29 | unsigned int dbatl = 0; 30 | 31 | if((src & 0xFFF00000) == 0xFFF00000) { 32 | dbatu = (src & 0xFFF00000) | 0x02; 33 | dbatl = (src & 0xFFF00000) | 0x32; 34 | } 35 | else { 36 | dbatu = (addr & 0xFFF00000) | 0x02; 37 | dbatl = (addr & 0xFFF00000) | 0x32; 38 | } 39 | 40 | if( ((addr & 0xFFF00000) != (dbatu0 & 0xFFF00000)) && ((src & 0xFFF00000) != (dbatu0 & 0xFFF00000)) ) 41 | { 42 | asm volatile("mtdbatu 0, %0" : : "r" (dbatu)); 43 | asm volatile("mtdbatl 0, %0" : : "r" (dbatl)); 44 | } 45 | else 46 | { 47 | asm volatile("mtdbatu 1, %0" : : "r" (dbatu)); 48 | asm volatile("mtdbatl 1, %0" : : "r" (dbatl)); 49 | } 50 | } 51 | else 52 | { 53 | asm volatile("mtdbatu 0, %0" : : "r" (0xC0001FFF)); 54 | asm volatile("mtdbatl 0, %0" : : "r" (0x30000012)); 55 | asm volatile("mtdbatu 1, %0" : : "r" (0xB0801FFF)); 56 | asm volatile("mtdbatl 1, %0" : : "r" (0x20800012)); 57 | } 58 | asm volatile("eieio; isync"); 59 | 60 | 61 | unsigned char *src_p = (unsigned char*)src; 62 | unsigned char *dst_p = (unsigned char*)addr; 63 | 64 | unsigned int i; 65 | for(i = 0; i < len; i++) 66 | { 67 | dst_p[i] = src_p[i]; 68 | } 69 | 70 | /* 71 | * Restore original DBAT value 72 | */ 73 | asm volatile("mtdbatu 0, %0" : : "r" (dbatu0)); 74 | asm volatile("mtdbatl 0, %0" : : "r" (dbatl0)); 75 | asm volatile("mtdbatu 1, %0" : : "r" (dbatu1)); 76 | asm volatile("mtdbatl 1, %0" : : "r" (dbatl1)); 77 | asm volatile("eieio; isync"); 78 | } 79 | 80 | static void KernelReadDBATs(bat_table_t * table) 81 | { 82 | u32 i = 0; 83 | 84 | asm volatile("eieio; isync"); 85 | 86 | asm volatile("mfspr %0, 536" : "=r" (table->bat[i].h)); 87 | asm volatile("mfspr %0, 537" : "=r" (table->bat[i].l)); 88 | i++; 89 | asm volatile("mfspr %0, 538" : "=r" (table->bat[i].h)); 90 | asm volatile("mfspr %0, 539" : "=r" (table->bat[i].l)); 91 | i++; 92 | asm volatile("mfspr %0, 540" : "=r" (table->bat[i].h)); 93 | asm volatile("mfspr %0, 541" : "=r" (table->bat[i].l)); 94 | i++; 95 | asm volatile("mfspr %0, 542" : "=r" (table->bat[i].h)); 96 | asm volatile("mfspr %0, 543" : "=r" (table->bat[i].l)); 97 | i++; 98 | 99 | asm volatile("mfspr %0, 568" : "=r" (table->bat[i].h)); 100 | asm volatile("mfspr %0, 569" : "=r" (table->bat[i].l)); 101 | i++; 102 | asm volatile("mfspr %0, 570" : "=r" (table->bat[i].h)); 103 | asm volatile("mfspr %0, 571" : "=r" (table->bat[i].l)); 104 | i++; 105 | asm volatile("mfspr %0, 572" : "=r" (table->bat[i].h)); 106 | asm volatile("mfspr %0, 573" : "=r" (table->bat[i].l)); 107 | i++; 108 | asm volatile("mfspr %0, 574" : "=r" (table->bat[i].h)); 109 | asm volatile("mfspr %0, 575" : "=r" (table->bat[i].l)); 110 | } 111 | 112 | static void KernelWriteDBATs(bat_table_t * table) 113 | { 114 | u32 i = 0; 115 | 116 | asm volatile("eieio; isync"); 117 | 118 | asm volatile("mtspr 536, %0" : : "r" (table->bat[i].h)); 119 | asm volatile("mtspr 537, %0" : : "r" (table->bat[i].l)); 120 | i++; 121 | asm volatile("mtspr 538, %0" : : "r" (table->bat[i].h)); 122 | asm volatile("mtspr 539, %0" : : "r" (table->bat[i].l)); 123 | i++; 124 | asm volatile("mtspr 540, %0" : : "r" (table->bat[i].h)); 125 | asm volatile("mtspr 541, %0" : : "r" (table->bat[i].l)); 126 | i++; 127 | asm volatile("mtspr 542, %0" : : "r" (table->bat[i].h)); 128 | asm volatile("mtspr 543, %0" : : "r" (table->bat[i].l)); 129 | i++; 130 | 131 | asm volatile("mtspr 568, %0" : : "r" (table->bat[i].h)); 132 | asm volatile("mtspr 569, %0" : : "r" (table->bat[i].l)); 133 | i++; 134 | asm volatile("mtspr 570, %0" : : "r" (table->bat[i].h)); 135 | asm volatile("mtspr 571, %0" : : "r" (table->bat[i].l)); 136 | i++; 137 | asm volatile("mtspr 572, %0" : : "r" (table->bat[i].h)); 138 | asm volatile("mtspr 573, %0" : : "r" (table->bat[i].l)); 139 | i++; 140 | asm volatile("mtspr 574, %0" : : "r" (table->bat[i].h)); 141 | asm volatile("mtspr 575, %0" : : "r" (table->bat[i].l)); 142 | 143 | asm volatile("eieio; isync"); 144 | } 145 | 146 | /* Write a 32-bit word with kernel permissions */ 147 | static void __attribute__ ((noinline)) kern_write(void *addr, uint32_t value) 148 | { 149 | asm volatile ( 150 | "li 3,1\n" 151 | "li 4,0\n" 152 | "mr 5,%1\n" 153 | "li 6,0\n" 154 | "li 7,0\n" 155 | "lis 8,1\n" 156 | "mr 9,%0\n" 157 | "mr %1,1\n" 158 | "li 0,0x3500\n" 159 | "sc\n" 160 | "nop\n" 161 | "mr 1,%1\n" 162 | : 163 | : "r"(addr), "r"(value) 164 | : "memory", "ctr", "lr", "0", "3", "4", "5", "6", "7", "8", "9", "10", 165 | "11", "12" 166 | ); 167 | } 168 | 169 | void KernelSetupSyscalls(void) 170 | { 171 | //! assign 1 so that this variable gets into the retained .data section 172 | static uint8_t ucSyscallsSetupRequired = 1; 173 | if(!ucSyscallsSetupRequired) 174 | return; 175 | 176 | ucSyscallsSetupRequired = 0; 177 | 178 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl1 + (0x36 * 4)), (unsigned int)KernelReadDBATs); 179 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl2 + (0x36 * 4)), (unsigned int)KernelReadDBATs); 180 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl3 + (0x36 * 4)), (unsigned int)KernelReadDBATs); 181 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl4 + (0x36 * 4)), (unsigned int)KernelReadDBATs); 182 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl5 + (0x36 * 4)), (unsigned int)KernelReadDBATs); 183 | 184 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl1 + (0x37 * 4)), (unsigned int)KernelWriteDBATs); 185 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl2 + (0x37 * 4)), (unsigned int)KernelWriteDBATs); 186 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl3 + (0x37 * 4)), (unsigned int)KernelWriteDBATs); 187 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl4 + (0x37 * 4)), (unsigned int)KernelWriteDBATs); 188 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl5 + (0x37 * 4)), (unsigned int)KernelWriteDBATs); 189 | 190 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl1 + (0x25 * 4)), (unsigned int)KernelCopyData); 191 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl2 + (0x25 * 4)), (unsigned int)KernelCopyData); 192 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl3 + (0x25 * 4)), (unsigned int)KernelCopyData); 193 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl4 + (0x25 * 4)), (unsigned int)KernelCopyData); 194 | kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl5 + (0x25 * 4)), (unsigned int)KernelCopyData); 195 | 196 | //! write our hook to the 197 | u32 addr_my_PrepareTitle_hook = ((u32)my_PrepareTitle_hook) | 0x48000003; 198 | 199 | SC0x25_KernelCopyData((u32)&origPrepareTitleInstr, (u32)addr_PrepareTitle_hook, 4); 200 | SC0x25_KernelCopyData((u32)addr_PrepareTitle_hook, (u32)&addr_my_PrepareTitle_hook, 4); 201 | } 202 | 203 | 204 | void KernelRestoreInstructions(void) 205 | { 206 | if(origPrepareTitleInstr != 0) 207 | SC0x25_KernelCopyData((u32)addr_PrepareTitle_hook, (u32)&origPrepareTitleInstr, 4); 208 | } 209 | -------------------------------------------------------------------------------- /src/kernel/syscalls.h: -------------------------------------------------------------------------------- 1 | #ifndef __SYSCALLS_H_ 2 | #define __SYSCALLS_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include "common/kernel_defs.h" 10 | 11 | void KernelSetupSyscalls(void); 12 | void KernelRestoreInstructions(void); 13 | 14 | void SC0x25_KernelCopyData(unsigned int addr, unsigned int src, unsigned int len); 15 | void SC0x36_KernelReadDBATs(bat_table_t * table); 16 | void SC0x37_KernelWriteDBATs(bat_table_t * table); 17 | 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | 23 | #endif // __KERNEL_FUNCTIONS_H_ 24 | -------------------------------------------------------------------------------- /src/kernel/syscalls_asm.S: -------------------------------------------------------------------------------- 1 | # Syscalls for kernel that we use 2 | 3 | .globl SC0x36_KernelReadDBATs 4 | SC0x36_KernelReadDBATs: 5 | li r0, 0x3600 6 | sc 7 | blr 8 | 9 | .globl SC0x37_KernelWriteDBATs 10 | SC0x37_KernelWriteDBATs: 11 | li r0, 0x3700 12 | sc 13 | blr 14 | 15 | .globl SC0x25_KernelCopyData 16 | SC0x25_KernelCopyData: 17 | li r0, 0x2500 18 | sc 19 | blr 20 | -------------------------------------------------------------------------------- /src/link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT(ddd.elf); 2 | 3 | /* Tell linker where our application entry is so the garbage collect can work correct */ 4 | ENTRY(__entry_menu); 5 | 6 | SECTIONS { 7 | . = 0x00802000; 8 | .text : { 9 | *(.kernel_code*); 10 | *(.text*); 11 | /* Tell linker to not garbage collect this section as it is not referenced anywhere */ 12 | KEEP(*(.kernel_code*)); 13 | } 14 | .rodata : { 15 | *(.rodata*); 16 | } 17 | .data : { 18 | *(.data*); 19 | 20 | __sdata_start = .; 21 | *(.sdata*); 22 | __sdata_end = .; 23 | 24 | __sdata2_start = .; 25 | *(.sdata2*); 26 | __sdata2_end = .; 27 | } 28 | .bss : { 29 | __bss_start = .; 30 | *(.bss*); 31 | *(.sbss*); 32 | *(COMMON); 33 | __bss_end = .; 34 | } 35 | __CODE_END = .; 36 | 37 | /DISCARD/ : { 38 | *(*); 39 | } 40 | } 41 | 42 | /******************************************************** FS ********************************************************/ 43 | /* coreinit.rpl difference in addresses 0xFE3C00 */ 44 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "dynamic_libs/os_functions.h" 7 | #include "dynamic_libs/fs_functions.h" 8 | #include "dynamic_libs/aoc_functions.h" 9 | #include "dynamic_libs/gx2_functions.h" 10 | #include "dynamic_libs/sys_functions.h" 11 | #include "dynamic_libs/vpad_functions.h" 12 | #include "dynamic_libs/padscore_functions.h" 13 | #include "dynamic_libs/socket_functions.h" 14 | #include "dynamic_libs/ax_functions.h" 15 | #include "system/memory.h" 16 | #include "utils/logger.h" 17 | #include "common/common.h" 18 | #include "game/rpx_rpl_table.h" 19 | #include "game/memory_area_table.h" 20 | #include "saviine.h" 21 | #include "patcher/function_hooks.h" 22 | #include "kernel/kernel_functions.h" 23 | 24 | typedef union u_serv_ip 25 | { 26 | uint8_t digit[4]; 27 | uint32_t full; 28 | } u_serv_ip; 29 | 30 | #define PRINT_TEXT2(x, y, ...) { snprintf(msg, 80, __VA_ARGS__); OSScreenPutFontEx(0, x, y, msg); OSScreenPutFontEx(1, x, y, msg); } 31 | 32 | /* Entry point */ 33 | int Menu_Main(void) 34 | { 35 | //!******************************************************************* 36 | //! Initialize function pointers * 37 | //!******************************************************************* 38 | //! do OS (for acquire) and sockets first so we got logging 39 | InitOSFunctionPointers(); 40 | InitSocketFunctionPointers(); 41 | InitFSFunctionPointers(); 42 | InitVPadFunctionPointers(); 43 | InitSysFunctionPointers(); 44 | InitAocFunctionPointers(); 45 | 46 | log_init("192.168.0.181"); 47 | log_deinit(); 48 | log_init("192.168.0.181"); 49 | log_printf("Started %s\n", cosAppXmlInfoStruct.rpx_name); 50 | 51 | if(strcasecmp("men.rpx", cosAppXmlInfoStruct.rpx_name) == 0) 52 | { 53 | return EXIT_RELAUNCH_ON_LOAD; 54 | } 55 | else if(strlen(cosAppXmlInfoStruct.rpx_name) > 0 && strcasecmp("ffl_app.rpx", cosAppXmlInfoStruct.rpx_name) != 0) 56 | { 57 | StartDumper(); 58 | return EXIT_RELAUNCH_ON_LOAD; 59 | } 60 | 61 | // initialize memory tables once on start 62 | memoryInitAreaTable(); 63 | rpxRplTableInit(); 64 | SetupKernelCallback(); 65 | PatchMethodHooks(); 66 | 67 | memoryInitialize(); 68 | 69 | VPADInit(); 70 | 71 | // Prepare screen 72 | int screen_buf0_size = 0; 73 | int screen_buf1_size = 0; 74 | 75 | // Init screen and screen buffers 76 | OSScreenInit(); 77 | screen_buf0_size = OSScreenGetBufferSizeEx(0); 78 | screen_buf1_size = OSScreenGetBufferSizeEx(1); 79 | 80 | unsigned char *screenBuffer = MEM1_alloc(screen_buf0_size + screen_buf1_size, 0x40); 81 | 82 | OSScreenSetBufferEx(0, screenBuffer); 83 | OSScreenSetBufferEx(1, (screenBuffer + screen_buf0_size)); 84 | 85 | OSScreenEnableEx(0, 1); 86 | OSScreenEnableEx(1, 1); 87 | 88 | char msg[80]; 89 | uint8_t sel_ip = 3; 90 | int launchMethod = 0; 91 | int update_screen = 1; 92 | int vpadError = -1; 93 | VPADData vpad_data; 94 | u_serv_ip ip; 95 | ip.full = GetServerIp(); 96 | int delay = 0; 97 | 98 | while (1) 99 | { 100 | // Read vpad 101 | VPADRead(0, &vpad_data, 1, &vpadError); 102 | 103 | if(update_screen) 104 | { 105 | OSScreenClearBufferEx(0, 0); 106 | OSScreenClearBufferEx(1, 0); 107 | 108 | // Print message 109 | PRINT_TEXT2(14, 1, "-- Saviine v1.1 by Maschell --"); 110 | PRINT_TEXT2(0, 5, "1. Setup IP address of server application."); 111 | 112 | // Print ip digit selector 113 | uint8_t x_shift = 17 + 4 * sel_ip; 114 | PRINT_TEXT2(x_shift, 6, "vvv"); 115 | 116 | PRINT_TEXT2(0, 7, " Server IP: %3d.%3d.%3d.%3d", ip.digit[0], ip.digit[1], ip.digit[2], ip.digit[3]); 117 | 118 | PRINT_TEXT2(0, 10, "2. Press A to install Saviine and try to launch disc."); 119 | PRINT_TEXT2(0, 11, " or Press X to install Saviine and return to system menu."); 120 | 121 | PRINT_TEXT2(0, 13, "3. Start the title to be dumped."); 122 | 123 | PRINT_TEXT2(0, 17, "Press home button to exit ..."); 124 | 125 | 126 | OSScreenFlipBuffersEx(0); 127 | OSScreenFlipBuffersEx(1); 128 | } 129 | 130 | u32 pressedBtns = vpad_data.btns_d | vpad_data.btns_h; 131 | 132 | // Check for buttons 133 | // Home Button 134 | if (pressedBtns & VPAD_BUTTON_HOME) { 135 | launchMethod = 0; 136 | break; 137 | } 138 | // A Button 139 | if (pressedBtns & VPAD_BUTTON_A) { 140 | SetServerIp(ip.full); 141 | launchMethod = 1; 142 | break; 143 | } 144 | // A Button 145 | if (pressedBtns & VPAD_BUTTON_X) { 146 | SetServerIp(ip.full); 147 | launchMethod = 2; 148 | break; 149 | } 150 | // Left/Right Buttons 151 | if (vpad_data.btns_d & VPAD_BUTTON_LEFT ) 152 | { 153 | if(sel_ip == 0) 154 | sel_ip = 3; 155 | else 156 | --sel_ip; 157 | } 158 | 159 | if (vpad_data.btns_d & VPAD_BUTTON_RIGHT) 160 | { 161 | sel_ip = ((sel_ip + 1) % 4); 162 | } 163 | 164 | // Up/Down Buttons 165 | if (pressedBtns & VPAD_BUTTON_UP) 166 | { 167 | if(--delay <= 0) { 168 | ip.digit[sel_ip]++; 169 | delay = (vpad_data.btns_d & VPAD_BUTTON_UP) ? 6 : 0; 170 | } 171 | } 172 | else if (pressedBtns & VPAD_BUTTON_DOWN) 173 | { 174 | if(--delay <= 0) { 175 | ip.digit[sel_ip]--; 176 | delay = (vpad_data.btns_d & VPAD_BUTTON_DOWN) ? 6 : 0; 177 | } 178 | } 179 | else { 180 | delay = 0; 181 | } 182 | 183 | // Button pressed ? 184 | update_screen = (pressedBtns & (VPAD_BUTTON_LEFT | VPAD_BUTTON_RIGHT | VPAD_BUTTON_UP | VPAD_BUTTON_DOWN)) ? 1 : 0; 185 | usleep(20000); 186 | } 187 | 188 | MEM1_free(screenBuffer); 189 | screenBuffer = NULL; 190 | 191 | log_deinit(); 192 | 193 | memoryRelease(); 194 | 195 | if(launchMethod == 0) 196 | { 197 | RestoreInstructions(); 198 | return EXIT_SUCCESS; 199 | } 200 | else if(launchMethod == 1) 201 | { 202 | char buf_vol_odd[20]; 203 | snprintf(buf_vol_odd, sizeof(buf_vol_odd), "%s", "/vol/storage_odd03"); 204 | _SYSLaunchTitleByPathFromLauncher(buf_vol_odd, 18, 0); 205 | } 206 | else 207 | { 208 | SYSLaunchMenu(); 209 | } 210 | 211 | return EXIT_RELAUNCH_ON_LOAD; 212 | } 213 | 214 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | #ifndef _MAIN_H_ 2 | #define _MAIN_H_ 3 | 4 | #include "common/types.h" 5 | #include "dynamic_libs/os_functions.h" 6 | 7 | /* Main */ 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | //! C wrapper for our C++ functions 13 | int Menu_Main(void); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/patcher/function_hooks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common/common.h" 5 | #include "common/fs_defs.h" 6 | #include "common/loader_defs.h" 7 | #include "game/rpx_rpl_table.h" 8 | #include "dynamic_libs/fs_functions.h" 9 | #include "dynamic_libs/os_functions.h" 10 | #include "kernel/kernel_functions.h" 11 | #include "function_hooks.h" 12 | #include "saviine.h" 13 | 14 | #define LIB_CODE_RW_BASE_OFFSET 0xC1000000 15 | #define CODE_RW_BASE_OFFSET 0x00000000 16 | 17 | #define USE_EXTRA_LOG_FUNCTIONS 0 18 | 19 | #define DECL(res, name, ...) \ 20 | res (* real_ ## name)(__VA_ARGS__) __attribute__((section(".data"))); \ 21 | res my_ ## name(__VA_ARGS__) 22 | 23 | /* ***************************************************************************** 24 | * Creates function pointer array 25 | * ****************************************************************************/ 26 | #define MAKE_MAGIC(x, lib) { (unsigned int) my_ ## x, (unsigned int) &real_ ## x, lib, # x } 27 | 28 | static const struct hooks_magic_t { 29 | const unsigned int replaceAddr; 30 | const unsigned int replaceCall; 31 | const unsigned int library; 32 | const char functionName[30]; 33 | } method_hooks[] = { 34 | // LOADER function 35 | //MAKE_MAGIC(LiWaitOneChunk, LIB_LOADER), 36 | 37 | }; 38 | 39 | //! buffer to store our 2 instructions needed for our replacements 40 | //! the code will be placed in the address of that buffer - CODE_RW_BASE_OFFSET 41 | //! avoid this buffer to be placed in BSS and reset on start up 42 | volatile unsigned int fs_method_calls[sizeof(method_hooks) / sizeof(struct hooks_magic_t) * 2] __attribute__((section(".data"))); 43 | 44 | void PatchMethodHooks(void) 45 | { 46 | restore_instructions_t * restore = (restore_instructions_t *)(RESTORE_INSTR_ADDR); 47 | //! check if it is already patched 48 | if(restore->magic == RESTORE_INSTR_MAGIC) 49 | return; 50 | 51 | restore->magic = RESTORE_INSTR_MAGIC; 52 | restore->instr_count = 0; 53 | 54 | bat_table_t table; 55 | KernelSetDBATs(&table); 56 | 57 | /* Patch branches to it. */ 58 | volatile unsigned int *space = &fs_method_calls[0]; 59 | 60 | int method_hooks_count = sizeof(method_hooks) / sizeof(struct hooks_magic_t); 61 | 62 | for(int i = 0; i < method_hooks_count; i++) 63 | { 64 | unsigned int repl_addr = (unsigned int)method_hooks[i].replaceAddr; 65 | unsigned int call_addr = (unsigned int)method_hooks[i].replaceCall; 66 | 67 | unsigned int real_addr = 0; 68 | 69 | if(strcmp(method_hooks[i].functionName, "OSDynLoad_Acquire") == 0) 70 | { 71 | memcpy(&real_addr, &OSDynLoad_Acquire, 4); 72 | } 73 | else if(strcmp(method_hooks[i].functionName, "LiWaitOneChunk") == 0) 74 | { 75 | memcpy(&real_addr, &addr_LiWaitOneChunk, 4); 76 | } 77 | else 78 | { 79 | OSDynLoad_FindExport(coreinit_handle, 0, method_hooks[i].functionName, &real_addr); 80 | } 81 | 82 | // fill the restore instruction section 83 | restore->data[restore->instr_count].addr = real_addr; 84 | restore->data[restore->instr_count].instr = *(volatile unsigned int *)(LIB_CODE_RW_BASE_OFFSET + real_addr); 85 | restore->instr_count++; 86 | 87 | // set pointer to the real function 88 | *(volatile unsigned int *)(call_addr) = (unsigned int)(space) - CODE_RW_BASE_OFFSET; 89 | DCFlushRange((void*)(call_addr), 4); 90 | 91 | // fill the instruction of the real function 92 | *space = *(volatile unsigned int*)(LIB_CODE_RW_BASE_OFFSET + real_addr); 93 | space++; 94 | 95 | // jump to real function skipping the first/replaced instruction 96 | *space = 0x48000002 | ((real_addr + 4) & 0x03fffffc); 97 | space++; 98 | DCFlushRange((void*)(space - 2), 8); 99 | ICInvalidateRange((unsigned char*)(space - 2) - CODE_RW_BASE_OFFSET, 8); 100 | 101 | unsigned int replace_instr = 0x48000002 | (repl_addr & 0x03fffffc); 102 | *(volatile unsigned int *)(LIB_CODE_RW_BASE_OFFSET + real_addr) = replace_instr; 103 | DCFlushRange((void*)(LIB_CODE_RW_BASE_OFFSET + real_addr), 4); 104 | ICInvalidateRange((void*)(real_addr), 4); 105 | } 106 | 107 | KernelRestoreDBATs(&table); 108 | } 109 | 110 | /* ****************************************************************** */ 111 | /* RESTORE ORIGINAL INSTRUCTIONS */ 112 | /* ****************************************************************** */ 113 | void RestoreInstructions(void) 114 | { 115 | bat_table_t table; 116 | KernelSetDBATs(&table); 117 | 118 | restore_instructions_t * restore = (restore_instructions_t *)(RESTORE_INSTR_ADDR); 119 | if(restore->magic == RESTORE_INSTR_MAGIC) 120 | { 121 | for(unsigned int i = 0; i < restore->instr_count; i++) 122 | { 123 | *(volatile unsigned int *)(LIB_CODE_RW_BASE_OFFSET + restore->data[i].addr) = restore->data[i].instr; 124 | DCFlushRange((void*)(LIB_CODE_RW_BASE_OFFSET + restore->data[i].addr), 4); 125 | ICInvalidateRange((void*)restore->data[i].addr, 4); 126 | } 127 | 128 | } 129 | restore->magic = 0; 130 | restore->instr_count = 0; 131 | 132 | KernelRestoreDBATs(&table); 133 | KernelRestoreInstructions(); 134 | } 135 | -------------------------------------------------------------------------------- /src/patcher/function_hooks.h: -------------------------------------------------------------------------------- 1 | #ifndef _FUNCTION_HOOKS_H_ 2 | #define _FUNCTION_HOOKS_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | void PatchMethodHooks(void); 9 | void RestoreInstructions(void); 10 | 11 | #ifdef __cplusplus 12 | } 13 | #endif 14 | 15 | #endif /* _FS_H */ 16 | -------------------------------------------------------------------------------- /src/saviine.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common/kernel_defs.h" 5 | #include "dynamic_libs/os_functions.h" 6 | #include "dynamic_libs/fs_functions.h" 7 | #include "dynamic_libs/aoc_functions.h" 8 | #include "dynamic_libs/socket_functions.h" 9 | #include "dynamic_libs/sys_functions.h" 10 | #include "game/rpx_rpl_table.h" 11 | #include "game/memory_area_table.h" 12 | #include "utils/net.h" 13 | #include "saviine.h" 14 | #include "utils/logger.h" 15 | 16 | #define TITLE_LOCATION_ODD 0 17 | #define TITLE_LOCATION_USB 1 18 | #define TITLE_LOCATION_MLC 2 19 | 20 | #define BUFFER_SIZE 0x400 * 101 21 | #define BUFFER_SIZE_STEPS 0x200 22 | 23 | #define DEBUG_LOG 0 24 | 25 | extern ReducedCosAppXmlInfo cosAppXmlInfoStruct; 26 | 27 | //! setup some default IP 28 | static u32 serverIpAddress = 0xC0A800B5; 29 | 30 | static int socket_saviine = -1; 31 | static int socket_log = -1; 32 | 33 | void StartDumper() 34 | { 35 | void *pClient = NULL; 36 | void *pCmd = NULL; 37 | 38 | do{ 39 | pClient = malloc(FS_CLIENT_SIZE); 40 | if (!pClient) 41 | break; 42 | 43 | pCmd = malloc(FS_CMD_BLOCK_SIZE); 44 | if (!pCmd) 45 | break; 46 | 47 | 48 | log_printf("StartDumper\n"); 49 | FSInit(); 50 | FSInitCmdBlock(pCmd); 51 | 52 | FSAddClientEx(pClient, 0, -1); 53 | 54 | log_printf("StartDumper\n"); 55 | cafiine_connect(&socket_saviine,serverIpAddress); 56 | cafiine_connect(&socket_log,serverIpAddress); 57 | log_printf("StartDumper\n"); 58 | 59 | if(socket_saviine >= 0 && socket_log >= 0){ 60 | handle_saves(pClient, pCmd,-1); 61 | } 62 | FSDelClient(pClient); 63 | 64 | }while(0); 65 | 66 | if(pCmd) 67 | free(pCmd); 68 | if(pClient) 69 | free(pClient); 70 | 71 | if(socket_saviine >= 0) 72 | { 73 | cafiine_disconnect(socket_saviine); 74 | socket_saviine = -1; 75 | } 76 | if(socket_log >= 0) 77 | { 78 | cafiine_disconnect(socket_log); 79 | socket_log = -1; 80 | } 81 | } 82 | 83 | void handle_saves(void *pClient, void *pCmd,int error){ 84 | if(DEBUG_LOG) log_string(socket_log, "init", BYTE_LOG_STR); 85 | unsigned char slotNo; 86 | long id = getPesistentID(&slotNo); 87 | init_Save(slotNo); 88 | int mode; 89 | 90 | if(DEBUG_LOG)log_string(socket_log, "getting mode", BYTE_LOG_STR); 91 | if(getMode(socket_saviine,&mode)){ 92 | if(id >= 0x80000000 && id <= 0x90000000){ 93 | char savepath[20]; 94 | __os_snprintf(savepath, sizeof(savepath), "/vol/save/%08x",id); 95 | if(mode == BYTE_MODE_D){ 96 | log_string(socket_log, "dump mode!", BYTE_LOG_STR); 97 | dumpSavaData(pClient, pCmd,id,error); 98 | }else if(mode == BYTE_MODE_I){ 99 | log_string(socket_log, "inject mode", BYTE_LOG_STR); 100 | injectSaveData(pClient,pCmd,id,error); 101 | } 102 | } 103 | } 104 | } 105 | 106 | void dumpSavaData(void *pClient, void *pCmd,long persistentID,int error){ 107 | /* 108 | Allocate buffer for injection 109 | */ 110 | int buf_size = BUFFER_SIZE; 111 | char * pBuffer; 112 | int failed = 0; 113 | do{ 114 | buf_size -= BUFFER_SIZE_STEPS; 115 | if(buf_size < 0){ 116 | log_string(socket_log, "error on buffer allocation", BYTE_LOG_STR); 117 | failed = 1; 118 | break; 119 | } 120 | pBuffer = (char *)memalign(0x40,buf_size); 121 | if(pBuffer) memset(pBuffer, 0x00, buf_size); 122 | }while(!pBuffer); 123 | 124 | if(!failed){ 125 | int mask = 0; 126 | char buffer[60]; 127 | __os_snprintf(buffer, sizeof(buffer), "allocated %d bytes",buf_size); 128 | log_string(socket_log, buffer, BYTE_LOG_STR); 129 | if(saviine_start_dump(socket_saviine, persistentID,&mask)){ 130 | if((mask & MASK_USER) == MASK_USER){ 131 | char savepath[20]; 132 | __os_snprintf(savepath, sizeof(savepath), "/vol/save/%08x",persistentID); 133 | log_string(socket_log, "dumping user savedata", BYTE_LOG_STR); 134 | if(dump_dir(pClient,pCmd,savepath,pBuffer,buf_size,error,50) == -1){ 135 | log_string(socket_log, "error dumping user dir", BYTE_LOG_STR); 136 | } 137 | } 138 | if((mask & MASK_COMMON) == MASK_COMMON){ 139 | char * commonDir = "/vol/save/common"; 140 | log_string(socket_log, "dumping common savedata", BYTE_LOG_STR); 141 | if(dump_dir(pClient,pCmd,commonDir,pBuffer,buf_size,error,60) == -1){ 142 | log_string(socket_log, "error dumping common dir (maybe the game has no common folder?)", BYTE_LOG_STR); 143 | } 144 | } 145 | 146 | log_string(socket_log, "done!", BYTE_LOG_STR); 147 | 148 | if(!saviine_end_dump(socket_saviine)) if(DEBUG_LOG) log_string(socket_log, "saviine_end_injection() failed", BYTE_LOG_STR); 149 | if(DEBUG_LOG) log_string(socket_log, "end of dump", BYTE_LOG_STR); 150 | }else{ 151 | log_string(socket_log, "saviine_start_dump() failed", BYTE_LOG_STR); 152 | } 153 | 154 | if(DEBUG_LOG) log_string(socket_log, "free(pBuffer) coming next", BYTE_LOG_STR); 155 | free(pBuffer); 156 | if(DEBUG_LOG) log_string(socket_log, "free(pBuffer)", BYTE_LOG_STR); 157 | } 158 | } 159 | 160 | int dump_dir(void *pClient, void *pCmd, char *path, void * pBuffer, int size,int error, int handle){ 161 | int dir_handle = handle; 162 | int my_handle = handle +1; 163 | int ret = 0; 164 | int final_result = 0; 165 | if ((ret = FSOpenDir(pClient, pCmd, path, &dir_handle, FS_RET_ALL_ERROR)) == FS_STATUS_OK){ 166 | char buffer[strlen(path) + 30]; 167 | __os_snprintf(buffer, sizeof(buffer), "open dir %s",path); 168 | log_string(socket_log, buffer, BYTE_LOG_STR); 169 | FSDirEntry dir_entry; 170 | while (FSReadDir(pClient, pCmd, dir_handle, &dir_entry, FS_RET_ALL_ERROR) == FS_STATUS_OK && final_result == 0) 171 | { 172 | char full_path[strlen(path) + 1 + strlen(dir_entry.name) +1]; 173 | __os_snprintf(full_path, sizeof(full_path), "%s/%s",path,dir_entry.name); 174 | 175 | if((dir_entry.stat.flag&FS_STAT_FLAG_IS_DIRECTORY) == FS_STAT_FLAG_IS_DIRECTORY){ 176 | log_string(socket_log, "-> dir", BYTE_LOG_STR); 177 | 178 | if(dump_dir(pClient, pCmd,full_path,pBuffer,size,error,my_handle) == -1){ 179 | log_string(socket_log, "error", BYTE_LOG_STR); 180 | final_result = -1; 181 | } 182 | }else{ 183 | //DUMP 184 | ret = FSOpenFile(pClient, pCmd, full_path, "r", &my_handle, error); 185 | if (ret >= 0) { 186 | __os_snprintf(buffer, sizeof(buffer), "dumping %s",dir_entry.name); 187 | log_string(socket_log, buffer, BYTE_LOG_STR); 188 | 189 | int ret2; 190 | 191 | int my_ret = cafiine_send_handle(socket_saviine, full_path, my_handle); 192 | if(my_ret != -1){ 193 | while ((ret2 = FSReadFile(pClient, pCmd, pBuffer, 1, size, my_handle, 0, 0)) > 0) 194 | cafiine_send_file(socket_saviine, pBuffer, ret2, my_handle); 195 | cafiine_fclose(socket_saviine, &ret2, my_handle,1); 196 | }else{ 197 | log_string(socket_log, "error on opening file on pc" , BYTE_LOG_STR); 198 | final_result = -1; 199 | } 200 | if((ret2 = FSCloseFile(pClient, pCmd, my_handle, error)) < FS_STATUS_OK){ 201 | __os_snprintf(buffer, sizeof(buffer), "error on FSOpenFile: %d",ret2); 202 | log_string(socket_log, buffer, BYTE_LOG_STR); 203 | } 204 | }else{ 205 | __os_snprintf(buffer, sizeof(buffer), "error on FSOpenFile: %d",ret); 206 | log_string(socket_log, buffer, BYTE_LOG_STR); 207 | final_result = -1; 208 | } 209 | } 210 | } 211 | if(FSCloseDir(pClient, pCmd, dir_handle, error) < FS_STATUS_OK){ 212 | if(DEBUG_LOG) log_string(socket_log, "error on FSCloseDir()", BYTE_LOG_STR); 213 | } 214 | }else{ 215 | log_string(socket_log, "error on FSOpenDir()", BYTE_LOG_STR); 216 | final_result = -1; 217 | } 218 | return final_result; 219 | } 220 | 221 | /************************** 222 | Injection functions 223 | **************************/ 224 | 225 | void injectSaveData(void *pClient, void *pCmd,long persistentID,int error){ 226 | char logbuffer[255]; 227 | /* 228 | Allocate buffer for injection 229 | */ 230 | int buf_size = BUFFER_SIZE; 231 | char * pBuffer; 232 | int failed = 0; 233 | do{ 234 | buf_size -= BUFFER_SIZE_STEPS; 235 | if(buf_size < 0){ 236 | log_string(socket_log, "error on buffer allocation", BYTE_LOG_STR); 237 | failed = 1; 238 | break; 239 | } 240 | pBuffer = (char *)memalign(0x40,buf_size); 241 | if(pBuffer) memset(pBuffer, 0x00, buf_size); 242 | }while(!pBuffer); 243 | 244 | if(!failed){ 245 | char buffer[60]; 246 | __os_snprintf(buffer, sizeof(buffer), "allocated %d bytes",buf_size); 247 | log_string(socket_log, buffer, BYTE_LOG_STR); 248 | int result = 0; 249 | int mask = 0; 250 | if((result = saviine_start_injection(socket_saviine, persistentID,&mask))){ 251 | if((mask & MASK_USER) == MASK_USER){ 252 | char savepath[20]; 253 | __os_snprintf(savepath, sizeof(savepath), "/vol/save/%08x",persistentID); 254 | __os_snprintf(logbuffer, sizeof(logbuffer), "injecting new userdata in %08x",persistentID); 255 | log_string(socket_log, logbuffer, BYTE_LOG_STR); 256 | log_string(socket_log, "deleting user save", BYTE_LOG_STR); 257 | if(remove_files_in_dir(pClient,pCmd,savepath,0) == 0){ 258 | /* 259 | Inject Save 260 | */ 261 | result = injectFiles(pClient,pCmd,savepath,"/",savepath,pBuffer,buf_size,error); 262 | doFlushOrRollback(pClient,pCmd,result,savepath); 263 | } 264 | } 265 | if((mask & MASK_COMMON) == MASK_COMMON && !failed){ 266 | char * commonDir = "/vol/save/common"; 267 | 268 | if((mask & MASK_COMMON_CLEAN) == MASK_COMMON_CLEAN){ 269 | log_string(socket_log, "deleting common save", BYTE_LOG_STR); 270 | if(remove_files_in_dir(pClient,pCmd,commonDir,0) == -1){ 271 | failed = 1; 272 | } 273 | } 274 | if(!failed){ 275 | /* 276 | Inject common 277 | */ 278 | result = injectFiles(pClient,pCmd,commonDir,"/",commonDir,pBuffer,buf_size,error); 279 | doFlushOrRollback(pClient,pCmd,result,commonDir); 280 | } 281 | } 282 | if(!saviine_end_injection(socket_saviine)) if(DEBUG_LOG) log_string(socket_log, "saviine_end_injection() failed", BYTE_LOG_STR); 283 | if(DEBUG_LOG)log_string(socket_log, "end of injection", BYTE_LOG_STR); 284 | }else{ 285 | log_string(socket_log, "saviine_start_injection() failed", BYTE_LOG_STR); 286 | } 287 | free(pBuffer); 288 | } 289 | } 290 | 291 | int injectFiles(void *pClient, void *pCmd, char * path,char * relativepath, char * basepath, char * pBuffer, int buffer_size, int error){ 292 | int failed = 0; 293 | int filesinjected = 0; 294 | int type = 0; 295 | log_string(socket_log, "injecting files", BYTE_LOG_STR); 296 | char namebuffer[255]; 297 | char logbuffer[255]; 298 | int filesize = 0; 299 | 300 | if(!failed){ 301 | while(saviine_readdir(socket_saviine,path,namebuffer, &type,&filesize) && !failed){ 302 | if(DEBUG_LOG)log_string(socket_log, "got a file", BYTE_LOG_STR); 303 | char newpath[strlen(path) + 1 + strlen(namebuffer) + 1]; 304 | __os_snprintf(newpath, sizeof(newpath), "%s/%s",path,namebuffer); 305 | if(type == BYTE_FILE){ 306 | __os_snprintf(logbuffer, sizeof(logbuffer), "file: %s%s size: %d",relativepath,namebuffer,filesize); 307 | log_string(socket_log, logbuffer, BYTE_LOG_STR); 308 | 309 | __os_snprintf(logbuffer, sizeof(logbuffer), "newpath: %s ",newpath); 310 | log_string(socket_log, logbuffer, BYTE_LOG_STR); 311 | 312 | if(DEBUG_LOG) log_string(socket_log, "downloading it", BYTE_LOG_STR); 313 | 314 | int handle = 10; 315 | if(FSOpenFile(pClient, pCmd, newpath,"w+",&handle,error) >= FS_STATUS_OK){ 316 | if(DEBUG_LOG) log_string(socket_log, "file opened and created", BYTE_LOG_STR); 317 | 318 | if(filesize > 0){ 319 | failed = doInjectForFile(pClient,pCmd,handle,newpath,filesize,basepath,pBuffer,buffer_size); 320 | if(failed == 2) // trying it again if the journal was full 321 | failed = doInjectForFile(pClient,pCmd,handle,newpath,filesize,basepath,pBuffer,buffer_size); 322 | }else{ 323 | if(DEBUG_LOG) log_string(socket_log, "filesize is 0", BYTE_LOG_STR); 324 | } 325 | 326 | if((FSCloseFile (pClient, pCmd, handle, error)) < FS_STATUS_OK){ 327 | log_string(socket_log, "FSCloseFile failed", BYTE_LOG_STR); 328 | failed = 1; 329 | } 330 | }else{ 331 | log_string(socket_log, "opening the file failed", BYTE_LOG_STR); 332 | failed = 1; 333 | } 334 | if(!failed) filesinjected++; 335 | }else if( type == BYTE_FOLDER){ 336 | __os_snprintf(logbuffer, sizeof(logbuffer), "dir: %s",namebuffer); 337 | log_string(socket_log, logbuffer, BYTE_LOG_STR); 338 | if(DEBUG_LOG) log_string(socket_log, newpath, BYTE_LOG_STR); 339 | int ret = 0; 340 | if((ret = FSMakeDir(pClient, pCmd, newpath, -1)) == FS_STATUS_OK || ret == FS_STATUS_EXISTS ){ 341 | char op_offset[strlen(relativepath) + strlen(namebuffer)+ 1 + 1]; 342 | __os_snprintf(op_offset, sizeof(op_offset), "%s%s/",relativepath,namebuffer); 343 | int injectedsub = injectFiles(pClient, pCmd, newpath,op_offset,basepath,pBuffer,buffer_size,error); 344 | if(injectedsub == -1){ 345 | failed = 1; 346 | }else{ 347 | filesinjected += injectedsub; 348 | } 349 | }else{ 350 | log_string(socket_log, "folder creation failed", BYTE_LOG_STR); 351 | failed = 1; 352 | } 353 | } 354 | } 355 | if(failed) return -1; 356 | else return filesinjected; 357 | }else{ 358 | return -1; 359 | } 360 | } 361 | 362 | int doInjectForFile(void * pClient, void * pCmd,int handle,char * filepath,int filesize, char * basepath,void * pBuffer,int buf_size){ 363 | int failed = 0; 364 | int myhandle; 365 | int ret = 0; 366 | char logbuffer[255]; 367 | if(DEBUG_LOG)__os_snprintf(logbuffer, sizeof(logbuffer), "cafiine_fopen %s",filepath); 368 | if(DEBUG_LOG) log_string(socket_log, logbuffer, BYTE_LOG_STR); 369 | 370 | if((cafiine_fopen(socket_saviine, &ret, filepath, "r", &myhandle)) == 0 && ret == FS_STATUS_OK){ 371 | if(DEBUG_LOG)__os_snprintf(logbuffer, sizeof(logbuffer), "cafiine_fopen with handle %d",myhandle); 372 | if(DEBUG_LOG) log_string(socket_log, logbuffer, BYTE_LOG_STR); 373 | int retsize = 0; 374 | int pos = 0; 375 | while(pos < filesize){ 376 | if(DEBUG_LOG) log_string(socket_log, "reading", BYTE_LOG_STR); 377 | if(cafiine_fread(socket_saviine, &retsize, pBuffer, buf_size , myhandle) == FS_STATUS_OK){ 378 | if(DEBUG_LOG)__os_snprintf(logbuffer, sizeof(logbuffer), "got %d",retsize); 379 | if(DEBUG_LOG) log_string(socket_log, logbuffer, BYTE_LOG_STR); 380 | int fwrite = 0; 381 | if((fwrite = FSWriteFile(pClient, pCmd, pBuffer,sizeof(char),retsize,handle,0,0x0200)) >= FS_STATUS_OK){ 382 | if(DEBUG_LOG)__os_snprintf(logbuffer, sizeof(logbuffer), "wrote %d",retsize); 383 | if(DEBUG_LOG) log_string(socket_log, logbuffer, BYTE_LOG_STR); 384 | }else{ 385 | if(fwrite == FS_STATUS_JOURNAL_FULL || fwrite == FS_STATUS_STORAGE_FULL){ 386 | log_string(socket_log, "journal or storage is full, flushing it now.", BYTE_LOG_STR); 387 | if(FSFlushQuota(pClient,pCmd,basepath,FS_RET_ALL_ERROR) == FS_STATUS_OK){ 388 | log_string(socket_log, "success", BYTE_LOG_STR); 389 | failed = 2; 390 | break; 391 | }else{ 392 | log_string(socket_log, "failed", BYTE_LOG_STR); 393 | failed = 1; 394 | break; 395 | } 396 | 397 | } 398 | __os_snprintf(logbuffer, sizeof(logbuffer), "my_FSWriteFile failed with error: %d",fwrite); 399 | log_string(socket_log, logbuffer, BYTE_LOG_STR); 400 | //log_string(socket_log, "error while FSWriteFile", BYTE_LOG_STR); 401 | failed = 1; 402 | break; 403 | } 404 | if(DEBUG_LOG)__os_snprintf(logbuffer, sizeof(logbuffer), "old p %d new p %d",pos,pos+retsize); 405 | if(DEBUG_LOG) log_string(socket_log, logbuffer, BYTE_LOG_STR); 406 | pos += retsize; 407 | }else{ 408 | log_string(socket_log, "error while recieving file", BYTE_LOG_STR); 409 | failed = 1; 410 | break; 411 | } 412 | } 413 | 414 | int result = 0; 415 | if((cafiine_fclose(socket_saviine, &result, myhandle,0)) == 0 && result == FS_STATUS_OK){ 416 | if(DEBUG_LOG) log_string(socket_log, "cafiine_fclose success", BYTE_LOG_STR); 417 | }else{ 418 | log_string(socket_log, "cafiine_fclose failed", BYTE_LOG_STR); 419 | failed = 1; 420 | } 421 | 422 | 423 | }else{ 424 | log_string(socket_log, "cafiine_fopen failed", BYTE_LOG_STR); 425 | failed = 1; 426 | } 427 | return failed; 428 | } 429 | 430 | /************************* 431 | Util functions 432 | **************************/ 433 | 434 | /*flush if result != -1*/ 435 | 436 | void doFlushOrRollback(void *pClient, void *pCmd,int result,char *savepath){ 437 | char logbuffer[50 + strlen(savepath)]; 438 | if(result != -1){ 439 | __os_snprintf(logbuffer, sizeof(logbuffer), "injected %d files",result); 440 | log_string(socket_log, logbuffer, BYTE_LOG_STR); 441 | log_string(socket_log, "Flushing data now", BYTE_LOG_STR); 442 | if(FSFlushQuota(pClient,pCmd,savepath,FS_RET_ALL_ERROR) == FS_STATUS_OK){ 443 | log_string(socket_log, "success", BYTE_LOG_STR); 444 | }else{ 445 | log_string(socket_log, "failed", BYTE_LOG_STR); 446 | } 447 | }else{ 448 | log_string(socket_log, "injection failed, trying to restore the data", BYTE_LOG_STR); 449 | if(FSRollbackQuota(pClient,pCmd,savepath,FS_RET_ALL_ERROR) == FS_STATUS_OK){ 450 | log_string(socket_log, "rollback done", BYTE_LOG_STR); 451 | }else{ 452 | log_string(socket_log, "rollback failed", BYTE_LOG_STR); 453 | } 454 | } 455 | } 456 | 457 | void init_Save(unsigned char slotNo){ 458 | int (*SAVEInit)(); 459 | int (*SAVEInitSaveDir)(unsigned char accountSlotNo); 460 | unsigned int save_handle; 461 | OSDynLoad_Acquire("nn_save.rpl", &save_handle); 462 | OSDynLoad_FindExport(save_handle, 0, "SAVEInit", (void **)&SAVEInit); 463 | OSDynLoad_FindExport(save_handle, 0, "SAVEInitSaveDir", (void **)&SAVEInitSaveDir); 464 | SAVEInit(); 465 | if(DEBUG_LOG) log_string(socket_log, "saveinit done", BYTE_LOG_STR); 466 | SAVEInitSaveDir(slotNo); 467 | if(DEBUG_LOG) log_string(socket_log, "SAVEInitSaveDir done", BYTE_LOG_STR); 468 | SAVEInitSaveDir(255U); 469 | if(DEBUG_LOG) log_string(socket_log, "SAVEInitSaveDir 2 done", BYTE_LOG_STR); 470 | } 471 | 472 | long getPesistentID(unsigned char * slotno){ 473 | unsigned int nn_act_handle; 474 | unsigned long (*GetPersistentIdEx)(unsigned char); 475 | int (*GetSlotNo)(void); 476 | void (*nn_Initialize)(void); 477 | void (*nn_Finalize)(void); 478 | OSDynLoad_Acquire("nn_act.rpl", &nn_act_handle); 479 | OSDynLoad_FindExport(nn_act_handle, 0, "GetPersistentIdEx__Q2_2nn3actFUc", (void **)&GetPersistentIdEx); 480 | OSDynLoad_FindExport(nn_act_handle, 0, "GetSlotNo__Q2_2nn3actFv", (void **)&GetSlotNo); 481 | OSDynLoad_FindExport(nn_act_handle, 0, "Initialize__Q2_2nn3actFv", (void **)&nn_Initialize); 482 | OSDynLoad_FindExport(nn_act_handle, 0, "Finalize__Q2_2nn3actFv", (void **)&nn_Finalize); 483 | 484 | nn_Initialize(); // To be sure that it is really Initialized 485 | 486 | *slotno = GetSlotNo(); 487 | 488 | long idlong = GetPersistentIdEx(*slotno); 489 | 490 | nn_Finalize(); //must be called an equal number of times to nn_Initialize 491 | return idlong; 492 | } 493 | 494 | int remove_files_in_dir(void * pClient,void * pCmd, char * path, int handle){ 495 | int ret = 0; 496 | int my_handle = handle +1; 497 | char buffer[strlen(path) + 50]; 498 | if ((ret = FSOpenDir(pClient, pCmd, path, &handle, FS_RET_ALL_ERROR)) == FS_STATUS_OK){ 499 | __os_snprintf(buffer, sizeof(buffer), "remove files in dir %s",path); 500 | log_string(socket_log, buffer, BYTE_LOG_STR); 501 | FSDirEntry dir_entry; 502 | while (FSReadDir(pClient, pCmd, handle, &dir_entry, FS_RET_ALL_ERROR) == FS_STATUS_OK) 503 | { 504 | char full_path[strlen(path) + 1 + strlen(dir_entry.name) +1]; 505 | __os_snprintf(full_path, sizeof(full_path), "%s/%s",path,dir_entry.name); 506 | if((dir_entry.stat.flag&FS_STAT_FLAG_IS_DIRECTORY) == FS_STAT_FLAG_IS_DIRECTORY){ 507 | if(DEBUG_LOG) log_string(socket_log, "recursive deletion", BYTE_LOG_STR); 508 | if(remove_files_in_dir(pClient,pCmd,full_path,my_handle) == -1) return -1; 509 | 510 | } 511 | char buffer[strlen(full_path) + 50]; 512 | __os_snprintf(buffer, sizeof(buffer), "deleting %s",full_path); 513 | log_string(socket_log, buffer, BYTE_LOG_STR); 514 | if((ret = FSRemove(pClient,pCmd,full_path,FS_RET_ALL_ERROR)) < FS_STATUS_OK){ 515 | __os_snprintf(buffer, sizeof(buffer), "error: %d on removing %s",ret,full_path); 516 | log_string(socket_log, buffer, BYTE_LOG_STR); 517 | return -1; 518 | } 519 | 520 | } 521 | if((FSCloseDir(pClient, pCmd, handle, FS_RET_NO_ERROR)) < FS_STATUS_OK ){ 522 | log_string(socket_log, "error while closing dir", BYTE_LOG_STR); 523 | return -1; 524 | } 525 | }else{ 526 | __os_snprintf(buffer, sizeof(buffer), "error: %d on opening %s",ret,path); 527 | log_string(socket_log, buffer, BYTE_LOG_STR); 528 | return -1; 529 | } 530 | return 0; 531 | } 532 | 533 | void ResetDumper(void) 534 | { 535 | socket_saviine = -1; 536 | socket_log = -1; 537 | 538 | // reset RPX table on every launch of system menu 539 | rpxRplTableInit(); 540 | } 541 | 542 | void SetServerIp(u32 ip) 543 | { 544 | serverIpAddress = ip; 545 | } 546 | 547 | u32 GetServerIp(void) 548 | { 549 | return serverIpAddress; 550 | } 551 | -------------------------------------------------------------------------------- /src/saviine.h: -------------------------------------------------------------------------------- 1 | #ifndef _DISCDUMPER_H_ 2 | #define _DISCDUMPER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | typedef struct { 9 | unsigned char tag; 10 | unsigned int length; 11 | unsigned char data[0]; 12 | } __attribute__((packed)) SendData; 13 | 14 | 15 | long getPesistentID(unsigned char * slotno); 16 | void handle_saves(void *pClient, void *pCmd,int error); 17 | void dumpSavaData(void *pClient, void *pCmd,long persistentID,int error); 18 | int dump_dir(void *pClient, void *pCmd, char *path, void * pBuffer, int size,int error, int handle); 19 | void injectSaveData(void *pClient, void *pCmd,long persistentID,int error); 20 | int injectFiles(void *pClient, void *pCmd, char * path,char * relativepath, char * basepath, char * pBuffer, int buffer_size, int error); 21 | int doInjectForFile(void * pClient, void * pCmd,int handle,char * filepath,int filesize, char * basepath,void * pBuffer,int buf_size); 22 | void doFlushOrRollback(void *pClient, void *pCmd,int result,char *savepath); 23 | void init_Save(unsigned char slotNo); 24 | long getPesistentID(unsigned char * slotno); 25 | int remove_files_in_dir(void * pClient,void * pCmd, char * path, int handle); 26 | void StartDumper(void); 27 | void ResetDumper(void); 28 | 29 | void SetServerIp(u32 ip); 30 | u32 GetServerIp(void); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/system/memory.c: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (C) 2015 Dimok 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | ****************************************************************************/ 17 | #include 18 | #include 19 | #include "dynamic_libs/os_functions.h" 20 | #include "common/common.h" 21 | #include "memory.h" 22 | 23 | #define MEMORY_ARENA_1 0 24 | #define MEMORY_ARENA_2 1 25 | #define MEMORY_ARENA_3 2 26 | #define MEMORY_ARENA_4 3 27 | #define MEMORY_ARENA_5 4 28 | #define MEMORY_ARENA_6 5 29 | #define MEMORY_ARENA_7 6 30 | #define MEMORY_ARENA_8 7 31 | #define MEMORY_ARENA_FG_BUCKET 8 32 | 33 | //!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 34 | //! Memory functions 35 | //! This is the only place where those are needed so lets keep them more or less private 36 | //!---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 37 | extern unsigned int * pMEMAllocFromDefaultHeapEx; 38 | extern unsigned int * pMEMAllocFromDefaultHeap; 39 | extern unsigned int * pMEMFreeToDefaultHeap; 40 | 41 | extern int (* MEMGetBaseHeapHandle)(int mem_arena); 42 | extern unsigned int (* MEMGetAllocatableSizeForFrmHeapEx)(int heap, int align); 43 | extern void *(* MEMAllocFromFrmHeapEx)(int heap, unsigned int size, int align); 44 | extern void (* MEMFreeToFrmHeap)(int heap, int mode); 45 | extern void *(* MEMAllocFromExpHeapEx)(int heap, unsigned int size, int align); 46 | extern int (* MEMCreateExpHeapEx)(void* address, unsigned int size, unsigned short flags); 47 | extern void *(* MEMDestroyExpHeap)(int heap); 48 | extern void (* MEMFreeToExpHeap)(int heap, void* ptr); 49 | 50 | static int mem1_heap = -1; 51 | static int bucket_heap = -1; 52 | 53 | void memoryInitialize(void) 54 | { 55 | int mem1_heap_handle = MEMGetBaseHeapHandle(MEMORY_ARENA_1); 56 | unsigned int mem1_allocatable_size = MEMGetAllocatableSizeForFrmHeapEx(mem1_heap_handle, 4); 57 | void *mem1_memory = MEMAllocFromFrmHeapEx(mem1_heap_handle, mem1_allocatable_size, 4); 58 | if(mem1_memory) 59 | mem1_heap = MEMCreateExpHeapEx(mem1_memory, mem1_allocatable_size, 0); 60 | 61 | int bucket_heap_handle = MEMGetBaseHeapHandle(MEMORY_ARENA_FG_BUCKET); 62 | unsigned int bucket_allocatable_size = MEMGetAllocatableSizeForFrmHeapEx(bucket_heap_handle, 4); 63 | void *bucket_memory = MEMAllocFromFrmHeapEx(bucket_heap_handle, bucket_allocatable_size, 4); 64 | if(bucket_memory) 65 | bucket_heap = MEMCreateExpHeapEx(bucket_memory, bucket_allocatable_size, 0); 66 | } 67 | 68 | void memoryRelease(void) 69 | { 70 | MEMDestroyExpHeap(mem1_heap); 71 | MEMFreeToFrmHeap(MEMGetBaseHeapHandle(MEMORY_ARENA_1), 3); 72 | mem1_heap = -1; 73 | 74 | MEMDestroyExpHeap(bucket_heap); 75 | MEMFreeToFrmHeap(MEMGetBaseHeapHandle(MEMORY_ARENA_FG_BUCKET), 3); 76 | bucket_heap = -1; 77 | } 78 | 79 | //!------------------------------------------------------------------------------------------- 80 | //! wraps 81 | //!------------------------------------------------------------------------------------------- 82 | void *__wrap_malloc(size_t size) 83 | { 84 | // pointer to a function resolve 85 | return ((void * (*)(size_t))(*pMEMAllocFromDefaultHeap))(size); 86 | } 87 | 88 | void *__wrap_memalign(size_t align, size_t size) 89 | { 90 | if (align < 4) 91 | align = 4; 92 | 93 | // pointer to a function resolve 94 | return ((void * (*)(size_t, size_t))(*pMEMAllocFromDefaultHeapEx))(size, align); 95 | } 96 | 97 | void __wrap_free(void *p) 98 | { 99 | // pointer to a function resolve 100 | if(p != 0) 101 | ((void (*)(void *))(*pMEMFreeToDefaultHeap))(p); 102 | } 103 | 104 | void *__wrap_calloc(size_t n, size_t size) 105 | { 106 | void *p = __wrap_malloc(n * size); 107 | if (p != 0) { 108 | memset(p, 0, n * size); 109 | } 110 | return p; 111 | } 112 | 113 | size_t __wrap_malloc_usable_size(void *p) 114 | { 115 | //! TODO: this is totally wrong and needs to be addressed 116 | return 0x7FFFFFFF; 117 | } 118 | 119 | void *__wrap_realloc(void *p, size_t size) 120 | { 121 | void *new_ptr = __wrap_malloc(size); 122 | if (new_ptr != 0) 123 | { 124 | memcpy(new_ptr, p, __wrap_malloc_usable_size(p) < size ? __wrap_malloc_usable_size(p) : size); 125 | __wrap_free(p); 126 | } 127 | return new_ptr; 128 | } 129 | 130 | //!------------------------------------------------------------------------------------------- 131 | //! reent versions 132 | //!------------------------------------------------------------------------------------------- 133 | void *__wrap__malloc_r(struct _reent *r, size_t size) 134 | { 135 | return __wrap_malloc(size); 136 | } 137 | 138 | void *__wrap__calloc_r(struct _reent *r, size_t n, size_t size) 139 | { 140 | return __wrap_calloc(n, size); 141 | } 142 | 143 | void *__wrap__memalign_r(struct _reent *r, size_t align, size_t size) 144 | { 145 | return __wrap_memalign(align, size); 146 | } 147 | 148 | void __wrap__free_r(struct _reent *r, void *p) 149 | { 150 | __wrap_free(p); 151 | } 152 | 153 | size_t __wrap__malloc_usable_size_r(struct _reent *r, void *p) 154 | { 155 | return __wrap_malloc_usable_size(p); 156 | } 157 | 158 | void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) 159 | { 160 | return __wrap_realloc(p, size); 161 | } 162 | 163 | //!------------------------------------------------------------------------------------------- 164 | //! some wrappers 165 | //!------------------------------------------------------------------------------------------- 166 | void * MEM2_alloc(unsigned int size, unsigned int align) 167 | { 168 | return __wrap_memalign(align, size); 169 | } 170 | 171 | void MEM2_free(void *ptr) 172 | { 173 | __wrap_free(ptr); 174 | } 175 | 176 | void * MEM1_alloc(unsigned int size, unsigned int align) 177 | { 178 | if (align < 4) 179 | align = 4; 180 | return MEMAllocFromExpHeapEx(mem1_heap, size, align); 181 | } 182 | 183 | void MEM1_free(void *ptr) 184 | { 185 | MEMFreeToExpHeap(mem1_heap, ptr); 186 | } 187 | 188 | void * MEMBucket_alloc(unsigned int size, unsigned int align) 189 | { 190 | if (align < 4) 191 | align = 4; 192 | return MEMAllocFromExpHeapEx(bucket_heap, size, align); 193 | } 194 | 195 | void MEMBucket_free(void *ptr) 196 | { 197 | MEMFreeToExpHeap(bucket_heap, ptr); 198 | } 199 | -------------------------------------------------------------------------------- /src/system/memory.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (C) 2015 Dimok 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | ****************************************************************************/ 17 | #ifndef __MEMORY_H_ 18 | #define __MEMORY_H_ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | 26 | void memoryInitialize(void); 27 | void memoryRelease(void); 28 | 29 | void * MEM2_alloc(unsigned int size, unsigned int align); 30 | void MEM2_free(void *ptr); 31 | 32 | void * MEM1_alloc(unsigned int size, unsigned int align); 33 | void MEM1_free(void *ptr); 34 | 35 | void * MEMBucket_alloc(unsigned int size, unsigned int align); 36 | void MEMBucket_free(void *ptr); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif // __MEMORY_H_ 43 | -------------------------------------------------------------------------------- /src/utils/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "common/common.h" 7 | #include "dynamic_libs/os_functions.h" 8 | #include "dynamic_libs/socket_functions.h" 9 | #include "logger.h" 10 | 11 | #ifdef DEBUG_LOGGER 12 | static int log_socket = -1; 13 | static volatile int log_lock = 0; 14 | 15 | 16 | void log_init(const char * ipString) 17 | { 18 | log_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 19 | if (log_socket < 0) 20 | return; 21 | 22 | struct sockaddr_in connect_addr; 23 | memset(&connect_addr, 0, sizeof(connect_addr)); 24 | connect_addr.sin_family = AF_INET; 25 | connect_addr.sin_port = 4405; 26 | inet_aton(ipString, &connect_addr.sin_addr); 27 | 28 | if(connect(log_socket, (struct sockaddr*)&connect_addr, sizeof(connect_addr)) < 0) 29 | { 30 | socketclose(log_socket); 31 | log_socket = -1; 32 | } 33 | } 34 | 35 | void log_deinit(void) 36 | { 37 | if(log_socket >= 0) 38 | { 39 | socketclose(log_socket); 40 | log_socket = -1; 41 | } 42 | } 43 | 44 | void log_print(const char *str) 45 | { 46 | // socket is always 0 initially as it is in the BSS 47 | if(log_socket < 0) { 48 | return; 49 | } 50 | 51 | while(log_lock) 52 | usleep(1000); 53 | log_lock = 1; 54 | 55 | int len = strlen(str); 56 | int ret; 57 | while (len > 0) { 58 | int block = len < 1400 ? len : 1400; // take max 1400 bytes per UDP packet 59 | ret = send(log_socket, str, block, 0); 60 | if(ret < 0) 61 | break; 62 | 63 | len -= ret; 64 | str += ret; 65 | } 66 | 67 | log_lock = 0; 68 | } 69 | 70 | void log_printf(const char *format, ...) 71 | { 72 | if(log_socket < 0) { 73 | return; 74 | } 75 | 76 | char * tmp = NULL; 77 | 78 | va_list va; 79 | va_start(va, format); 80 | if((vasprintf(&tmp, format, va) >= 0) && tmp) 81 | { 82 | log_print(tmp); 83 | } 84 | va_end(va); 85 | 86 | if(tmp) 87 | free(tmp); 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /src/utils/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOGGER_H_ 2 | #define __LOGGER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | //#define DEBUG_LOGGER 1 9 | 10 | #ifdef DEBUG_LOGGER 11 | void log_init(const char * ip); 12 | void log_deinit(void); 13 | void log_print(const char *str); 14 | void log_printf(const char *format, ...); 15 | #else 16 | #define log_init(x) 17 | #define log_deinit() 18 | #define log_print(x) 19 | #define log_printf(x, ...) 20 | #endif 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/utils/net.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common/common.h" 4 | #include "dynamic_libs/os_functions.h" 5 | #include "dynamic_libs/socket_functions.h" 6 | #include "net.h" 7 | 8 | static volatile int iLock = 0; 9 | 10 | #define CHECK_ERROR(cond) if (cond) { goto error; } 11 | 12 | void cafiine_connect(int *psock,u32 server_ip) { 13 | struct sockaddr_in addr; 14 | int sock, ret; 15 | 16 | socket_lib_init(); 17 | 18 | sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 19 | CHECK_ERROR(sock == -1); 20 | 21 | addr.sin_family = AF_INET; 22 | addr.sin_port = 7332; 23 | addr.sin_addr.s_addr = server_ip; 24 | 25 | ret = connect(sock, (void *)&addr, sizeof(addr)); 26 | CHECK_ERROR(ret < 0); 27 | ret = cafiine_handshake(sock); 28 | CHECK_ERROR(ret < 0); 29 | CHECK_ERROR(ret == BYTE_NORMAL); 30 | 31 | *psock = sock; 32 | return; 33 | error: 34 | if (sock != -1) 35 | socketclose(sock); 36 | *psock = -1; 37 | return; 38 | } 39 | 40 | void cafiine_disconnect(int sock) { 41 | CHECK_ERROR(sock == -1); 42 | socketclose(sock); 43 | error: 44 | return; 45 | } 46 | 47 | int cafiine_handshake(int sock) { 48 | int ret; 49 | 50 | unsigned char buffer[16]; 51 | 52 | u64 title_id = OSGetTitleID(); 53 | memcpy(buffer, &title_id, 16); 54 | 55 | ret = sendwait(sock, buffer, sizeof(buffer)); 56 | CHECK_ERROR(ret < 0); 57 | ret = recvbyte(sock); 58 | CHECK_ERROR(ret < 0); 59 | return ret; 60 | error: 61 | return ret; 62 | } 63 | 64 | int getMode(int sock,int * result) 65 | { 66 | while (iLock) 67 | usleep(5000); 68 | iLock = 1; 69 | int ret = 0; 70 | CHECK_ERROR(sock == -1); 71 | 72 | // create and send buffer with : [cmd id][fd][size][buffer data ...] 73 | { 74 | ret = sendbyte(sock, BYTE_G_MODE); 75 | 76 | // wait reply 77 | ret = recvbyte(sock); 78 | CHECK_ERROR(ret < 0); 79 | if(ret == BYTE_MODE_D) *result = BYTE_MODE_D; 80 | if(ret == BYTE_MODE_I) *result = BYTE_MODE_I; 81 | ret = 1; 82 | } 83 | error: 84 | iLock = 0; 85 | return ret; 86 | } 87 | 88 | int cafiine_fsetpos(int sock, int *result, int fd, int set) { 89 | while (iLock) 90 | usleep(5000); 91 | iLock = 1; 92 | 93 | CHECK_ERROR(sock == -1); 94 | 95 | int ret; 96 | char buffer[1 + 8]; 97 | buffer[0] = BYTE_SETPOS; 98 | *(int *)(buffer + 1) = fd; 99 | *(int *)(buffer + 5) = set; 100 | ret = sendwait(sock, buffer, 1 + 8); 101 | CHECK_ERROR(ret < 0); 102 | ret = recvbyte(sock); 103 | CHECK_ERROR(ret < 0); 104 | CHECK_ERROR(ret == BYTE_NORMAL); 105 | ret = recvwait(sock, result, 4); 106 | CHECK_ERROR(ret < 0); 107 | 108 | iLock = 0; 109 | return 0; 110 | error: 111 | iLock = 0; 112 | return -1; 113 | } 114 | 115 | int cafiine_send_handle(int sock, const char *path, int handle) 116 | { 117 | while (iLock) 118 | usleep(5000); 119 | iLock = 1; 120 | 121 | CHECK_ERROR(sock == -1); 122 | 123 | // create and send buffer with : [cmd id][handle][path length][path data ...] 124 | { 125 | int ret; 126 | int len_path = 0; 127 | while (path[len_path++]); 128 | char buffer[1 + 4 + 4 + len_path]; 129 | 130 | buffer[0] = BYTE_HANDLE; 131 | *(int *)(buffer + 1) = handle; 132 | *(int *)(buffer + 5) = len_path; 133 | for (ret = 0; ret < len_path; ret++) 134 | buffer[9 + ret] = path[ret]; 135 | 136 | // send buffer, wait for reply 137 | ret = sendwait(sock, buffer, 1 + 4 + 4 + len_path); 138 | CHECK_ERROR(ret < 0); 139 | 140 | // wait reply 141 | ret = recvbyte(sock); 142 | CHECK_ERROR(ret < 0); 143 | CHECK_ERROR(ret == BYTE_SPECIAL); 144 | if(ret == BYTE_REQUEST){ 145 | ret = 1; 146 | }else{ 147 | ret = 2; 148 | } 149 | // wait reply 150 | int special_ret = recvbyte(sock); 151 | CHECK_ERROR(special_ret < 0); 152 | CHECK_ERROR(special_ret != BYTE_SPECIAL); 153 | iLock = 0; 154 | return ret; 155 | } 156 | 157 | error: 158 | iLock = 0; 159 | return -1; 160 | } 161 | 162 | int cafiine_fopen(int sock, int *result, const char *path, const char *mode, int *handle) { 163 | while (iLock) 164 | usleep(5000); 165 | iLock = 1; 166 | 167 | int final_result = -1; 168 | CHECK_ERROR(sock == -1); 169 | 170 | int ret; 171 | int len_path = 0; 172 | while (path[len_path++]); 173 | int len_mode = 0; 174 | while (mode[len_mode++]); 175 | 176 | // 177 | { 178 | char buffer[1 + 8 + len_path + len_mode]; 179 | buffer[0] = BYTE_OPEN; 180 | *(int *)(buffer + 1) = len_path; 181 | *(int *)(buffer + 5) = len_mode; 182 | for (ret = 0; ret < len_path; ret++) 183 | buffer[9 + ret] = path[ret]; 184 | for (ret = 0; ret < len_mode; ret++) 185 | buffer[9 + len_path + ret] = mode[ret]; 186 | 187 | ret = sendwait(sock, buffer, 1 + 8 + len_path + len_mode); 188 | CHECK_ERROR(ret < 0); 189 | 190 | ret = recvbyte(sock); 191 | CHECK_ERROR(ret < 0); 192 | CHECK_ERROR(ret == BYTE_NORMAL); 193 | 194 | ret = recvwait(sock, result, 4); 195 | CHECK_ERROR(ret < 0); 196 | ret = recvwait(sock, handle, 4); 197 | CHECK_ERROR(ret < 0); 198 | } 199 | final_result = 0; 200 | 201 | 202 | error: 203 | iLock = 0; 204 | return final_result; 205 | } 206 | 207 | void cafiine_send_file(int sock, char *file, int size, int fd) { 208 | while (iLock) 209 | usleep(5000); 210 | iLock = 1; 211 | 212 | CHECK_ERROR(sock == -1); 213 | 214 | int ret; 215 | 216 | // create and send buffer with : [cmd id][fd][size][buffer data ...] 217 | { 218 | char buffer[1 + 4 + 4 + size]; 219 | 220 | buffer[0] = BYTE_DUMP; 221 | *(int *)(buffer + 1) = fd; 222 | *(int *)(buffer + 5) = size; 223 | for (ret = 0; ret < size; ret++) 224 | buffer[9 + ret] = file[ret]; 225 | 226 | // send buffer, wait for reply 227 | ret = sendwait(sock, buffer, 1 + 4 + 4 + size); 228 | CHECK_ERROR(ret < 0); 229 | // wait reply 230 | ret = recvbyte(sock); 231 | CHECK_ERROR(ret != BYTE_SPECIAL); 232 | } 233 | 234 | error: 235 | iLock = 0; 236 | return; 237 | } 238 | 239 | int cafiine_fread(int sock, int *result, void *ptr, int size, int fd) { 240 | while (iLock) 241 | usleep(5000); 242 | iLock = 1; 243 | 244 | CHECK_ERROR(sock == -1); 245 | 246 | int ret; 247 | char buffer[1 + 8]; 248 | buffer[0] = BYTE_READ; 249 | *(int *)(buffer + 1) = size; 250 | *(int *)(buffer + 5) = fd; 251 | ret = sendwait(sock, buffer, 1 + 8); 252 | CHECK_ERROR(ret < 0); 253 | ret = recvbyte(sock); 254 | CHECK_ERROR(ret == BYTE_NORMAL); 255 | int sz; 256 | ret = recvwait(sock, &sz, 4); 257 | CHECK_ERROR(ret < 0); 258 | ret = recvwaitlen(sock, ptr, sz); 259 | *result = sz - ret; 260 | ret = sendbyte(sock, BYTE_OK); 261 | CHECK_ERROR(ret < 0); 262 | 263 | iLock = 0; 264 | return 0; 265 | error: 266 | iLock = 0; 267 | return -1; 268 | } 269 | 270 | int cafiine_fclose(int sock, int *result, int fd,int dumpclose) { 271 | while (iLock) 272 | usleep(5000); 273 | iLock = 1; 274 | 275 | CHECK_ERROR(sock == -1); 276 | 277 | int ret; 278 | char buffer[1 + 4]; 279 | buffer[0] = BYTE_CLOSE; 280 | if(dumpclose)buffer[0] = BYTE_CLOSE_DUMP; 281 | *(int *)(buffer + 1) = fd; 282 | ret = sendwait(sock, buffer, 1 + 4); 283 | CHECK_ERROR(ret < 0); 284 | ret = recvbyte(sock); 285 | CHECK_ERROR(ret == BYTE_NORMAL); 286 | ret = recvwait(sock, result, 4); 287 | CHECK_ERROR(ret < 0); 288 | 289 | iLock = 0; 290 | return 0; 291 | error: 292 | iLock = 0; 293 | return -1; 294 | } 295 | 296 | int saviine_start_injection(int sock, long persistentID,int * mask){ 297 | while (iLock) 298 | usleep(5000); 299 | iLock = 1; 300 | 301 | int result = 0; 302 | CHECK_ERROR(sock == -1); 303 | int ret; 304 | { 305 | char buffer[1+4]; 306 | 307 | buffer[0] = BYTE_INJECTSTART; 308 | *(long *)(buffer + 1) = persistentID; 309 | ret = sendwait(sock, buffer, 1 + 4); 310 | CHECK_ERROR(ret < 0); 311 | 312 | ret = recvbyte(sock); 313 | CHECK_ERROR(ret < 0); 314 | CHECK_ERROR(ret != BYTE_SPECIAL); 315 | 316 | ret = recvwait(sock, mask, 4); 317 | CHECK_ERROR(ret < 0); 318 | CHECK_ERROR((*mask & MASK_NORMAL) != MASK_NORMAL); 319 | 320 | ret = recvbyte(sock); 321 | CHECK_ERROR(ret < 0); 322 | CHECK_ERROR(ret != BYTE_SPECIAL); 323 | result = 1; 324 | } 325 | error: 326 | iLock = 0; 327 | return result; 328 | } 329 | 330 | int saviine_end_injection(int sock){ 331 | while (iLock) 332 | usleep(5000); 333 | iLock = 1; 334 | 335 | int result = 0; 336 | CHECK_ERROR(sock == -1); 337 | int ret; 338 | { 339 | ret = sendbyte(sock, BYTE_INJECTEND); 340 | CHECK_ERROR(ret < 0); 341 | 342 | ret = recvbyte(sock); 343 | CHECK_ERROR(ret < 0); 344 | CHECK_ERROR(ret != BYTE_OK); 345 | result = 1; 346 | } 347 | error: 348 | iLock = 0; 349 | return result; 350 | } 351 | 352 | int saviine_start_dump(int sock, long persistentID,int * mask){ 353 | while (iLock) 354 | usleep(5000); 355 | iLock = 1; 356 | 357 | int result = 0; 358 | CHECK_ERROR(sock == -1); 359 | int ret; 360 | { 361 | char buffer[1+4]; 362 | 363 | buffer[0] = BYTE_DUMPSTART; 364 | *(long *)(buffer + 1) = persistentID; 365 | ret = sendwait(sock, buffer, 1 + 4); 366 | CHECK_ERROR(ret < 0); 367 | 368 | ret = recvbyte(sock); 369 | CHECK_ERROR(ret < 0); 370 | CHECK_ERROR(ret != BYTE_SPECIAL); 371 | 372 | ret = recvwait(sock, mask, 4); 373 | CHECK_ERROR(ret < 0); 374 | CHECK_ERROR((*mask & MASK_NORMAL) != MASK_NORMAL); 375 | 376 | ret = recvbyte(sock); 377 | CHECK_ERROR(ret < 0); 378 | CHECK_ERROR(ret != BYTE_SPECIAL); 379 | result = 1; 380 | } 381 | error: 382 | iLock = 0; 383 | return result; 384 | } 385 | 386 | int saviine_end_dump(int sock){ 387 | while (iLock) 388 | usleep(5000); 389 | iLock = 1; 390 | 391 | int result = 0; 392 | CHECK_ERROR(sock == -1); 393 | int ret; 394 | { 395 | ret = sendbyte(sock, BYTE_DUMPEND); 396 | CHECK_ERROR(ret < 0); 397 | 398 | ret = recvbyte(sock); 399 | CHECK_ERROR(ret < 0); 400 | CHECK_ERROR(ret != BYTE_OK); 401 | result = 1; 402 | } 403 | error: 404 | iLock = 0; 405 | return result; 406 | } 407 | 408 | int saviine_readdir(int sock, char * path,char * resultname, int * resulttype, int * filesize){ 409 | while (iLock) 410 | usleep(5000); 411 | iLock = 1; 412 | 413 | int result = 0; 414 | CHECK_ERROR(sock == -1); 415 | int ret; 416 | // create and send buffer with : [cmd id][len_path][path][filesize] 417 | { 418 | int size = 0; 419 | while (path[size++]); 420 | char buffer[1+4+size]; 421 | 422 | buffer[0] = BYTE_READ_DIR; 423 | *(int *)(buffer + 1) = size; 424 | for (ret = 0; ret < size; ret++) 425 | buffer[5 + ret] = path[ret]; 426 | 427 | // send buffer, wait for reply 428 | ret = sendwait(sock, buffer, 1+4+size); 429 | CHECK_ERROR(ret < 0); 430 | 431 | // wait reply 432 | ret = recvbyte(sock); 433 | CHECK_ERROR(ret != BYTE_OK); 434 | 435 | ret = recvbyte(sock); 436 | CHECK_ERROR(ret != BYTE_FILE && ret != BYTE_FOLDER); 437 | *resulttype = ret; 438 | size = 0; 439 | ret = recvwait(sock, &size, 4); 440 | CHECK_ERROR(ret < 0); 441 | 442 | ret = recvwait(sock, resultname, size+1); 443 | CHECK_ERROR(ret < 0); 444 | 445 | size = 0; 446 | ret = recvwait(sock, &size, 4); 447 | CHECK_ERROR(ret < 0); 448 | *filesize = size; 449 | ret = recvbyte(sock); 450 | CHECK_ERROR(ret < 0); 451 | CHECK_ERROR(ret != BYTE_SPECIAL); 452 | result = 1; 453 | 454 | } 455 | error: 456 | iLock = 0; 457 | return result; 458 | } 459 | 460 | void cafiine_send_ping(int sock, int val1, int val2) { 461 | while (iLock) 462 | usleep(5000); 463 | iLock = 1; 464 | 465 | int ret; 466 | char buffer[1 + 4 + 4]; 467 | buffer[0] = BYTE_PING; 468 | *(int *)(buffer + 1) = val1; 469 | *(int *)(buffer + 5) = val2; 470 | 471 | ret = sendwait(sock, buffer, 1 + 4 + 4); 472 | CHECK_ERROR(ret < 0); 473 | 474 | error: 475 | iLock = 0; 476 | return; 477 | } 478 | 479 | int recvwait(int sock, void *buffer, int len) { 480 | int ret; 481 | while (len > 0) { 482 | ret = recv(sock, buffer, len, 0); 483 | CHECK_ERROR(ret < 0); 484 | len -= ret; 485 | buffer += ret; 486 | } 487 | return 0; 488 | error: 489 | return ret; 490 | } 491 | 492 | int recvwaitlen(int sock, void *buffer, int len) { 493 | int ret; 494 | while (len > 0) { 495 | ret = recv(sock, buffer, len, 0); 496 | CHECK_ERROR(ret < 0); 497 | len -= ret; 498 | buffer += ret; 499 | } 500 | return 0; 501 | error: 502 | return len; 503 | } 504 | 505 | 506 | int recvbyte(int sock) { 507 | unsigned char buffer[1]; 508 | int ret; 509 | 510 | ret = recvwait(sock, buffer, 1); 511 | if (ret < 0) return ret; 512 | return buffer[0]; 513 | } 514 | 515 | int sendwait(int sock, const void *buffer, int len) { 516 | int ret; 517 | while (len > 0) { 518 | ret = send(sock, buffer, len, 0); 519 | CHECK_ERROR(ret < 0); 520 | len -= ret; 521 | buffer += ret; 522 | } 523 | return 0; 524 | error: 525 | return ret; 526 | } 527 | 528 | void log_string(int sock, const char* str, char flag_byte) { 529 | if(sock == -1) { 530 | return; 531 | } 532 | while (iLock) 533 | usleep(5000); 534 | iLock = 1; 535 | 536 | int i; 537 | int len_str = 0; 538 | while (str[len_str++]); 539 | 540 | // 541 | { 542 | char buffer[1 + 4 + len_str]; 543 | buffer[0] = flag_byte; 544 | *(int *)(buffer + 1) = len_str; 545 | for (i = 0; i < len_str; i++) 546 | buffer[5 + i] = str[i]; 547 | 548 | buffer[5 + i] = 0; 549 | 550 | sendwait(sock, buffer, 1 + 4 + len_str); 551 | } 552 | 553 | iLock = 0; 554 | } 555 | 556 | int sendbyte(int sock, unsigned char byte) { 557 | unsigned char buffer[1]; 558 | 559 | buffer[0] = byte; 560 | return sendwait(sock, buffer, 1); 561 | } 562 | -------------------------------------------------------------------------------- /src/utils/net.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_H_ 2 | #define NETWORK_H_ 3 | 4 | 5 | #define BYTE_NORMAL 0xff 6 | #define BYTE_SPECIAL 0xfe 7 | #define BYTE_OPEN 0x00 8 | #define BYTE_READ 0x01 9 | #define BYTE_CLOSE 0x02 10 | #define BYTE_OK 0x03 11 | #define BYTE_SETPOS 0x04 12 | #define BYTE_STATFILE 0x05 13 | #define BYTE_EOF 0x06 14 | #define BYTE_GETPOS 0x07 15 | #define BYTE_REQUEST 0x08 16 | #define BYTE_REQUEST_SLOW 0x09 17 | #define BYTE_HANDLE 0x0A 18 | #define BYTE_DUMP 0x0B 19 | #define BYTE_PING 0x0C 20 | #define BYTE_G_MODE 0x0D 21 | #define BYTE_MODE_D 0x0E 22 | #define BYTE_MODE_I 0x0F 23 | #define BYTE_CLOSE_DUMP 0x10 24 | #define BYTE_LOG_STR 0xfb 25 | #define BYTE_FILE 0xC0 26 | #define BYTE_FOLDER 0xC1 27 | #define BYTE_READ_DIR 0xCC 28 | #define BYTE_INJECTSTART 0x40 29 | #define BYTE_INJECTEND 0x41 30 | #define BYTE_DUMPSTART 0x42 31 | #define BYTE_DUMPEND 0x43 32 | #define BYTE_END 0xfd 33 | 34 | #define MASK_NORMAL 0x8000 35 | #define MASK_USER 0x0100 36 | #define MASK_COMMON 0x0200 37 | #define MASK_COMMON_CLEAN 0x0400 38 | 39 | void cafiine_connect(int *psock,u32 ip); 40 | void cafiine_disconnect(int sock); 41 | int cafiine_handshake(int sock); 42 | int getMode(int sock,int * result); 43 | int cafiine_fsetpos(int sock, int *result, int fd, int set); 44 | int cafiine_send_handle(int sock, const char *path, int handle); 45 | int cafiine_fopen(int sock, int *result, const char *path, const char *mode, int *handle); 46 | void cafiine_send_file(int sock, char *file, int size, int fd); 47 | int cafiine_fread(int sock, int *result, void *ptr, int size, int fd); 48 | int cafiine_fclose(int sock, int *result, int fd,int dumpclose); 49 | int saviine_start_injection(int sock, long persistentID,int * mask); 50 | int saviine_end_injection(int sock); 51 | int saviine_start_dump(int sock, long persistentID,int * mask); 52 | int saviine_end_dump(int sock); 53 | int saviine_readdir(int sock, char * path,char * resultname, int * resulttype, int * filesize); 54 | void cafiine_send_ping(int sock, int val1, int val2); 55 | int recvwait(int sock, void *buffer, int len); 56 | int recvwaitlen(int sock, void *buffer, int len); 57 | int recvbyte(int sock); 58 | int sendwait(int sock, const void *buffer, int len); 59 | void log_string(int sock, const char* str, char flag_byte); 60 | int sendbyte(int sock, unsigned char byte); 61 | 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/utils/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_H_ 2 | #define __UTILS_H_ 3 | 4 | #include 5 | #include "../common/types.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #define FlushBlock(addr) asm volatile("dcbf %0, %1\n" \ 12 | "icbi %0, %1\n" \ 13 | "sync\n" \ 14 | "eieio\n" \ 15 | "isync\n" \ 16 | : \ 17 | :"r"(0), "r"(((addr) & ~31)) \ 18 | :"memory", "ctr", "lr", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" \ 19 | ); 20 | 21 | #define LIMIT(x, min, max) \ 22 | ({ \ 23 | typeof( x ) _x = x; \ 24 | typeof( min ) _min = min; \ 25 | typeof( max ) _max = max; \ 26 | ( ( ( _x ) < ( _min ) ) ? ( _min ) : ( ( _x ) > ( _max ) ) ? ( _max) : ( _x ) ); \ 27 | }) 28 | 29 | #define DegToRad(a) ( (a) * 0.01745329252f ) 30 | #define RadToDeg(a) ( (a) * 57.29577951f ) 31 | 32 | #define ALIGN4(x) (((x) + 3) & ~3) 33 | #define ALIGN32(x) (((x) + 31) & ~31) 34 | 35 | // those work only in powers of 2 36 | #define ROUNDDOWN(val, align) ((val) & ~(align-1)) 37 | #define ROUNDUP(val, align) ROUNDDOWN(((val) + (align-1)), align) 38 | 39 | #define le16(i) ((((u16) ((i) & 0xFF)) << 8) | ((u16) (((i) & 0xFF00) >> 8))) 40 | #define le32(i) ((((u32)le16((i) & 0xFFFF)) << 16) | ((u32)le16(((i) & 0xFFFF0000) >> 16))) 41 | #define le64(i) ((((u64)le32((i) & 0xFFFFFFFFLL)) << 32) | ((u64)le32(((i) & 0xFFFFFFFF00000000LL) >> 32))) 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | #endif // __UTILS_H_ 48 | --------------------------------------------------------------------------------