├── .gitignore ├── Makefile ├── icon.png ├── readme.md └── source ├── common.h ├── filebrowser ├── common.h ├── dir.c ├── dir.h ├── draw.c ├── draw.h ├── filebrowser.c ├── filebrowser.h ├── sort.c └── sort.h ├── keyboard.c ├── keyboard.h ├── main.c ├── uds.c ├── uds.h ├── ui.c └── ui.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | *.elf 3 | *.cia 4 | *.bnr 5 | *.3dsx 6 | *.smdh 7 | *.icn 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITARM)/3ds_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # 19 | # NO_SMDH: if set to anything, no SMDH file is generated. 20 | # ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) 21 | # APP_TITLE is the name of the app stored in the SMDH file (Optional) 22 | # APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) 23 | # APP_AUTHOR is the author of the app stored in the SMDH file (Optional) 24 | # ICON is the filename of the icon (.png), relative to the project folder. 25 | # If not set, it attempts to use one of the following (in this order): 26 | # - .png 27 | # - icon.png 28 | # - /default_icon.png 29 | #--------------------------------------------------------------------------------- 30 | TARGET := $(notdir $(CURDIR)) 31 | BUILD := build 32 | SOURCES := source source/filebrowser 33 | DATA := data 34 | INCLUDES := include 35 | #ROMFS := romfs 36 | 37 | APP_TITLE := LocalFileShare 38 | APP_DESCRIPTION := Transfer files between consoles without a PC 39 | APP_AUTHOR := LiquidFenrir 40 | ICON := icon.png 41 | 42 | #--------------------------------------------------------------------------------- 43 | # options for code generation 44 | #--------------------------------------------------------------------------------- 45 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft 46 | 47 | CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \ 48 | -fomit-frame-pointer -ffunction-sections \ 49 | $(ARCH) 50 | 51 | CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE 52 | 53 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 54 | 55 | ASFLAGS := -g $(ARCH) 56 | LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 57 | 58 | LIBS := -lctru -lm -lz 59 | 60 | #--------------------------------------------------------------------------------- 61 | # list of directories containing libraries, this must be the top level containing 62 | # include and lib 63 | #--------------------------------------------------------------------------------- 64 | LIBDIRS := $(CTRULIB) $(PORTLIBS) 65 | 66 | 67 | #--------------------------------------------------------------------------------- 68 | # no real need to edit anything past this point unless you need to add additional 69 | # rules for different file extensions 70 | #--------------------------------------------------------------------------------- 71 | ifneq ($(BUILD),$(notdir $(CURDIR))) 72 | #--------------------------------------------------------------------------------- 73 | 74 | export OUTPUT := $(CURDIR)/$(TARGET) 75 | export TOPDIR := $(CURDIR) 76 | 77 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 78 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 79 | 80 | export DEPSDIR := $(CURDIR)/$(BUILD) 81 | 82 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 83 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 84 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 85 | PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) 86 | SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) 87 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 88 | 89 | #--------------------------------------------------------------------------------- 90 | # use CXX for linking C++ projects, CC for standard C 91 | #--------------------------------------------------------------------------------- 92 | ifeq ($(strip $(CPPFILES)),) 93 | #--------------------------------------------------------------------------------- 94 | export LD := $(CC) 95 | #--------------------------------------------------------------------------------- 96 | else 97 | #--------------------------------------------------------------------------------- 98 | export LD := $(CXX) 99 | #--------------------------------------------------------------------------------- 100 | endif 101 | #--------------------------------------------------------------------------------- 102 | 103 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 104 | $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ 105 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 106 | 107 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 108 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 109 | -I$(CURDIR)/$(BUILD) 110 | 111 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 112 | 113 | ifeq ($(strip $(ICON)),) 114 | icons := $(wildcard *.png) 115 | ifneq (,$(findstring $(TARGET).png,$(icons))) 116 | export APP_ICON := $(TOPDIR)/$(TARGET).png 117 | else 118 | ifneq (,$(findstring icon.png,$(icons))) 119 | export APP_ICON := $(TOPDIR)/icon.png 120 | endif 121 | endif 122 | else 123 | export APP_ICON := $(TOPDIR)/$(ICON) 124 | endif 125 | 126 | ifeq ($(strip $(NO_SMDH)),) 127 | export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh 128 | endif 129 | 130 | ifneq ($(ROMFS),) 131 | export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) 132 | endif 133 | 134 | .PHONY: $(BUILD) clean all 135 | 136 | #--------------------------------------------------------------------------------- 137 | all: $(BUILD) 138 | 139 | $(BUILD): 140 | @[ -d $@ ] || mkdir -p $@ 141 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 142 | 143 | #--------------------------------------------------------------------------------- 144 | clean: 145 | @echo clean ... 146 | @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf 147 | 148 | 149 | #--------------------------------------------------------------------------------- 150 | else 151 | 152 | DEPENDS := $(OFILES:.o=.d) 153 | 154 | #--------------------------------------------------------------------------------- 155 | # main targets 156 | #--------------------------------------------------------------------------------- 157 | ifeq ($(strip $(NO_SMDH)),) 158 | $(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh 159 | else 160 | $(OUTPUT).3dsx : $(OUTPUT).elf 161 | endif 162 | 163 | $(OUTPUT).elf : $(OFILES) 164 | 165 | #--------------------------------------------------------------------------------- 166 | # you need a rule like this for each extension you use as binary data 167 | #--------------------------------------------------------------------------------- 168 | %.bin.o : %.bin 169 | #--------------------------------------------------------------------------------- 170 | @echo $(notdir $<) 171 | @$(bin2o) 172 | 173 | #--------------------------------------------------------------------------------- 174 | # rules for assembling GPU shaders 175 | #--------------------------------------------------------------------------------- 176 | define shader-as 177 | $(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) 178 | picasso -o $(CURBIN) $1 179 | bin2s $(CURBIN) | $(AS) -o $@ 180 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h 181 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h 182 | echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h 183 | endef 184 | 185 | %.shbin.o : %.v.pica %.g.pica 186 | @echo $(notdir $^) 187 | @$(call shader-as,$^) 188 | 189 | %.shbin.o : %.v.pica 190 | @echo $(notdir $<) 191 | @$(call shader-as,$<) 192 | 193 | %.shbin.o : %.shlist 194 | @echo $(notdir $<) 195 | @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) 196 | 197 | -include $(DEPENDS) 198 | 199 | #--------------------------------------------------------------------------------------- 200 | endif 201 | #--------------------------------------------------------------------------------------- 202 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LiquidFenrir/LocalFileShare/372a8f5f70e8ef35e1dd6e79f05c54221d410afc/icon.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # LocalFileShare 2 | 3 | Working attempt at file "sharing" (sending/receiving) using local wlan on 3ds 4 | 5 | I recommend hosting on an o3ds (*NOT* a 2DS) as it has a physical switch for wireless, so if it desyncs just flip it and press START on all consoles then retry. 6 | -------------------------------------------------------------------------------- /source/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include <3ds.h> 13 | 14 | PrintConsole debugConsole, uiConsole; 15 | -------------------------------------------------------------------------------- /source/filebrowser/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include <3ds.h> 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // according to wikipedia, the max FAT32 path length is 255 UTF-16 characters, so 0xFF * 2 (because the 16 in UTF-16 means 16 bits = 2 bytes) (shifting left of 1 is the same as multiplying by 2) 11 | #define MAX_PATH_LEN (0xFF << 1) 12 | 13 | typedef struct { 14 | char name[MAX_PATH_LEN+1]; 15 | bool isFile; 16 | } dirInfo; 17 | -------------------------------------------------------------------------------- /source/filebrowser/dir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dir.h" 3 | 4 | int listdir(char * path, dirInfo * dirInfoArray) 5 | { 6 | DIR *dir; 7 | struct dirent *ent; 8 | int count = 0; 9 | 10 | if ((dir = opendir(path)) != NULL) { 11 | while ((ent = readdir(dir)) != NULL) { 12 | strcpy(dirInfoArray[count].name, ent->d_name); 13 | dirInfoArray[count].isFile = (ent->d_type == 8); 14 | count++; 15 | } 16 | closedir (dir); 17 | } 18 | 19 | return count; 20 | } 21 | -------------------------------------------------------------------------------- /source/filebrowser/dir.h: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | int listdir(char * path, dirInfo * dirInfoArray); 4 | -------------------------------------------------------------------------------- /source/filebrowser/draw.c: -------------------------------------------------------------------------------- 1 | #include "draw.h" 2 | 3 | //width in characters of the screen you're drawing on. 50 for the top screen, 40 for bottom 4 | #define SCREEN_WIDTH 50 5 | #define SCREEN_HEIGHT 30 6 | 7 | #define OFFSET 1 //offset in characters of the file list from the location bar 8 | 9 | #define MAX_ENTRIES_PER_SCREEN (SCREEN_HEIGHT-1-OFFSET*2) 10 | 11 | static int scroll = 0; 12 | 13 | static char uparrow[] = {30, 0}; 14 | static char downarrow[] = {31, 0}; 15 | 16 | void drawDirList(dirInfo * dirInfoArray, char * currentPath, int currentDir, int dirCount) 17 | { 18 | consoleClear(); 19 | if (strlen(currentPath) <= SCREEN_WIDTH) printf("\x1b[0;0H\x1b[47;30m%s\x1b[0m", currentPath); 20 | else printf("\x1b[0;0H\x1b[47;30m...%s\x1b[0m", ¤tPath[strlen(currentPath)-SCREEN_WIDTH+3]); 21 | 22 | if (currentDir == 0) { 23 | scroll = 0; 24 | } 25 | else if ((dirCount > MAX_ENTRIES_PER_SCREEN) && (currentDir == dirCount-1)) { 26 | scroll = dirCount - MAX_ENTRIES_PER_SCREEN; 27 | } 28 | else if (currentDir >= (MAX_ENTRIES_PER_SCREEN + scroll)) { 29 | scroll++; 30 | } 31 | else if ((currentDir - scroll) < 0) { 32 | scroll--; 33 | } 34 | 35 | if (scroll != 0) printf("\x1b[%i;0H%s", OFFSET+1, uparrow); 36 | 37 | if ((dirCount > MAX_ENTRIES_PER_SCREEN) && scroll != (dirCount - MAX_ENTRIES_PER_SCREEN)) printf("\x1b[%i;0H%s", MAX_ENTRIES_PER_SCREEN+OFFSET, downarrow); 38 | 39 | for (int i = scroll; (i-scroll) < MAX_ENTRIES_PER_SCREEN; i++) { 40 | //selected dir has white background, others have black 41 | int bgcolor = (i == currentDir) ? 47 : 40; 42 | //if it's a file, it has a blue name. otherwise, reverse of background 43 | int txtcolor = (dirInfoArray[i].isFile) ? 36 : ((i == currentDir) ? 30 : 37); 44 | if (dirInfoArray[i].name != NULL) printf("\x1b[%i;2H\x1b[%i;%im%s\x1b[0m", i+OFFSET+1-scroll, bgcolor, txtcolor, dirInfoArray[i].name); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /source/filebrowser/draw.h: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | void drawDirList(dirInfo * dirInfoArray, char * currentPath, int currentDir, int dirCount); 4 | -------------------------------------------------------------------------------- /source/filebrowser/filebrowser.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "dir.h" 3 | #include "draw.h" 4 | #include "sort.h" 5 | 6 | static char currentPath[MAX_PATH_LEN+1]; //for the ending nullbyte 7 | 8 | static dirInfo dirInfoArray[256]; 9 | static int dirCount = 0; 10 | static int currentDir = 0; 11 | 12 | char * filebrowser(void) { 13 | chdir("/"); 14 | 15 | goto change; 16 | 17 | while (aptMainLoop()) { 18 | 19 | //things in there will only run if you do a goto 20 | //otherwise the screen would flicker because of constantly clearing 21 | if (false) { 22 | change: 23 | currentDir = 0; 24 | getcwd(currentPath, MAX_PATH_LEN+1); 25 | dirCount = listdir(currentPath, dirInfoArray); 26 | for (int i = dirCount; i < 256; i++) { 27 | dirInfoArray[i].name[0] = 0; 28 | dirInfoArray[i].isFile = false; 29 | } 30 | sortDirList(dirInfoArray, dirCount); 31 | 32 | draw: 33 | if (currentDir > dirCount-1) currentDir = dirCount-1; 34 | if (currentDir < 0) currentDir = 0; 35 | drawDirList(dirInfoArray, currentPath, currentDir, dirCount); 36 | gfxFlushBuffers(); 37 | gfxSwapBuffers(); 38 | gspWaitForVBlank(); 39 | } 40 | 41 | hidScanInput(); 42 | 43 | if (hidKeysDown() & KEY_START) { 44 | break; 45 | } 46 | else if (hidKeysDown() & KEY_A) { 47 | if (dirCount != 0) { 48 | if (dirInfoArray[currentDir].isFile) { 49 | //return the path to the file 50 | return strcat(currentPath, dirInfoArray[currentDir].name); 51 | } 52 | else { 53 | chdir(dirInfoArray[currentDir].name); 54 | goto change; 55 | } 56 | } 57 | } 58 | else if (hidKeysDown() & KEY_X) { 59 | return currentPath; 60 | } 61 | else if (hidKeysDown() & KEY_B) { 62 | chdir(".."); 63 | goto change; 64 | } 65 | else if (hidKeysDown() & KEY_LEFT) { 66 | currentDir = 0; 67 | goto draw; 68 | } 69 | else if (hidKeysDown() & KEY_RIGHT) { 70 | currentDir = dirCount-1; 71 | goto draw; 72 | } 73 | else if (hidKeysDown() & KEY_UP) { 74 | currentDir--; 75 | goto draw; 76 | } 77 | else if (hidKeysDown() & KEY_DOWN) { 78 | currentDir++; 79 | goto draw; 80 | } 81 | 82 | gfxFlushBuffers(); 83 | gfxSwapBuffers(); 84 | gspWaitForVBlank(); 85 | } 86 | 87 | return ""; 88 | } 89 | -------------------------------------------------------------------------------- /source/filebrowser/filebrowser.h: -------------------------------------------------------------------------------- 1 | #ifndef FILEBROWSER_H_ 2 | #define FILEBROWSER_H_ 3 | 4 | char * filebrowser(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /source/filebrowser/sort.c: -------------------------------------------------------------------------------- 1 | #include "sort.h" 2 | 3 | int checkSorted(const void * a, const void * b) 4 | { 5 | dirInfo * entryA = (dirInfo *)a; 6 | dirInfo * entryB = (dirInfo *)b; 7 | 8 | if ((entryA->isFile && entryB->isFile) || (!entryA->isFile && !entryB->isFile)) { 9 | return strcmp(entryA->name, entryB->name); 10 | } 11 | else if (entryA->isFile) { 12 | return 1; 13 | } 14 | else if (entryB->isFile) { 15 | return -1; 16 | } 17 | 18 | return 0; 19 | } 20 | 21 | void sortDirList(dirInfo * dirInfoArrayArray, int dirCount) 22 | { 23 | qsort(dirInfoArrayArray, dirCount, sizeof(dirInfo), &checkSorted); 24 | } 25 | -------------------------------------------------------------------------------- /source/filebrowser/sort.h: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | void sortDirList(dirInfo * dirInfoArrayArray, int dirCount); 4 | -------------------------------------------------------------------------------- /source/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "keyboard.h" 2 | 3 | char * getStr(int max) 4 | { 5 | char * str = malloc(max); 6 | memset(str, 0, max); 7 | static SwkbdState swkbd; 8 | swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 1, max); 9 | swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY, 0, 0); 10 | swkbdInputText(&swkbd, str, max); 11 | return str; 12 | } 13 | -------------------------------------------------------------------------------- /source/keyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | char * getStr(int max); 6 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | #include "uds.h" 2 | 3 | PrintConsole debugConsole, uiConsole; 4 | 5 | int main() 6 | { 7 | gfxInitDefault(); 8 | udsInit(0x3000, NULL); 9 | 10 | consoleInit(GFX_BOTTOM, &debugConsole); 11 | consoleInit(GFX_TOP, &uiConsole); 12 | consoleSelect(&uiConsole); 13 | 14 | const char * serverquestions[] = {"Connect to server", "Host server"}; 15 | int selected = display_menu(serverquestions, 2, "What do you want to do?"); 16 | consoleClear(); 17 | consoleSelect(&debugConsole); 18 | 19 | if (selected == -1) goto exit; 20 | startComm(selected); 21 | 22 | exit: 23 | udsExit(); 24 | gfxExit(); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /source/uds.c: -------------------------------------------------------------------------------- 1 | // #include 2 | #include "uds.h" 3 | 4 | // #define MAX_SIZE_FOR_SINGLE (2*1048576) //2 Mio 5 | #define BLOCKSIZE 0x400 // 1 Kio, actual maximum is 0x5C6 but this is cleaner 6 | 7 | static Result ret = 0; 8 | static u32 pos; 9 | 10 | static bool client = false; 11 | 12 | static u32 wlancommID = 0x04114800; 13 | static u8 appdata[0x14] = {0x69, 0x8a, 0x05, 0x5c}; 14 | 15 | static u8 data_channel = 1; 16 | static udsNetworkStruct networkstruct; 17 | static udsBindContext bindctx; 18 | 19 | static u32 recv_buffer_size = UDS_DEFAULT_RECVBUFSIZE; 20 | static u32 * tmpbuf; 21 | static size_t tmpbuf_size; 22 | 23 | static size_t actual_size = 0; 24 | static u16 src_NetworkNodeID; 25 | 26 | void print_constatus() 27 | { 28 | udsConnectionStatus constatus; 29 | 30 | //By checking the output of udsGetConnectionStatus you can check for nodes (including the current one) which just (dis)connected, etc. 31 | ret = udsGetConnectionStatus(&constatus); 32 | if (R_FAILED(ret)) printf("udsGetConnectionStatus() returned 0x%08x.\n", (unsigned int)ret); 33 | else 34 | { 35 | printf("constatus:\nstatus=0x%x\n", (unsigned int)constatus.status); 36 | printf("1=0x%x\n", (unsigned int)constatus.unk_x4); 37 | printf("cur_NetworkNodeID=0x%x\n", (unsigned int)constatus.cur_NetworkNodeID); 38 | printf("unk_xa=0x%x\n", (unsigned int)constatus.unk_xa); 39 | for(pos = 0; pos < (0x20 >> 2); pos++) printf("%u=0x%x ", (unsigned int)pos+3, (unsigned int)constatus.unk_xc[pos]); 40 | printf("\ntotal_nodes=0x%x\n", (unsigned int)constatus.total_nodes); 41 | printf("max_nodes=0x%x\n", (unsigned int)constatus.max_nodes); 42 | printf("node_bitmask=0x%x\n", (unsigned int)constatus.total_nodes); 43 | } 44 | } 45 | 46 | void initCommID(void) 47 | { 48 | psInit(); 49 | u32 tempbuf; 50 | PS_GenerateRandomBytes(&tempbuf, 4); 51 | wlancommID ^= tempbuf; 52 | psExit(); 53 | } 54 | 55 | char * getAppDataStr(udsNetworkScanInfo * network) 56 | { 57 | char * retval = NULL; 58 | u8 out_appdata[0x14]; 59 | 60 | ret = udsGetNetworkStructApplicationData(&network->network, out_appdata, sizeof(out_appdata), &actual_size); 61 | if (R_FAILED(ret) || (actual_size != sizeof(out_appdata))) 62 | { 63 | printf("udsGetNetworkStructApplicationData() returned 0x%08x. actual_size = 0x%x.\n", (unsigned int)ret, actual_size); 64 | return retval; 65 | } 66 | 67 | if (memcmp(out_appdata, appdata, 4) != 0) 68 | { 69 | puts("The first 4-bytes of appdata is invalid.\n"); 70 | return retval; 71 | } 72 | 73 | return strdup((char*)&out_appdata[4]); 74 | } 75 | 76 | void waitForConnection(void) 77 | { 78 | if (udsWaitConnectionStatusEvent(false, false)) 79 | { 80 | printf("Constatus event signaled.\n"); 81 | print_constatus(); 82 | } 83 | } 84 | 85 | void startServer(void) 86 | { 87 | udsGenerateDefaultNetworkStruct(&networkstruct, wlancommID, 0, UDS_MAXNODES); 88 | 89 | puts("Creating the network...\n"); 90 | 91 | puts("Type in the password you want for the network.\n"); 92 | char * passphrase = getStr(64); 93 | printf("Password chosen: '%s'\n", passphrase); 94 | 95 | ret = udsCreateNetwork(&networkstruct, passphrase, strlen(passphrase)+1, &bindctx, data_channel, recv_buffer_size); 96 | if(R_FAILED(ret)) 97 | { 98 | printf("udsstartServer() returned 0x%08x.\n", (unsigned int)ret); 99 | return; 100 | } 101 | free(passphrase); 102 | 103 | puts("Type in the name you want for the network.\n"); 104 | char * name = getStr(10); 105 | printf("Name chosen: '%s'\n", name); 106 | strncpy((char*)&appdata[4], name, sizeof(appdata)-1); 107 | free(name); 108 | 109 | ret = udsSetApplicationData(appdata, sizeof(appdata)); 110 | if(R_FAILED(ret)) 111 | { 112 | printf("udsSetApplicationData() returned 0x%08x.\n", (unsigned int)ret); 113 | udsDestroyNetwork(); 114 | udsUnbind(&bindctx); 115 | return; 116 | } 117 | 118 | u32 tmp = 0; 119 | ret = udsGetChannel((u8*)&tmp); //Normally you don't need to use this. 120 | printf("udsGetChannel() returned 0x%08x. channel = %u.\n", (unsigned int)ret, (unsigned int)tmp); 121 | if(R_FAILED(ret)) 122 | { 123 | udsDestroyNetwork(); 124 | udsUnbind(&bindctx); 125 | return; 126 | } 127 | } 128 | 129 | void connectToNetwork(udsNetworkScanInfo * network) 130 | { 131 | u32 tmp = 0; 132 | 133 | printf("network: total nodes = %u.\n", (unsigned int)network->network.total_nodes); 134 | 135 | char * tmpstr = malloc(256); 136 | for (pos = 0; pos < UDS_MAXNODES; pos++) 137 | { 138 | if (!udsCheckNodeInfoInitialized(&network->nodes[pos])) continue; 139 | 140 | memset(tmpstr, 0, 256); 141 | 142 | ret = udsGetNodeInfoUsername(&network->nodes[pos], tmpstr); 143 | if (R_FAILED(ret)) 144 | { 145 | printf("udsGetNodeInfoUsername() returned 0x%08x.\n", (unsigned int)ret); 146 | return; 147 | } 148 | 149 | printf("node%u username: %s\n", (unsigned int)pos, tmpstr); 150 | } 151 | free(tmpstr); 152 | 153 | tmpstr = getAppDataStr(network); 154 | printf("String from appdata: %s\n", tmpstr); 155 | free(tmpstr); 156 | 157 | puts("Connecting to the network as a client...\n"); 158 | 159 | puts("Type in the password of the network.\n"); 160 | char * passphrase = getStr(64); 161 | printf("Password used: '%s'\n", passphrase); 162 | 163 | for(pos=0; pos<10; pos++) 164 | { 165 | ret = udsConnectNetwork(&network->network, passphrase, strlen(passphrase)+1, &bindctx, UDS_BROADCAST_NETWORKNODEID, UDSCONTYPE_Client, data_channel, recv_buffer_size); 166 | if (R_FAILED(ret)) printf("udsConnectNetwork() returned 0x%08x.\n", (unsigned int)ret); 167 | else break; 168 | 169 | if (pos == 10) goto quit; 170 | 171 | printf("Connected.\n"); 172 | 173 | tmp = 0; 174 | ret = udsGetChannel((u8*)&tmp);//Normally you don't need to use this. 175 | printf("udsGetChannel() returned 0x%08x. channel = %u.\n", (unsigned int)ret, (unsigned int)tmp); 176 | if (R_FAILED(ret)) goto quit; 177 | } 178 | 179 | tmpstr = getAppDataStr(network); 180 | printf("String from appdata: '%s'\n", tmpstr); 181 | free(tmpstr); 182 | 183 | client = true; 184 | 185 | quit: 186 | free(passphrase); 187 | } 188 | 189 | void startClient(void) 190 | { 191 | udsNetworkScanInfo *networks = NULL; 192 | size_t total_networks = 0; 193 | 194 | tmpbuf_size = 0x4000; 195 | tmpbuf = malloc(tmpbuf_size); 196 | if (tmpbuf == NULL) 197 | { 198 | puts("Failed to allocate tmpbuf for beacon data.\n"); 199 | return; 200 | } 201 | 202 | u32 i; 203 | 204 | scan: 205 | total_networks = 0; 206 | memset(tmpbuf, 0, sizeof(tmpbuf_size)); 207 | ret = udsScanBeacons(tmpbuf, tmpbuf_size, &networks, &total_networks, wlancommID, 0, NULL, false); 208 | printf("udsScanBeacons() returned 0x%08x.\ntotal_networks=%u.\n", (unsigned int)ret, (unsigned int)total_networks); 209 | 210 | if (total_networks) 211 | { 212 | char * names[total_networks]; 213 | for (i = 0; i < total_networks; i++) { 214 | names[i] = getAppDataStr(&networks[i]); 215 | if (names[i] == NULL) goto quit; 216 | } 217 | 218 | consoleSelect(&uiConsole); 219 | int selected = display_menu((const char **)names, total_networks, "Select a network to connect to or press Y to refresh:"); 220 | consoleClear(); 221 | consoleSelect(&debugConsole); 222 | 223 | for (i = 0; i < total_networks; i++) { 224 | free(names[i]); 225 | } 226 | 227 | if (selected == -1) goto quit; 228 | if (selected == -2) goto scan; 229 | 230 | printf("Connecting to network #%u\n", selected); 231 | connectToNetwork(&networks[selected]); 232 | goto quit; 233 | } 234 | else 235 | { 236 | const char * refreshStr[] = {"No networks found."}; 237 | consoleSelect(&uiConsole); 238 | int selected = display_menu(refreshStr, 1, "Select a network to connect to or press Y to refresh:"); 239 | consoleClear(); 240 | consoleSelect(&debugConsole); 241 | if (selected != -1) goto scan; 242 | goto quit; 243 | } 244 | 245 | quit: 246 | free(networks); 247 | return; 248 | } 249 | 250 | Result wait(bool udswait) 251 | { 252 | if (udswait) udsWaitDataAvailable(&bindctx, false, true); //Check whether data is available via udsPullPacket(). 253 | for (int i = 0; i < 10; i++) 254 | { 255 | memset(tmpbuf, 0, tmpbuf_size); 256 | actual_size = 0; 257 | src_NetworkNodeID = 0; 258 | ret = udsPullPacket(&bindctx, tmpbuf, tmpbuf_size, &actual_size, &src_NetworkNodeID); 259 | if(R_FAILED(ret)) 260 | { 261 | printf("udsPullPacket() returned 0x%08x.\n", (unsigned int)ret); 262 | break; 263 | } 264 | if (actual_size != 0) break; 265 | } 266 | return ret; 267 | } 268 | 269 | Result sendData(const void * data, u32 size) 270 | { 271 | ret = udsSendTo(UDS_BROADCAST_NETWORKNODEID, data_channel, UDS_SENDFLAG_Default, (u8 *)data, size); 272 | if (R_FAILED(ret)) printf("udsPullPacket() returned 0x%08x.\n", (unsigned int)ret); 273 | return ret; 274 | } 275 | 276 | Result sendStr(char * str) 277 | { 278 | printf("Sending string:\n'%s'\n", str); 279 | return sendData(str, strlen(str)+1); 280 | } 281 | 282 | void sendFile(void) 283 | { 284 | puts("Select a file to send\n"); 285 | consoleSelect(&uiConsole); 286 | char * path = filebrowser(); 287 | consoleClear(); 288 | consoleSelect(&debugConsole); 289 | 290 | if (path[0] == 0x0) return; 291 | 292 | FILE * fh; 293 | u8 * tempbuf = NULL; 294 | 295 | fh = fopen(path, "rb"); 296 | fseek(fh, 0, SEEK_END); 297 | u32 size = ftell(fh); 298 | fseek(fh, 0, SEEK_SET); 299 | 300 | sendStr("SENDING FILE"); 301 | char * name = basename(path); 302 | sendStr(name); 303 | free(name); 304 | sendData(&size, 4); 305 | 306 | //wait for the receiver to have selected where he wants to save the file 307 | if (wait(true)) goto quit; 308 | if (tmpbuf[0] != 0) goto quit; 309 | 310 | consoleClear(); 311 | puts("Sending file\n"); 312 | 313 | u32 lastblock = size%BLOCKSIZE; 314 | u32 blocksamount = (size - lastblock)/BLOCKSIZE; 315 | sendData(&blocksamount, 4); 316 | sendData(&lastblock, 4); 317 | printf("Sending %lu blocks, lastblock size %lu\n", blocksamount, lastblock); 318 | 319 | tempbuf = malloc(BLOCKSIZE); 320 | u32 oldblock = 0; 321 | u32 block = 0; 322 | u32 used_size = BLOCKSIZE; 323 | 324 | while (block != blocksamount) 325 | { 326 | progressBar(blocksamount, block); 327 | 328 | if (wait(false)) goto quit; 329 | if (actual_size == 0) continue; 330 | 331 | block = (u32 )tmpbuf[0]; 332 | 333 | if (block != (oldblock+1)) fseek(fh, BLOCKSIZE*block, SEEK_SET); 334 | if (block == blocksamount) used_size = lastblock; 335 | 336 | fread(tempbuf, 1, used_size, fh); 337 | sendData(tempbuf, used_size); 338 | } 339 | free(tempbuf); 340 | 341 | quit: 342 | fclose(fh); 343 | instructions(); 344 | } 345 | 346 | void receiveFile(void) 347 | { 348 | puts("Waiting for a file to receive...\n"); 349 | if (wait(true)) return; 350 | 351 | if (strcmp((char *)tmpbuf, "SENDING FILE") != 0) return; 352 | if (wait(true)) return; 353 | char * name = strdup((char *)tmpbuf); 354 | if (wait(true)) return; 355 | u32 size = tmpbuf[0]; 356 | 357 | printf("Where do you want to save file %s ?\nFile size: %lu\n", name, size); 358 | printf("Press X in the folder you want to save the file to.\n"); 359 | consoleSelect(&uiConsole); 360 | char * path = filebrowser(); 361 | consoleClear(); 362 | consoleSelect(&debugConsole); 363 | 364 | u32 val = 0; 365 | if (path[0] == 0x0) //if user quit instead of selecting a folder, path is empty 366 | { 367 | val++; 368 | sendData(&val, 4); 369 | return; 370 | } 371 | 372 | path = strcat(path, name); 373 | printf("Saving file to %s\n", path); 374 | FILE * fh = fopen(path, "wb"); 375 | 376 | sendData(&val, 4); 377 | 378 | consoleClear(); 379 | puts("Receiving file.\n"); 380 | 381 | if (wait(true)) goto quit; 382 | u32 blocksamount = (u32 )tmpbuf[0]; 383 | if (wait(true)) goto quit; 384 | u32 lastblock = (u32 )tmpbuf[0]; 385 | printf("Receiving %lu blocks, lastblock size %lu\n", blocksamount, lastblock); 386 | 387 | for (u32 i = 0; i <= blocksamount; i++) 388 | { 389 | progressBar(blocksamount, i); 390 | 391 | request: 392 | sendData(&i, 4); 393 | 394 | if (wait(false)) goto quit; 395 | if (actual_size == 0) goto request; 396 | 397 | fwrite(tmpbuf, 1, actual_size, fh); 398 | } 399 | 400 | quit: 401 | fclose(fh); 402 | instructions(); 403 | } 404 | 405 | void quitNetwork(void) 406 | { 407 | free(tmpbuf); 408 | 409 | if (client) udsDestroyNetwork(); 410 | else udsDisconnectNetwork(); 411 | 412 | udsUnbind(&bindctx); 413 | } 414 | 415 | void startComm(bool server) 416 | { 417 | initCommID(); 418 | 419 | if (server) startServer(); 420 | else startClient(); 421 | 422 | waitForConnection(); 423 | 424 | tmpbuf_size = UDS_DATAFRAME_MAXSIZE; // 0x5C6 bytes 425 | tmpbuf = malloc(tmpbuf_size); 426 | 427 | instructions(); 428 | 429 | while (true) 430 | { 431 | gspWaitForVBlank(); 432 | hidScanInput(); 433 | u32 kDown = hidKeysDown(); 434 | 435 | if (kDown & KEY_START) break; 436 | else if (kDown & KEY_X) receiveFile(); 437 | else if (kDown & KEY_Y) sendFile(); 438 | else if (kDown & KEY_A) print_constatus(); 439 | 440 | waitForConnection(); 441 | } 442 | 443 | quitNetwork(); 444 | } 445 | -------------------------------------------------------------------------------- /source/uds.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #include "ui.h" 6 | #include "keyboard.h" 7 | #include "filebrowser/filebrowser.h" 8 | 9 | void startComm(bool server); 10 | -------------------------------------------------------------------------------- /source/ui.c: -------------------------------------------------------------------------------- 1 | #include "ui.h" 2 | 3 | #define PERCENTAGE_PER_CHAR 5 4 | #define PERCENT_CHAR "#" 5 | 6 | int display_menu(const char *menu_entries[], const int entries_amount, const char *headerstr) 7 | { 8 | bool redraw = true; 9 | u32 kDown; 10 | int ctr = 0; 11 | 12 | consoleClear(); 13 | 14 | while (aptMainLoop()) 15 | { 16 | 17 | hidScanInput(); 18 | kDown = hidKeysDown(); 19 | 20 | if (redraw) 21 | { 22 | printf("\x1b[0;0H"); //places cursor at top left of screen 23 | printf("%s\n\n", headerstr); 24 | for (int i = 0; i < entries_amount; i++) 25 | { 26 | printf("%s %s\n", (ctr == i) ? "#" : " ", menu_entries[i]); 27 | } 28 | redraw = false; 29 | } 30 | 31 | if (kDown) redraw = true; 32 | 33 | if (kDown & KEY_A) return ctr; 34 | else if (kDown & (KEY_B | KEY_START)) break; // Exit 35 | else if (kDown & KEY_Y) return -2; 36 | // else if (kDown & KEY_X) return -3; 37 | 38 | else if (kDown & KEY_LEFT) ctr = 0; 39 | else if (kDown & KEY_RIGHT) ctr = entries_amount-1; 40 | else if (kDown & KEY_DOWN) ctr++; 41 | else if (kDown & KEY_UP) ctr--; 42 | 43 | if (ctr >= entries_amount) ctr = 0; 44 | else if (ctr < 0) ctr = entries_amount-1; 45 | 46 | gspWaitForVBlank(); 47 | } 48 | 49 | return -1; 50 | } 51 | 52 | void progressBar(u32 max, u32 current) 53 | { 54 | printf("\x1b[s"); 55 | 56 | int i; 57 | char * blockStr = NULL; 58 | asprintf(&blockStr, "Block %lu of %lu", current, max); 59 | //erase the previous block count, to avoid having to clear the console (would cause blinking of the screen) 60 | for (i = 0; i <= (int)strlen(blockStr); i++) printf(" "); 61 | 62 | //start printing the actual info 63 | printf("\x1b[u"); 64 | puts(blockStr); 65 | 66 | printf("["); 67 | for (i = 0; i < (int)(current*100/max); i += PERCENTAGE_PER_CHAR) printf(PERCENT_CHAR); 68 | for (; i < 100; i += PERCENTAGE_PER_CHAR) printf(" "); 69 | printf("]"); 70 | 71 | printf("\x1b[u"); 72 | } 73 | -------------------------------------------------------------------------------- /source/ui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | int display_menu(const char *menu_entries[], const int entries_amount, const char *headerstr); 6 | void progressBar(u32 max, u32 current); 7 | 8 | inline void instructions(void) 9 | { 10 | puts("Press START to quit.\nPress Y to send a file.\nPress X to receive a file.\n"); 11 | } 12 | --------------------------------------------------------------------------------