diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0922335 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +3DS/build/ +3DS/*.elf +3DS/*stripped.elf +PC/build/ +*.pnproj +*.pnps +*.ppg diff --git a/3DS/3DSController.3ds b/3DS/3DSController.3ds new file mode 100644 index 0000000..7590001 Binary files /dev/null and b/3DS/3DSController.3ds differ diff --git a/3DS/3DSController.3dsx b/3DS/3DSController.3dsx new file mode 100644 index 0000000..0b5f3c6 Binary files /dev/null and b/3DS/3DSController.3dsx differ diff --git a/3DS/3DSController.cia b/3DS/3DSController.cia new file mode 100644 index 0000000..1fd4c6e Binary files /dev/null and b/3DS/3DSController.cia differ diff --git a/3DS/3DSController.ini b/3DS/3DSController.ini new file mode 100644 index 0000000..8d43317 --- /dev/null +++ b/3DS/3DSController.ini @@ -0,0 +1,4 @@ +Change the IP to be your PC's local IP, (run 3DSController.exe to find it), + +IP: 192.168.0.4 +Port: 8889 \ No newline at end of file diff --git a/3DS/3DSController.smdh b/3DS/3DSController.smdh new file mode 100644 index 0000000..9c71067 Binary files /dev/null and b/3DS/3DSController.smdh differ diff --git a/3DS/Makefile b/3DS/Makefile new file mode 100644 index 0000000..072f53e --- /dev/null +++ b/3DS/Makefile @@ -0,0 +1,188 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# +# NO_SMDH: if set to anything, no SMDH file is generated. +# APP_TITLE is the name of the app stored in the SMDH file (Optional) +# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) +# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) +# ICON is the filename of the icon (.png), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .png +# - icon.png +# - /default_icon.png +#--------------------------------------------------------------------------------- +TARGET := 3DSController +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include + +APP_TITLE := 3DS Controller +APP_DESCRIPTION := +APP_AUTHOR := CTurt +ICON := cxi/icon48x48.png + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard + +CFLAGS := -g -Wall -O2 -mword-relocations \ + -fomit-frame-pointer -ffast-math \ + -fms-extensions \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.png) + ifneq (,$(findstring $(TARGET).png,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).png + else + ifneq (,$(findstring icon.png,$(icons))) + export APP_ICON := $(TOPDIR)/icon.png + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).3ds $(OUTPUT).cia $(OUTPUT).smdh $(TARGET).elf $(OUTPUT)stripped.elf + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +ifeq ($(strip $(NO_SMDH)),) +.PHONY: all +all : $(OUTPUT).3dsx $(OUTPUT).3ds $(OUTPUT).cia $(OUTPUT).smdh +endif + +$(OUTPUT).3dsx : $(OUTPUT).elf + +$(OUTPUT).3ds : $(OUTPUT)stripped.elf + makerom -f cci -o $(OUTPUT).3ds -rsf "$(TOPDIR)/cxi/build_cia.rsf" -target t -exefslogo -elf $(OUTPUT)stripped.elf -icon "$(TOPDIR)/cxi/icon.icn" -banner "$(TOPDIR)/cxi/banner.bnr" + +$(OUTPUT).cia : $(OUTPUT)stripped.elf + makerom -f cia -o $(OUTPUT).cia -elf $(OUTPUT)stripped.elf -rsf "$(TOPDIR)/cxi/build_cia.rsf" -icon "$(TOPDIR)/cxi/icon.icn" -banner "$(TOPDIR)/cxi/banner.bnr" -exefslogo -target t + +$(OUTPUT).elf : $(OFILES) + +$(OUTPUT)stripped.elf : $(OUTPUT).elf + cp -f $(OUTPUT).elf $(OUTPUT)stripped.elf + @arm-none-eabi-strip $(OUTPUT)stripped.elf + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +# WARNING: This is not the right way to do this! TODO: Do it right! +#--------------------------------------------------------------------------------- +%.vsh.o : %.vsh +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @python $(AEMSTRO)/aemstro_as.py $< ../$(notdir $<).shbin + @bin2s ../$(notdir $<).shbin | $(PREFIX)as -o $@ + @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h + @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h + @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h + @rm ../$(notdir $<).shbin + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/3DS/cxi/banner.bnr b/3DS/cxi/banner.bnr new file mode 100644 index 0000000..c37656a Binary files /dev/null and b/3DS/cxi/banner.bnr differ diff --git a/3DS/cxi/banner.png b/3DS/cxi/banner.png new file mode 100644 index 0000000..b2c9380 Binary files /dev/null and b/3DS/cxi/banner.png differ diff --git a/3DS/cxi/build_cia.rsf b/3DS/cxi/build_cia.rsf new file mode 100644 index 0000000..8cbde36 --- /dev/null +++ b/3DS/cxi/build_cia.rsf @@ -0,0 +1,219 @@ +BasicInfo: + Title : "3DS Controller" + ProductCode : "CTR-N-3DSC" + Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem + +RomFs: + # Specifies the root path of the read only file system to include in the ROM. + RootPath : "$(ROMFS_ROOT)" + +TitleInfo: + Category : Application + UniqueId : 0xf0fc2 + +Option: + UseOnSD : true # true if App is to be installed to SD + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses where applicable (currently only exefs:/.code) + +AccessControlInfo: + CoreVersion : 2 + + # Exheader Format Version + DescVersion : 2 + + # Minimum Required Kernel Version (below is for 4.5.0) + ReleaseKernelMajor : "02" + ReleaseKernelMinor : "33" + + # ExtData + UseExtSaveData : false # enables ExtData + #ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId + + # FS:USER Archive Access Permissions + # Uncomment as required + FileSystemAccess: + #- CategorySystemApplication + #- CategoryHardwareCheck + - CategoryFileSystemTool + #- Debug + #- TwlCardBackup + #- TwlNandData + #- Boss + - DirectSdmc + #- Core + #- CtrNandRo + #- CtrNandRw + #- CtrNandRoWrite + #- CategorySystemSettings + #- CardBoard + #- ExportImportIvs + #- DirectSdmcWrite + #- SwitchCleanup + #- SaveDataMove + #- Shop + #- Shell + #- CategoryHomeMenu + + # Process Settings + MemoryType : Application # Application/System/Base + SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB + IdealProcessor : 0 + AffinityMask : 1 + Priority : 16 + MaxCpu : 0x9E # Default + HandleTableSize : 0x200 + DisableDebug : true + EnableForceDebug : false + CanWriteSharedPage : true + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : true + PermitMainFunctionArgument : true + CanShareDeviceMemory : true + RunnableOnSleep : false + SpecialMemoryArrange : true + + # New3DS Exclusive Process Settings + SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode + CpuSpeed : 804MHz # 256MHz(Default)/804MHz + EnableL2Cache : true # false(default)/true + CanAccessCore2 : true + + # Virtual Address Mappings + IORegisterMapping: + - 1ff00000-1ff7ffff # DSP memory + MemoryMapping: + - 1f000000-1f5fffff:r # VRAM + + # Accessible SVCs, : + SystemCallAccess: + ArbitrateAddress: 34 + Backdoor: 123 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + ControlProcessMemory: 112 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreateSemaphore: 21 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdOfThread: 54 + GetProcessIdealProcessor: 6 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + + # Service List + # Maximum 34 services (32 if firmware is prior to 9.6.0) + ServiceAccessControl: + - APT:U + - ac:u + - am:net + - boss:U + - cam:u + - cecd:u + - cfg:nor + - cfg:u + - csnd:SND + - dsp::DSP + - frd:u + - fs:USER + - gsp::Gpu + - hid:USER + - http:C + - ir:rst + - ir:u + - ir:USER + - mic:u + - ndm:u + - news:u + - nwm::UDS + - ptm:u + - pxi:dev + - soc:U + - ssl:C + - y2r:u + + +SystemControlInfo: + SaveDataSize: 0KB # Change if the app uses savedata + RemasterVersion: 2 + StackSize: 0x40000 + + # Modules that run services listed above should be included below + # Maximum 48 dependencies + # : + Dependency: + ac: 0x0004013000002402 + act: 0x0004013000003802 + am: 0x0004013000001502 + boss: 0x0004013000003402 + camera: 0x0004013000001602 + cecd: 0x0004013000002602 + cfg: 0x0004013000001702 + codec: 0x0004013000001802 + csnd: 0x0004013000002702 + dlp: 0x0004013000002802 + dsp: 0x0004013000001a02 + friends: 0x0004013000003202 + gpio: 0x0004013000001b02 + gsp: 0x0004013000001c02 + hid: 0x0004013000001d02 + http: 0x0004013000002902 + i2c: 0x0004013000001e02 + ir: 0x0004013000003302 + mcu: 0x0004013000001f02 + mic: 0x0004013000002002 + ndm: 0x0004013000002b02 + news: 0x0004013000003502 + nfc: 0x0004013000004002 + nim: 0x0004013000002c02 + nwm: 0x0004013000002d02 + pdn: 0x0004013000002102 + ps: 0x0004013000003102 + ptm: 0x0004013000002202 + qtm: 0x0004013020004202 + ro: 0x0004013000003702 + socket: 0x0004013000002e02 + spi: 0x0004013000002302 + ssl: 0x0004013000002f02 diff --git a/3DS/cxi/icon.icn b/3DS/cxi/icon.icn new file mode 100644 index 0000000..0dbbd45 Binary files /dev/null and b/3DS/cxi/icon.icn differ diff --git a/3DS/cxi/icon24x24.png b/3DS/cxi/icon24x24.png new file mode 100644 index 0000000..3086415 Binary files /dev/null and b/3DS/cxi/icon24x24.png differ diff --git a/3DS/cxi/icon48x48.png b/3DS/cxi/icon48x48.png new file mode 100644 index 0000000..e158664 Binary files /dev/null and b/3DS/cxi/icon48x48.png differ diff --git a/3DS/include/drawing.h b/3DS/include/drawing.h new file mode 100644 index 0000000..78f94bb --- /dev/null +++ b/3DS/include/drawing.h @@ -0,0 +1,23 @@ +#pragma once + +#ifndef REG_LCDBACKLIGHTMAIN +#define REG_LCDBACKLIGHTMAIN (u32)(0x1ED02240 - 0x1EB00000) +#endif + +#ifndef REG_LCDBACKLIGHTSUB +#define REG_LCDBACKLIGHTSUB (u32)(0x1ED02A40 - 0x1EB00000) +#endif + +inline void clearScreen(void); + +#define drawPixelRGB(x, y, r, g, b) drawPixelRGBFramebuffer(0, x, y, r, g, b) +void drawPixelRGBFramebuffer(u8 *fb, int x, int y, u8 r, u8 g, u8 b); + +#define drawBox(x, y, width, height, r, g, b) drawBoxFramebuffer(0, x, y, width, height, r, g, b) +inline void drawBoxFramebuffer(u8 *fb, int x, int y, int width, int height, u8 r, u8 g, u8 b); + +#define drawString(sx, sy, text, ...) drawStringFramebuffer(0, sx, sy, text, ##__VA_ARGS__) +void drawStringFramebuffer(u8 *fb, int sx, int sy, char *text, ...); + +void disableBacklight(); +void enableBacklight(); diff --git a/3DS/include/inet_pton.h b/3DS/include/inet_pton.h new file mode 100644 index 0000000..c214c2e --- /dev/null +++ b/3DS/include/inet_pton.h @@ -0,0 +1,5 @@ +#pragma once + +#define INADDRSZ 4 + +int inet_pton4(const char *src, unsigned char *dst); diff --git a/3DS/include/input.h b/3DS/include/input.h new file mode 100644 index 0000000..90ba655 --- /dev/null +++ b/3DS/include/input.h @@ -0,0 +1,3 @@ +#pragma once + +int inputIP(void); diff --git a/3DS/include/keyboard.h b/3DS/include/keyboard.h new file mode 100644 index 0000000..ad0271d --- /dev/null +++ b/3DS/include/keyboard.h @@ -0,0 +1,11 @@ +#pragma once + +extern const char keyboardChars[60]; + +extern unsigned char keyboardActive; +extern unsigned char keyboardToggle; + +extern unsigned char keyboardGfx[320 * 240 * 3]; + +void preRenderKeyboard(void); +inline void drawKeyboard(void); diff --git a/3DS/include/settings.h b/3DS/include/settings.h new file mode 100644 index 0000000..f5d87df --- /dev/null +++ b/3DS/include/settings.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct settings { + char IPString[16]; + int port; +}; + +extern struct settings settings; +extern struct settings defaultSettings; + +extern Handle fileHandle; + +bool readSettings(void); diff --git a/3DS/include/wireless.h b/3DS/include/wireless.h new file mode 100644 index 0000000..6ad6983 --- /dev/null +++ b/3DS/include/wireless.h @@ -0,0 +1,89 @@ +#pragma once + +#include + +#include <3ds.h> + +#include +#include +#include +#include +#include + +#include "inet_pton.h" + +#define SCREENSHOT_CHUNK 4000 + +#define DEFAULT_PORT 8889 + +enum NET_COMMANDS { + CONNECT, + KEYS, + SCREENSHOT, +}; + +// It is deliberately set up to have an anonymous struct as well as a named struct for convenience, not a mistake! +struct packet { + union { + struct packetHeader { + unsigned char command; + unsigned char keyboardActive; + }; + struct packetHeader packetHeader; + }; + + union { + // CONNECT + union { + struct connectPacket { + }; + struct connectPacket connectPacket; + }; + + // KEYS + union { + struct keysPacket { + unsigned int keys; + + struct { + short x; + short y; + } circlePad; + + struct { + unsigned short x; + unsigned short y; + } touch; + + struct { + short x; + short y; + } cStick; + + unsigned int volume; //way longer than needed, but it works. + }; + struct keysPacket keysPacket; + }; + + // SCREENSHOT + union { + struct screenshotPacket { + unsigned short offset; + unsigned char data[SCREENSHOT_CHUNK]; + }; + struct screenshotPacket screenshotPacket; + }; + }; +}; + +extern int sock; +extern struct sockaddr_in sain, saout; +extern struct packet outBuf, rcvBuf; + +extern socklen_t sockaddr_in_sizePtr; + +bool openSocket(int port); +void sendBuf(int length); +int receiveBuffer(int length); +void sendConnectionRequest(void); +void sendKeys(unsigned int keys, circlePosition circlePad, touchPosition touch, circlePosition cStick, unsigned int volume); diff --git a/3DS/source/drawing.c b/3DS/source/drawing.c new file mode 100644 index 0000000..653022d --- /dev/null +++ b/3DS/source/drawing.c @@ -0,0 +1,175 @@ +#include +#include +#include + +#include <3ds.h> + +#include "drawing.h" + +static const char fonts[] = { //Fonte 8x8 1BPP + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 //tile:0 + , 0x18, 0x18, 0x18, 0x18, 0x18, 0x0, 0x18, 0x0 //tile:1 + , 0x6c, 0x6c, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x0 //tile:2 + , 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x0 //tile:3 + , 0x18, 0x7e, 0xc0, 0x7c, 0x6, 0xfc, 0x18, 0x0 //tile:4 + , 0x0, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x0 //tile:5 + , 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x0 //tile:6 + , 0x30, 0x30, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0 //tile:7 + , 0xc, 0x18, 0x30, 0x30, 0x30, 0x18, 0xc, 0x0 //tile:8 + , 0x30, 0x18, 0xc, 0xc, 0xc, 0x18, 0x30, 0x0 //tile:9 + , 0x0, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x0, 0x0 //tile:10 + , 0x0, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x0, 0x0 //tile:11 + , 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x18, 0x30 //tile:12 + , 0x0, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0x0 //tile:13 + , 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x18, 0x0 //tile:14 + , 0x6, 0xc, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x0 //tile:15 + , 0x7c, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0x7c, 0x0 //tile:16 + , 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x0 //tile:17 + , 0x7c, 0xc6, 0x6, 0x7c, 0xc0, 0xc0, 0xfe, 0x0 //tile:18 + , 0xfc, 0x6, 0x6, 0x3c, 0x6, 0x6, 0xfc, 0x0 //tile:19 + , 0xc, 0xcc, 0xcc, 0xcc, 0xfe, 0xc, 0xc, 0x0 //tile:20 + , 0xfe, 0xc0, 0xfc, 0x6, 0x6, 0xc6, 0x7c, 0x0 //tile:21 + , 0x7c, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x0 //tile:22 + , 0xfe, 0x6, 0x6, 0xc, 0x18, 0x30, 0x30, 0x0 //tile:23 + , 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x0 //tile:24 + , 0x7c, 0xc6, 0xc6, 0x7e, 0x6, 0x6, 0x7c, 0x0 //tile:25 + , 0x0, 0x18, 0x18, 0x0, 0x0, 0x18, 0x18, 0x0 //tile:26 + , 0x0, 0x18, 0x18, 0x0, 0x0, 0x18, 0x18, 0x30 //tile:27 + , 0xc, 0x18, 0x30, 0x60, 0x30, 0x18, 0xc, 0x0 //tile:28 + , 0x0, 0x0, 0x7e, 0x0, 0x7e, 0x0, 0x0, 0x0 //tile:29 + , 0x30, 0x18, 0xc, 0x6, 0xc, 0x18, 0x30, 0x0 //tile:30 + , 0x3c, 0x66, 0xc, 0x18, 0x18, 0x0, 0x18, 0x0 //tile:31 + , 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x7e, 0x0 //tile:32 + , 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x0 //tile:33 + , 0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x0 //tile:34 + , 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x0 //tile:35 + , 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x0 //tile:36 + , 0xfe, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xfe, 0x0 //tile:37 + , 0xfe, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xc0, 0x0 //tile:38 + , 0x7c, 0xc6, 0xc0, 0xc0, 0xce, 0xc6, 0x7c, 0x0 //tile:39 + , 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x0 //tile:40 + , 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x0 //tile:41 + , 0x6, 0x6, 0x6, 0x6, 0x6, 0xc6, 0x7c, 0x0 //tile:42 + , 0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x0 //tile:43 + , 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x0 //tile:44 + , 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x0 //tile:45 + , 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x0 //tile:46 + , 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x0 //tile:47 + , 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x0 //tile:48 + , 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x6 //tile:49 + , 0xfc, 0xc6, 0xc6, 0xfc, 0xd8, 0xcc, 0xc6, 0x0 //tile:50 + , 0x7c, 0xc6, 0xc0, 0x7c, 0x6, 0xc6, 0x7c, 0x0 //tile:51 + , 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0 //tile:52 + , 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0x0 //tile:53 + , 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x38, 0x0 //tile:54 + , 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x0 //tile:55 + , 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x0 //tile:56 + , 0xc6, 0xc6, 0xc6, 0x7c, 0x18, 0x30, 0xe0, 0x0 //tile:57 + , 0xfe, 0x6, 0xc, 0x18, 0x30, 0x60, 0xfe, 0x0 //tile:58 + , 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x0 //tile:59 + , 0xc0, 0x60, 0x30, 0x18, 0xc, 0x6, 0x2, 0x0 //tile:60 + , 0x3c, 0xc, 0xc, 0xc, 0xc, 0xc, 0x3c, 0x0 //tile:61 + , 0x10, 0x38, 0x6c, 0xc6, 0x0, 0x0, 0x0, 0x0 //tile:62 + , 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff //tile:63 + , 0x18, 0x18, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0 //tile:64 + , 0x0, 0x0, 0x7c, 0x6, 0x7e, 0xc6, 0x7e, 0x0 //tile:65 + , 0xc0, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xfc, 0x0 //tile:66 + , 0x0, 0x0, 0x7c, 0xc6, 0xc0, 0xc6, 0x7c, 0x0 //tile:67 + , 0x6, 0x6, 0x6, 0x7e, 0xc6, 0xc6, 0x7e, 0x0 //tile:68 + , 0x0, 0x0, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x0 //tile:69 + , 0x1c, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x0 //tile:70 + , 0x0, 0x0, 0x7e, 0xc6, 0xc6, 0x7e, 0x6, 0xfc //tile:71 + , 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x0 //tile:72 + , 0x18, 0x0, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x0 //tile:73 + , 0x6, 0x0, 0x6, 0x6, 0x6, 0x6, 0xc6, 0x7c //tile:74 + , 0xc0, 0xc0, 0xcc, 0xd8, 0xf8, 0xcc, 0xc6, 0x0 //tile:75 + , 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x0 //tile:76 + , 0x0, 0x0, 0xcc, 0xfe, 0xfe, 0xd6, 0xd6, 0x0 //tile:77 + , 0x0, 0x0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x0 //tile:78 + , 0x0, 0x0, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x0 //tile:79 + , 0x0, 0x0, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0 //tile:80 + , 0x0, 0x0, 0x7e, 0xc6, 0xc6, 0x7e, 0x6, 0x6 //tile:81 + , 0x0, 0x0, 0xfc, 0xc6, 0xc0, 0xc0, 0xc0, 0x0 //tile:82 + , 0x0, 0x0, 0x7e, 0xc0, 0x7c, 0x6, 0xfc, 0x0 //tile:83 + , 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0xe, 0x0 //tile:84 + , 0x0, 0x0, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x0 //tile:85 + , 0x0, 0x0, 0xc6, 0xc6, 0xc6, 0x7c, 0x38, 0x0 //tile:86 + , 0x0, 0x0, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x0 //tile:87 + , 0x0, 0x0, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x0 //tile:88 + , 0x0, 0x0, 0xc6, 0xc6, 0xc6, 0x7e, 0x6, 0xfc //tile:89 + , 0x0, 0x0, 0xfe, 0xc, 0x38, 0x60, 0xfe, 0x0 //tile:90 + , 0xe, 0x18, 0x18, 0x70, 0x18, 0x18, 0xe, 0x0 //tile:91 + , 0x18, 0x18, 0x18, 0x0, 0x18, 0x18, 0x18, 0x0 //tile:92 + , 0x70, 0x18, 0x18, 0xe, 0x18, 0x18, 0x70, 0x0 //tile:93 + , 0x76, 0xdc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 //tile:94 + , 0x0, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x0 //tile:95 +}; + +inline void clearScreen(void) { + u8 *frame = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); + memset(frame, 0, 320 * 240 * 3); +} + +void drawPixelRGBFramebuffer(u8 *fb, int x, int y, u8 r, u8 g, u8 b) { + y = 240 - y; + x = x; + u32 v = (y + x * 240) * 3; + u8 *topLeftFB = fb ? fb : gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); + topLeftFB[v] = (b >> 3) + ((g & 0x1C) << 3); + topLeftFB[v+1] = ((g & 0xE0) >> 5) + (r & 0xF8); +} + +inline void drawBoxFramebuffer(u8 *fb, int x, int y, int width, int height, u8 r, u8 g, u8 b) { + int lx, ly; + for(lx = x; lx < x + width; lx++) { + for(ly = y; ly < y + height; ly++) { + drawPixelRGBFramebuffer(fb, lx, ly, r, g, b); + } + } +} + +void drawStringFramebuffer(u8 *fb, int sx, int sy, char *text, ...) { + char str[1024]; + va_list args; + va_start(args, text); + vsnprintf(str, 1023, text, args); + va_end(args); + + int i; + for(i = 0; i < strlen(str); i++) { + int fntnum = (str[i] - 32) & 0xFF; + int y; + for(y = 0; y < 8; y++) { + int currbyte = fonts[(fntnum * 8) + y]; + //Desenha sprite de 1BPP + int x; + int mult = 0x80; + for(x = 0; x < 8; x++) { + if((currbyte & mult) == mult) { + drawPixelRGBFramebuffer(fb, sx + x, sy + y, 0xFF, 0xFF, 0xFF); + drawPixelRGBFramebuffer(fb, sx + x, sy + y + 1, 0, 0, 0); //Sombra + } + mult /= 2; + } + } + sx += 8; + } +} + +static u32 brightnessMain; +static u32 brightnessSub; + +void disableBacklight() { + u32 off = 0; + + GSPGPU_ReadHWRegs(NULL, REG_LCDBACKLIGHTMAIN, &brightnessMain, 4); + GSPGPU_ReadHWRegs(NULL, REG_LCDBACKLIGHTSUB, &brightnessSub, 4); + + GSPGPU_WriteHWRegs(NULL, REG_LCDBACKLIGHTMAIN, &off, 4); + GSPGPU_WriteHWRegs(NULL, REG_LCDBACKLIGHTSUB, &off, 4); +} + +void enableBacklight() { + GSPGPU_WriteHWRegs(NULL, REG_LCDBACKLIGHTMAIN, &brightnessMain, 4); + GSPGPU_WriteHWRegs(NULL, REG_LCDBACKLIGHTSUB, &brightnessSub, 4); +} diff --git a/3DS/source/inet_pton.c b/3DS/source/inet_pton.c new file mode 100644 index 0000000..b402d26 --- /dev/null +++ b/3DS/source/inet_pton.c @@ -0,0 +1,44 @@ +#include "wireless.h" + +#include "inet_pton.h" + +int inet_pton4(const char *src, unsigned char *dst) { + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + unsigned char tmp[INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + tp = tmp; + *tp = 0; + while((ch = *src++) != '\0') { + const char *pch; + + if((pch = strchr(digits, ch)) != NULL) { + unsigned int val = *tp * 10 + (unsigned int)(pch - digits); + + if(saw_digit && *tp == 0) + return (0); + if(val > 255) + return (0); + *tp = (unsigned char)val; + if(! saw_digit) { + if(++octets > 4) + return (0); + saw_digit = 1; + } + } + else if(ch == '.' && saw_digit) { + if(octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } + else + return (0); + } + if(octets < 4) + return (0); + memcpy(dst, tmp, INADDRSZ); + return (1); +} diff --git a/3DS/source/input.c b/3DS/source/input.c new file mode 100644 index 0000000..e79248e --- /dev/null +++ b/3DS/source/input.c @@ -0,0 +1,59 @@ +#include +#include +#include + +#include <3ds.h> + +#include "drawing.h" + +#include "input.h" + +int inputIP(void) { + touchPosition touch; + hidTouchRead(&touch); + + //Draw Keypad + drawString(160-20,50, "1"); + drawString(160, 50, "2"); + drawString(160+20, 50, "3"); + + drawString(160-20, 50+20, "4"); + drawString(160, 50+20, "5"); + drawString(160+20, 50+20, "6"); + + drawString(160-20, 50+40, "7"); + drawString(160, 50+40, "8"); + drawString(160+20, 50+40, "9"); + + drawString(160-10, 50+60, "."); + drawString(160+10, 50+60, "0"); + + //Bottom Strip + if(touch.py > 50+50 && touch.py < 50+70) { + if(touch.px < 160 && touch.px > 160-20) return 11; // Dot + else if(touch.px < 160+20 && touch.px > 160) return 0; // Zero + } + + //First Column + else if(touch.px < 160-10 && touch.px > 160-30) { + if(touch.py < 50+10 && touch.py > 50-10) return 1; // One + else if(touch.py < 50+30 && touch.py > 50+10) return 4; // Four + else if(touch.py < 50+50 && touch.py > 50+30) return 7; // Seven + } + + // Second Column + else if(touch.px < 160+10 && touch.px > 160-10) { + if(touch.py < 50+10 && touch.py > 50-10) return 2; // Two + else if(touch.py < 50+30 && touch.py > 50+10) return 5; // Five + else if(touch.py < 50+50 && touch.py > 50+30) return 8; // Eight + } + + // Third Column + else if(touch.px < 160+30 && touch.px > 160+10) { + if(touch.py < 50+10 && touch.py > 50-10) return 3; // Three + else if(touch.py < 50+30 && touch.py > 50+10) return 6; // Six + else if(touch.py < 50+50 && touch.py > 50+30) return 9; // Nine + } + + return 10; +} \ No newline at end of file diff --git a/3DS/source/keyboard.c b/3DS/source/keyboard.c new file mode 100644 index 0000000..f23bbf5 --- /dev/null +++ b/3DS/source/keyboard.c @@ -0,0 +1,78 @@ +#include <3ds.h> +#include +#include + +#include "drawing.h" + +#include "keyboard.h" + +const char keyboardChars[60] = "!1234567890\x08QWERTYUIOP\13\13ASDFGHJKL-\13\13ZXCVBNM,.?\13\13\0\0\0 \0\0\0\0"; + +unsigned char keyboardActive = false; +unsigned char keyboardToggle = true; + +unsigned char keyboardGfx[320 * 240 * 3]; + +void preRenderKeyboard(void) { + const char chars[60] = "!1234567890\x08QWERTYUIOP\13\13ASDFGHJKL-\13\13ZXCVBNM,.?\13\13\0\0\0 \0\0\0\0"; + + int x, y; + for(x = 0; x < 12; x++) { + for(y = 0; y < 4; y++) { + // Not enter + if(chars[x + y * 12] != '\13') { + drawBoxFramebuffer(keyboardGfx, (int)((float)x * 320.0f / 12.0f), (int)(78.0f + (float)y * 320.0f / 12.0f), 25, 25, 31, 31, 31); + drawBoxFramebuffer(keyboardGfx, (int)((float)x * 320.0f / 12.0f) + 1, (int)(78.0f + (float)y * 320.0f / 12.0f) + 1, 24, 24, 31, 0, 0); + + // Backspace + if(chars[x + y * 12] == '\x08') { + drawStringFramebuffer(keyboardGfx, (int)((float)x * 320.0f / 12.0f) + 10, (int)(78.0f + (float)y * 320.0f / 12.0f) + 9, "-"); + drawStringFramebuffer(keyboardGfx, (int)((float)x * 320.0f / 12.0f) + 8, (int)(78.0f + (float)y * 320.0f / 12.0f) + 9, "<"); + } + else drawStringFramebuffer(keyboardGfx, (int)((float)x * 320.0f / 12.0f) + 9, (int)(78.0f + (float)y * 320.0f / 12.0f) + 9, "%c", chars[x + y * 12]); + } + } + } + + // Space + drawBoxFramebuffer(keyboardGfx, (int)((float)3 * 320.0f / 12.0f), (int)(78.0f + (float)4 * 320.0f / 12.0f), (int)(5.0f * 320.0f / 12.0f), 25, 31, 31, 31); + drawBoxFramebuffer(keyboardGfx, (int)((float)3 * 320.0f / 12.0f) + 1, (int)(78.0f + (float)4 * 320.0f / 12.0f) + 1, (int)(5.0f * 320.0f / 12.0f) - 1, 24, 31, 0, 0); + + // Enter + drawBoxFramebuffer(keyboardGfx, (int)((float)10 * 320.0f / 12.0f), (int)(78.0f + (float)1 * 320.0f / 12.0f), (int)(2.0f * 320.0f / 12.0f), (int)(3.0f * 320.0f / 12.0f), 31, 31, 31); + drawBoxFramebuffer(keyboardGfx, (int)((float)10 * 320.0f / 12.0f) + 1, (int)(78.0f + (float)1 * 320.0f / 12.0f) + 1, (int)(2.0f * 320.0f / 12.0f) - 1, (int)(3.0f * 320.0f / 12.0f) - 1, 31, 0, 0); +} + +inline void drawKeyboard(void) { + u8 *topLeftFB = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); + memcpy(topLeftFB, keyboardGfx, sizeof(keyboardGfx)); + + /*const char chars[60] = "!1234567890\x08QWERTYUIOP\13\13ASDFGHJKL-\13\13ZXCVBNM,.?\13\13\0\0\0 \0\0\0\0"; + + int x, y; + for(x = 0; x < 12; x++) { + for(y = 0; y < 4; y++) { + // Not enter + if(chars[x + y * 12] != '\13') { + drawBox((int)((float)x * 320.0f / 12.0f), (int)(78.0f + (float)y * 320.0f / 12.0f), 25, 25, 31, 31, 31); + drawBox((int)((float)x * 320.0f / 12.0f) + 1, (int)(78.0f + (float)y * 320.0f / 12.0f) + 1, 24, 24, 31, 0, 0); + + // Backspace + if(chars[x + y * 12] == '\x08') { + drawString((int)((float)x * 320.0f / 12.0f) + 10, (int)(78.0f + (float)y * 320.0f / 12.0f) + 9, "-"); + drawString((int)((float)x * 320.0f / 12.0f) + 8, (int)(78.0f + (float)y * 320.0f / 12.0f) + 9, "<"); + } + else drawString((int)((float)x * 320.0f / 12.0f) + 9, (int)(78.0f + (float)y * 320.0f / 12.0f) + 9, "%c", chars[x + y * 12]); + } + } + } + + // Space + drawBox((int)((float)3 * 320.0f / 12.0f), (int)(78.0f + (float)4 * 320.0f / 12.0f), (int)(5.0f * 320.0f / 12.0f), 25, 31, 31, 31); + drawBox((int)((float)3 * 320.0f / 12.0f) + 1, (int)(78.0f + (float)4 * 320.0f / 12.0f) + 1, (int)(5.0f * 320.0f / 12.0f) - 1, 24, 31, 0, 0); + + // Enter + drawBox((int)((float)10 * 320.0f / 12.0f), (int)(78.0f + (float)1 * 320.0f / 12.0f), (int)(2.0f * 320.0f / 12.0f), (int)(3.0f * 320.0f / 12.0f), 31, 31, 31); + drawBox((int)((float)10 * 320.0f / 12.0f) + 1, (int)(78.0f + (float)1 * 320.0f / 12.0f) + 1, (int)(2.0f * 320.0f / 12.0f) - 1, (int)(3.0f * 320.0f / 12.0f) - 1, 31, 0, 0); + */ +} diff --git a/3DS/source/main.c b/3DS/source/main.c new file mode 100644 index 0000000..3f774cf --- /dev/null +++ b/3DS/source/main.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include + +#include <3ds.h> + +#include "wireless.h" +#include "settings.h" +#include "drawing.h" +#include "input.h" +#include "keyboard.h" + +static jmp_buf exitJmp; + +void hang(char *message) { + while(aptMainLoop()) { + hidScanInput(); + + clearScreen(); + drawString(10, 10, "%s", message); + drawString(10, 20, "Start and Select to exit"); + + u32 kHeld = hidKeysHeld(); + if((kHeld & KEY_START) && (kHeld & KEY_SELECT)) longjmp(exitJmp, 1); + + gfxFlushBuffers(); + gspWaitForVBlank(); + gfxSwapBuffers(); + } +} + +int main(void) { + acInit(); + gfxInitDefault(); + + gfxSetDoubleBuffering(GFX_TOP, true); + gfxSetDoubleBuffering(GFX_BOTTOM, true); + + if(setjmp(exitJmp)) goto exit; + + preRenderKeyboard(); + + clearScreen(); + drawString(10, 10, "Initing FS..."); + gfxFlushBuffers(); + gfxSwapBuffers(); + + fsInit(); + + clearScreen(); + drawString(10, 10, "Initing SOC..."); + gfxFlushBuffers(); + gfxSwapBuffers(); + + SOC_Initialize((u32 *)memalign(0x1000, 0x100000), 0x100000); + + u32 wifiStatus = 0; + ACU_GetWifiStatus(NULL, &wifiStatus); + if(!wifiStatus) { + hang("No WiFi! Is your wireless slider on?"); + } + + clearScreen(); + drawString(10, 10, "Reading settings..."); + gfxFlushBuffers(); + gfxSwapBuffers(); + + if(!readSettings()) { + hang("Could not read 3DSController.ini!"); + } + + clearScreen(); + drawString(10, 10, "Connecting to %s on port %d...", settings.IPString, settings.port); + gfxFlushBuffers(); + gfxSwapBuffers(); + + openSocket(settings.port); + sendConnectionRequest(); + + clearScreen(); + gfxFlushBuffers(); + gfxSwapBuffers(); + + disableBacklight(); + + while(aptMainLoop()) { + hidScanInput(); + irrstScanInput(); + + u32 kHeld = hidKeysHeld(); + circlePosition circlePad; + circlePosition cStick; + u8 vol8; + u8* volp = &vol8; //As a test for pointing at things. + hidCstickRead(&cStick); + hidCircleRead(&circlePad); + HIDUSER_GetSoundVolume(volp); + u32 volume = (u32)vol8; //Upscale to 32 for transmission + touchPosition touch; + touchRead(&touch); + + clearScreen(); + + if((kHeld & KEY_L) && (kHeld & KEY_R) && (kHeld & KEY_X)) { + if(keyboardToggle) { + keyboardActive = !keyboardActive; + keyboardToggle = false; + + if(keyboardActive) { + enableBacklight(); + } else { + disableBacklight(); + } + } + } + else keyboardToggle = true; + + if(keyboardActive) { + drawKeyboard(); + + if(touch.px >= 1 && touch.px <= 312 && touch.py >= 78 && touch.py <= 208) { + int x = (int)((float)touch.px * 12.0f / 320.0f); + int y = (int)((float)(touch.py - 78) * 12.0f / 320.0f); + int width = 24; + int height = 24; + + if(keyboardChars[x + y * 12] == ' ') { + while(keyboardChars[(x - 1) + y * 12] == ' ') x--; + + width = (int)(5.0f * 320.0f / 12.0f) - 1; + } + + else if(keyboardChars[x + y * 12] == '\13') { + while(keyboardChars[(x - 1) + y * 12] == '\13') x--; + while(keyboardChars[x + (y - 1) * 12] == '\13') y--; + + width = (int)(2.0f * 320.0f / 12.0f) - 1; + height = (int)(3.0f * 320.0f / 12.0f) - 1; + } + + if(keyboardChars[x + y * 12]) drawBox((int)((float)x * 320.0f / 12.0f) + 1, (int)(78.0f + (float)y * 320.0f / 12.0f) + 1, width, height, 31, 31, 0); + } + } + + sendKeys(kHeld, circlePad, touch, cStick, volume); + //drawString(10, 10, "Volume: %x", volume); + //receiveBuffer(sizeof(struct packet)); + + if((kHeld & KEY_START) && (kHeld & KEY_SELECT)) { + sendKeys(0, circlePad, touch, cStick, volume); + longjmp(exitJmp, 1); + } + + gfxFlushBuffers(); + gspWaitForVBlank(); + gfxSwapBuffers(); + } + + exit: + + enableBacklight(); + + SOC_Shutdown(); + + svcCloseHandle(fileHandle); + fsExit(); + + gfxExit(); + acExit(); + + return 0; +} diff --git a/3DS/source/settings.c b/3DS/source/settings.c new file mode 100644 index 0000000..3559889 --- /dev/null +++ b/3DS/source/settings.c @@ -0,0 +1,81 @@ +#include +#include + +#include <3ds.h> + +#include "wireless.h" + +#include "settings.h" + +struct settings settings; + +struct settings defaultSettings = { + IPString: "", + port: DEFAULT_PORT, +}; + +Handle fileHandle; + +static bool getSetting(char *name, char *src, char *dest) { + char *start = strstr(src, name); + + if(start) { + start += strlen(name); + + char *end = start + strlen(start); + if(strstr(start, "\n") - 1 < end) end = strstr(start, "\n") - 1; + size_t size = (size_t)end - (size_t)start; + + strncpy(dest, start, size); + dest[size] = '\0'; + + return true; + } + + return false; +} + +bool readSettings(void) { + char *buffer = NULL; + u64 size; + u32 bytesRead; + + FS_archive sdmcArchive = (FS_archive){ARCH_SDMC, (FS_path){PATH_EMPTY, 1, (u8*)""}}; + FS_path filePath = FS_makePath(PATH_CHAR, "/3DSController.ini"); + + Result ret = FSUSER_OpenFileDirectly(NULL, &fileHandle, sdmcArchive, filePath, FS_OPEN_READ, FS_ATTRIBUTE_NONE); + if(ret) return false; + + ret = FSFILE_GetSize(fileHandle, &size); + if(ret) return false; + + buffer = malloc(size); + if(!buffer) return false; + + ret = FSFILE_Read(fileHandle, &bytesRead, 0x0, buffer, size); + if(ret || size != bytesRead) return false; + + ret = FSFILE_Close(fileHandle); + if(ret) return false; + + memcpy(&settings, &defaultSettings, sizeof(struct settings)); + + char setting[64] = { '\0' }; + + if(getSetting("IP: ", buffer, settings.IPString)) { + //inet_pton(AF_INET, settings.IPString, &(saout.sin_addr)); + inet_pton4(settings.IPString, (unsigned char *)&(saout.sin_addr)); + } + else { + free(buffer); + return false; + } + + if(getSetting("Port: ", buffer, setting)) { + sscanf(setting, "%d", &settings.port); + } + + free(buffer); + + return true; +} diff --git a/3DS/source/wireless.c b/3DS/source/wireless.c new file mode 100644 index 0000000..8abe2c8 --- /dev/null +++ b/3DS/source/wireless.c @@ -0,0 +1,50 @@ +#include + +#include "keyboard.h" + +#include "wireless.h" + +int sock; +struct sockaddr_in sain, saout; +struct packet outBuf, rcvBuf; + +socklen_t sockaddr_in_sizePtr = (int)sizeof(struct sockaddr_in); + +bool openSocket(int port) { + sock = socket(AF_INET, SOCK_DGRAM, 0); + + saout.sin_family = sain.sin_family = AF_INET; + saout.sin_port = sain.sin_port = htons(port); + sain.sin_addr.s_addr = INADDR_ANY; + + bind(sock, (struct sockaddr *)&sain, sizeof(sain)); + + fcntl(sock, F_SETFL, O_NONBLOCK); + + return true; +} + +void sendBuf(int length) { + sendto(sock, (char *)&outBuf, length, 0, (struct sockaddr *)&saout, sizeof(saout)); +} + +int receiveBuffer(int length) { + return recvfrom(sock, (char *)&rcvBuf, length, 0, (struct sockaddr *)&sain, &sockaddr_in_sizePtr); +} + +void sendConnectionRequest(void) { + outBuf.command = CONNECT; + outBuf.keyboardActive = keyboardActive; + sendBuf(offsetof(struct packet, connectPacket) + sizeof(struct connectPacket)); +} + +void sendKeys(unsigned int keys, circlePosition circlePad, touchPosition touch, circlePosition cStick, unsigned int volume) { + outBuf.command = KEYS; + outBuf.keyboardActive = keyboardActive; + memcpy(&outBuf.keys, &keys, 4); + memcpy(&outBuf.circlePad, &circlePad, 4); + memcpy(&outBuf.touch, &touch, 4); + memcpy(&outBuf.cStick, &cStick, 4); + memcpy(&outBuf.volume, &volume, 4); + sendBuf(offsetof(struct packet, keysPacket) + sizeof(struct keysPacket)); +} diff --git a/Linux/3DSController.py b/Linux/3DSController.py new file mode 100644 index 0000000..1615890 --- /dev/null +++ b/Linux/3DSController.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +# Compatible with both Python 2.7.6 and 3.4.3 + +from __future__ import print_function +import socket, struct, time +import Xlib, Xlib.display, Xlib.XK +LMouse = []; RMouse = [] +Button = []; MouseAbs = []; MouseRel = []; MouseAbsClick = []; MouseRelClick = [] + +########################################################## +# CONFIGURABLE REGION START - Don't touch anything above # +########################################################## +port = 8889 + +#This tells what the touch screen does if touched. +#Valid values: Button, MouseAbs, MouseRel, MouseRelClick, MouseRelClick +#Button sends the Tap button. +#MouseAbs moves your mouse to the same part of the screen as the touch screen was touched. +#MouseRel moves your mouse by the same distance as you drag across the touch screen. +#MouseAbsClick and MouseRelClick send the primary mouse button event if the screen is tapped, not held. +touch = MouseRelClick + +mouse_speed = 4 +# The number of pixels on each side of the 3DS screen which are ignored, since you can't reach the outermost corners. +abs_deadzone = 10 + +#Valid values can be found in any of these locations on your Linux system (some may not exist): +# /usr/include/X11/keysymdef.h +# /usr/lib/python3/dist-packages/Xlib/keysymdef/ +# /usr/lib/python2.7/dist-packages/Xlib/keysymdef/ +#Additionally, LMouse and RMouse can also be used on any button. +btn_map = { + "A": "A", + "B": "B", + "X": "X", + "Y": "Y", + "L": "L", + "R": "R", + "ZL": "Q", + "ZR": "W", + "Left": Xlib.XK.XK_Left, + "Right": Xlib.XK.XK_Right, + "Up": Xlib.XK.XK_Up, + "Down": Xlib.XK.XK_Down, + "Start": Xlib.XK.XK_Return, + "Select": Xlib.XK.XK_BackSpace, + "Tap": LMouse, +} +######################################################## +# CONFIGURABLE REGION END - Don't touch anything below # +######################################################## + +def pprint(obj): + import pprint + pprint.PrettyPrinter().pprint(obj) + +class x: pass + +command = x() +command.CONNECT = 0 +command.KEYS = 1 +command.SCREENSHOT = 2 + +keynames = [ + "A", "B", "Select", "Start", "Right", "Left", "Up", "Down", + "R", "L", "X", "Y", None, None, "ZL", "ZR", + None, None, None, None, "Tap", None, None, None, + "CSRight", "CSLeft", "CSUp", "CSDown", "CRight", "CLeft", "CUp", "CDown", +] + +keys = x() +keys.A = 1<<0 +keys.B = 1<<1 +keys.Select = 1<<2 +keys.Start = 1<<3 +keys.Right = 1<<4 +keys.Left = 1<<5 +keys.Up = 1<<6 +keys.Down = 1<<7 +keys.R = 1<<8 +keys.L = 1<<9 +keys.X = 1<<10 +keys.Y = 1<<11 +keys.ZL = 1<<14 # (new 3DS only) +keys.ZR = 1<<15 # (new 3DS only) +keys.Tap = 1<<20 # Not actually provided by HID +keys.CSRight = 1<<24 # c-stick (new 3DS only) +keys.CSLeft = 1<<25 # c-stick (new 3DS only) +keys.CSUp = 1<<26 # c-stick (new 3DS only) +keys.CSDown = 1<<27 # c-stick (new 3DS only) +keys.CRight = 1<<28 # circle pad +keys.CLeft = 1<<29 # circle pad +keys.CUp = 1<<30 # circle pad +keys.CDown = 1<<31 # circle pad + +def currentKeyboardKey(x, y): + chars = ("!1234567890\x08"+ + "QWERTYUIOP\13\13"+ + "ASDFGHJKL-\13\13"+ + "ZXCVBNM,.?\13\13"+ + "\0\0\0 \0\0\0\0") + + if x>=1 and x<=312 and y>=78 and y<=208: + xi = int(x*12/320) + yi = int((y-78)*12/320) + return chars[yi*12 + xi] + else: return None + +def key_to_keysym(key): + if not key: return 0 + + if isinstance(key,str): + if key=="\x08": return Xlib.XK.XK_BackSpace + if key=="\13": return Xlib.XK.XK_Return + if key==" ": return Xlib.XK.XK_space + return Xlib.XK.string_to_keysym(key) + + return key + +def action_key(key, action): + x_action = Xlib.X.ButtonRelease + x_action2 = Xlib.X.KeyRelease + if action: + x_action = Xlib.X.ButtonPress + x_action2 = Xlib.X.KeyPress + + if key is LMouse or key is RMouse: + if key is LMouse: button = 1 + if key is RMouse: button = 3 + button = disp.get_pointer_mapping()[button-1] # account for left-handed mice + disp.xtest_fake_input(x_action, button) + disp.sync() + return + + keysym = key_to_keysym(key) + if not keysym: return + + keycode = disp.keysym_to_keycode(keysym) + disp.xtest_fake_input(x_action2, keycode) + disp.sync() + +def press_key(key): + action_key(key,True) + +def release_key(key): + action_key(key,False) + +def move_mouse(x,y): + x=int(x) + y=int(y) + if not x and not y: return + + disp.warp_pointer(x,y) + disp.sync() + +def move_mouse_abs_frac(x,y): + root = disp.screen().root + geom = root.get_geometry() + + root.warp_pointer(int(x*geom.width), int(y*geom.height)) + disp.sync() + +disp = Xlib.display.Display() + +touch_click = (touch is MouseAbsClick or touch is MouseRelClick) +if touch is MouseAbsClick: touch = MouseAbs +if touch is MouseRelClick: touch = MouseRel + +if touch is MouseAbs and disp.screen_count()!=1: + print("Sorry, but MouseAbs only supports a single monitor. I'll use MouseRel instead.") + touch = MouseRel + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock.bind(("", port)) + +prevkeys = 0 + +touch_start = 0 +touch_last_x = 0 +touch_last_y = 0 +keyboard_prevkey = currentKeyboardKey(0, 0) + +while True: + rawdata, addr = sock.recvfrom(4096) + rawdata = bytearray(rawdata) + #print("received message", rawdata, "from", addr) + + if rawdata[0]==command.CONNECT: + pass # CONNECT packets are empty + + if rawdata[0]==command.KEYS: + fields = struct.unpack("=16 or abs(data["circleY"])>=16: + move_mouse(data["circleX"]*mouse_speed/32.0, -data["circleY"]*mouse_speed/32.0) + + if rawdata[0]==command.SCREENSHOT: + pass # unused by both 3DS and PC applications diff --git a/Linux/3DSController_gamepad.py b/Linux/3DSController_gamepad.py new file mode 100644 index 0000000..f57858e --- /dev/null +++ b/Linux/3DSController_gamepad.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# Compatible with both Python 2.7.6 and 3.4.3 + +from __future__ import print_function +import socket, struct, time +import uinput + +########################################################## +# CONFIGURABLE REGION START - Don't touch anything above # +########################################################## +port = 8889 + +#Valid values can be found in any of these locations on your Linux system (some may not exist): +# /usr/include/linux/input-event-codes.h +#The virtual device is defined on the device variable and the mapping of the keys on btn_map +#RECAP keynames (DO NOT TOUCH) are the keys that we expect commming from the 3ds, device is +#the virtual device that we define and btn_map maps what we recieve with our virtual device +btn_map = { + "A": uinput.BTN_A, + "B": uinput.BTN_B, + "X": uinput.BTN_X, + "Y": uinput.BTN_Y, + "L": uinput.BTN_TL, + "R": uinput.BTN_TR, + "ZL": uinput.BTN_THUMBL, + "ZR": uinput.BTN_THUMBR, + "Left": uinput.BTN_DPAD_LEFT, + "Right": uinput.BTN_DPAD_RIGHT, + "Up": uinput.BTN_DPAD_UP, + "Down": uinput.BTN_DPAD_DOWN, + "Start": uinput.BTN_START, + "Select": uinput.BTN_SELECT, + "Tap": uinput.BTN_MODE, +} + +device = uinput.Device([ + uinput.ABS_X + (-159, 159, 0, 10), + uinput.ABS_Y + (-159, 159, 0, 10), + uinput.ABS_RX + (-146, 146, 0, 16), + uinput.ABS_RY + (-146, 146, 0, 16), + uinput.BTN_A, + uinput.BTN_B, + uinput.BTN_X, + uinput.BTN_Y, + uinput.BTN_TL, + uinput.BTN_TR, + uinput.BTN_THUMBL, + uinput.BTN_THUMBR, + uinput.BTN_DPAD_LEFT, + uinput.BTN_DPAD_RIGHT, + uinput.BTN_DPAD_UP, + uinput.BTN_DPAD_DOWN, + uinput.BTN_START, + uinput.BTN_SELECT, + uinput.BTN_MODE, + ]) +######################################################## +# CONFIGURABLE REGION END - Don't touch anything below # +######################################################## + +def pprint(obj): + import pprint + pprint.PrettyPrinter().pprint(obj) + +class x: pass + +command = x() +command.CONNECT = 0 +command.KEYS = 1 +command.SCREENSHOT = 2 + +keynames = [ + "A", "B", "Select", "Start", "Right", "Left", "Up", "Down", + "R", "L", "X", "Y", None, None, "ZL", "ZR", + None, None, None, None, "Tap", None, None, None, + "CSRight", "CSLeft", "CSUp", "CSDown", "CRight", "CLeft", "CUp", "CDown", +] + +keys = x() +keys.A = 1<<0 +keys.B = 1<<1 +keys.Select = 1<<2 +keys.Start = 1<<3 +keys.Right = 1<<4 +keys.Left = 1<<5 +keys.Up = 1<<6 +keys.Down = 1<<7 +keys.R = 1<<8 +keys.L = 1<<9 +keys.X = 1<<10 +keys.Y = 1<<11 +keys.ZL = 1<<14 # (new 3DS only) +keys.ZR = 1<<15 # (new 3DS only) +keys.Tap = 1<<20 # Not actually provided by HID +keys.CSRight = 1<<24 # c-stick (new 3DS only) +keys.CSLeft = 1<<25 # c-stick (new 3DS only) +keys.CSUp = 1<<26 # c-stick (new 3DS only) +keys.CSDown = 1<<27 # c-stick (new 3DS only) +keys.CRight = 1<<28 # circle pad +keys.CLeft = 1<<29 # circle pad +keys.CUp = 1<<30 # circle pad +keys.CDown = 1<<31 # circle pad + +def press_key(key): + device.emit(key, 1) + +def release_key(key): + device.emit(key,0) + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock.bind(("", port)) + +prevkeys = 0 + +touch_start = 0 +touch_last_x = 0 +touch_last_y = 0 + +while True: + rawdata, addr = sock.recvfrom(4096) + rawdata = bytearray(rawdata) + #print("received message", rawdata, "from", addr) + + if rawdata[0]==command.CONNECT: + pass # CONNECT packets are empty + + if rawdata[0]==command.KEYS: + fields = struct.unpack(" + +void error(const char *functionName); diff --git a/PC/include/joystick.h b/PC/include/joystick.h new file mode 100644 index 0000000..6101009 --- /dev/null +++ b/PC/include/joystick.h @@ -0,0 +1,29 @@ +#pragma once + +#ifndef WINVER + #define WINVER 0x0500 +#endif + +#include + +#include "public.h" +#include "vjoyinterface.h" + +#define joyX iReport.wAxisX +#define joyY iReport.wAxisY +#define joyRX iReport.wAxisXRot +#define joyRY iReport.wAxisYRot +//#define joyVolume iReport.wSlider +#define joyVolume iReport.wAxisZ +#define povHat iReport.bHats + +#define joyButtons iReport.lButtons + +#define JOY_MIDDLE (128 * 128) + +extern int ContPovNumber; +//extern BOOL ContinuousPOV; + +extern JOYSTICK_POSITION iReport; + +BOOL updateJoystick(int); diff --git a/PC/include/keyboard.h b/PC/include/keyboard.h new file mode 100644 index 0000000..a69601d --- /dev/null +++ b/PC/include/keyboard.h @@ -0,0 +1,6 @@ +#pragma once + +extern unsigned char keyboardActive; +extern unsigned char keyboardToggle; + +char currentKeyboardKey(void); diff --git a/PC/include/keys.h b/PC/include/keys.h new file mode 100644 index 0000000..cdcb5bc --- /dev/null +++ b/PC/include/keys.h @@ -0,0 +1,109 @@ +#pragma once + +#include +#include +#include + +// For some reason, these are not defined in winuser.h like they used to be... +#ifndef VK_OEM_MINUS +#define VK_OEM_MINUS 0xBD +#endif +#ifndef VK_OEM_COMMA +#define VK_OEM_COMMA 0xBC +#endif +#ifndef KEYEVENTF_SCANCODE +#define KEYEVENTF_SCANCODE 0x08 +#endif + +#ifndef MAPVK_VK_TO_VSC +#define MAPVK_VK_TO_VSC 0 +#endif + +#define newpress(key) ((currentKeys & key) && !(lastKeys & key)) +#define release(key) (!(currentKeys & key) && (lastKeys & key)) + +#define handleKey(DSKey, PCKey) do {\ + if(!PCKey.useJoypad) {\ + if(newpress(DSKey)) simulateKeyNewpress(PCKey.virtualKey);\ + if(release(DSKey)) simulateKeyRelease(PCKey.virtualKey);\ + }\ + else if(PCKey.useJoypad == 1) {\ + if(currentKeys & DSKey) joyButtons |= PCKey.joypadButton;\ + else joyButtons &= ~PCKey.joypadButton;\ + }\ + else if(PCKey.useJoypad == 2) {\ + if(currentKeys & DSKey) joyButtons |= PCKey.joypadButton << 8;\ + else joyButtons &= ~(PCKey.joypadButton << 8);\ + }\ +} while(0) + +#define BIT(n) (1 << (n)) + +typedef enum { + KEY_A = BIT(0), + KEY_B = BIT(1), + KEY_SELECT = BIT(2), + KEY_START = BIT(3), + KEY_DRIGHT = BIT(4), + KEY_DLEFT = BIT(5), + KEY_DUP = BIT(6), + KEY_DDOWN = BIT(7), + KEY_R = BIT(8), + KEY_L = BIT(9), + KEY_X = BIT(10), + KEY_Y = BIT(11), + KEY_ZL = BIT(14), // (new 3DS only) + KEY_ZR = BIT(15), // (new 3DS only) + KEY_TOUCH = BIT(20), // Not actually provided by HID + KEY_CSTICK_RIGHT = BIT(24), // c-stick (new 3DS only) + KEY_CSTICK_LEFT = BIT(25), // c-stick (new 3DS only) + KEY_CSTICK_UP = BIT(26), // c-stick (new 3DS only) + KEY_CSTICK_DOWN = BIT(27), // c-stick (new 3DS only) + KEY_CPAD_RIGHT = BIT(28), // circle pad + KEY_CPAD_LEFT = BIT(29), // circle pad + KEY_CPAD_UP = BIT(30), // circle pad + KEY_CPAD_DOWN = BIT(31), // circle pad + + // Generic catch-all directions + KEY_UP = KEY_DUP | KEY_CPAD_UP, + KEY_DOWN = KEY_DDOWN | KEY_CPAD_DOWN, + KEY_LEFT = KEY_DLEFT | KEY_CPAD_LEFT, + KEY_RIGHT = KEY_DRIGHT | KEY_CPAD_RIGHT, +} KEYPAD_BITS; + +struct keyMapping { + unsigned char useJoypad; // 0 keyboard key, 1 joypad1-8, 2 joypad9-16, 3 hat + union { + unsigned char virtualKey; + unsigned char joypadButton; + }; +}; + +struct circlePad { + short x; + short y; +}; + +struct cStick { + short x; + short y; +}; + +struct touch { + short x; + short y; +}; + +extern unsigned int lastKeys; +extern unsigned int currentKeys; + +extern unsigned int volume; + +extern struct circlePad circlePad; +extern struct cStick cStick; +extern struct touch lastTouch; +extern struct touch currentTouch; + +unsigned int mapVirtualKey(unsigned int key); +void simulateKeyNewpress(unsigned int key); +void simulateKeyRelease(unsigned int key); diff --git a/PC/include/public.h b/PC/include/public.h new file mode 100644 index 0000000..32dc016 --- /dev/null +++ b/PC/include/public.h @@ -0,0 +1,227 @@ +/*++ + +Copyright (c) Shaul Eizikovich. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + public.h + +Abstract: + + Public header file for the vJoy project + Developpers that need to interface with vJoy need to include this file + +Author: + + +Environment: + + kernel mode and User mode + +Notes: + + +Revision History: + + +--*/ +#ifndef _PUBLIC_H +#define _PUBLIC_H + +// Compilation directives +#define PPJOY_MODE +#undef PPJOY_MODE // Comment-out for compatibility mode + +#ifdef PPJOY_MODE +#include "PPJIoctl.h" +#endif + +#include // Definitions for controlling GUID initialization + +// +// Usage example: +// CreateFile(TEXT("\\\\.\\vJoy"), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); +#ifdef PPJOY_MODE +#define DEVICENAME_STRING "PPJoyIOCTL1" +#else +#define DEVICENAME_STRING "vJoy" +#endif +#define NTDEVICE_NAME_STRING "\\Device\\"DEVICENAME_STRING +#define SYMBOLIC_NAME_STRING "\\DosDevices\\"DEVICENAME_STRING +#define DOS_FILE_NAME "\\\\.\\"DEVICENAME_STRING +#define VJOY_INTERFACE L"Device_" + +// Version parts +#define VER_X_ 0 +#define VER_H_ 2 +#define VER_M_ 0 +#define VER_L_ 5 + +#define STRINGIFY_1(x) #x +#define STRINGIFY(x) STRINGIFY_1(x) +#define PASTE(x, y) x##y +#define MAKEWIDE(x) PASTE(L,x) + +// Device Attributes +// +#define VENDOR_N_ID 0x1234 +#define PRODUCT_N_ID 0xBEAD +#define VERSION_N (VER_L_ + 0x10*VER_M_ + 0x100*VER_H_ + 0x1000*VER_X_) + +// Device Strings +// +#define VENDOR_STR_ID L"Shaul Eizikovich" +#define PRODUCT_STR_ID L"vJoy - Virtual Joystick" +#define SERIALNUMBER_STR MAKEWIDE(STRINGIFY(VER_H_)) L"." MAKEWIDE(STRINGIFY(VER_M_)) L"." MAKEWIDE(STRINGIFY(VER_L_)) + +// Function codes; +//#define LOAD_POSITIONS 0x910 +//#define GETATTRIB 0x911 +// #define GET_FFB_DATA 0x00222912 // METHOD_OUT_DIRECT + FILE_DEVICE_UNKNOWN + FILE_ANY_ACCESS +//#define SET_FFB_STAT 0x913 // METHOD_NEITHER +//#define GET_FFB_STAT 0x916 + +#define F_LOAD_POSITIONS 0x910 +#define F_GETATTRIB 0x911 +#define F_GET_FFB_DATA 0x912 +#define F_SET_FFB_STAT 0x913 +#define F_GET_FFB_STAT 0x916 +#define F_GET_DEV_INFO 0x917 +// IO Device Control codes; +#define IOCTL_VJOY_GET_ATTRIB CTL_CODE (FILE_DEVICE_UNKNOWN, GETATTRIB, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define LOAD_POSITIONS CTL_CODE (FILE_DEVICE_UNKNOWN, F_LOAD_POSITIONS, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define GET_FFB_DATA CTL_CODE (FILE_DEVICE_UNKNOWN, F_GET_FFB_DATA, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) +#define SET_FFB_STAT CTL_CODE (FILE_DEVICE_UNKNOWN, F_SET_FFB_STAT, METHOD_NEITHER, FILE_ANY_ACCESS) +#define GET_FFB_STAT CTL_CODE (FILE_DEVICE_UNKNOWN, F_GET_FFB_STAT, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define GET_DEV_INFO CTL_CODE (FILE_DEVICE_UNKNOWN, F_GET_DEV_INFO, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#ifndef __HIDPORT_H__ +// Copied from hidport.h +#define IOCTL_HID_SET_FEATURE 0xB0191 +#define IOCTL_HID_WRITE_REPORT 0xB000F + +#define MAX_N_DEVICES 16 // Maximum number of vJoy devices + + +typedef struct _HID_DEVICE_ATTRIBUTES { + + ULONG Size; + // + // sizeof (struct _HID_DEVICE_ATTRIBUTES) + // + + // + // Vendor ids of this hid device + // + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + USHORT Reserved[11]; + +} HID_DEVICE_ATTRIBUTES, * PHID_DEVICE_ATTRIBUTES; +#endif + +// Error levels for status report +enum ERRLEVEL {INFO, WARN, ERR, FATAL, APP}; +// Status report function prototype +#ifdef WINAPI +typedef BOOL (WINAPI *StatusMessageFunc)(void * output, TCHAR * buffer, enum ERRLEVEL level); +#endif + +/////////////////////////////////////////////////////////////// + +/////////////////////// Joystick Position /////////////////////// +// +// This structure holds data that is passed to the device from +// an external application such as SmartPropoPlus. +// +// Usage example: +// JOYSTICK_POSITION iReport; +// : +// DeviceIoControl (hDevice, 100, &iReport, sizeof(HID_INPUT_REPORT), NULL, 0, &bytes, NULL) +typedef struct _JOYSTICK_POSITION +{ + BYTE bDevice; // Index of device. 1-based. + LONG wThrottle; + LONG wRudder; + LONG wAileron; + LONG wAxisX; + LONG wAxisY; + LONG wAxisZ; + LONG wAxisXRot; + LONG wAxisYRot; + LONG wAxisZRot; + LONG wSlider; + LONG wDial; + LONG wWheel; + LONG wAxisVX; + LONG wAxisVY; + LONG wAxisVZ; + LONG wAxisVBRX; + LONG wAxisVBRY; + LONG wAxisVBRZ; + LONG lButtons; // 32 buttons: 0x00000001 means button1 is pressed, 0x80000000 -> button32 is pressed + DWORD bHats; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch + DWORD bHatsEx1; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch + DWORD bHatsEx2; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch + DWORD bHatsEx3; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch +} JOYSTICK_POSITION, *PJOYSTICK_POSITION; + +// Superset of JOYSTICK_POSITION +// Extension of JOYSTICK_POSITION with Buttons 33-128 appended to the end of the structure. +typedef struct _JOYSTICK_POSITION_V2 +{ + /// JOYSTICK_POSITION + BYTE bDevice; // Index of device. 1-based. + LONG wThrottle; + LONG wRudder; + LONG wAileron; + LONG wAxisX; + LONG wAxisY; + LONG wAxisZ; + LONG wAxisXRot; + LONG wAxisYRot; + LONG wAxisZRot; + LONG wSlider; + LONG wDial; + LONG wWheel; + LONG wAxisVX; + LONG wAxisVY; + LONG wAxisVZ; + LONG wAxisVBRX; + LONG wAxisVBRY; + LONG wAxisVBRZ; + LONG lButtons; // 32 buttons: 0x00000001 means button1 is pressed, 0x80000000 -> button32 is pressed + DWORD bHats; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch + DWORD bHatsEx1; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch + DWORD bHatsEx2; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch + DWORD bHatsEx3; // Lower 4 bits: HAT switch or 16-bit of continuous HAT switch LONG lButtonsEx1; // Buttons 33-64 + + /// JOYSTICK_POSITION_V2 Extenssion + LONG lButtonsEx1; // Buttons 33-64 + LONG lButtonsEx2; // Buttons 65-96 + LONG lButtonsEx3; // Buttons 97-128 +} JOYSTICK_POSITION_V2, *PJOYSTICK_POSITION_V2; + + +// HID Descriptor definitions +#define HID_USAGE_X 0x30 +#define HID_USAGE_Y 0x31 +#define HID_USAGE_Z 0x32 +#define HID_USAGE_RX 0x33 +#define HID_USAGE_RY 0x34 +#define HID_USAGE_RZ 0x35 +#define HID_USAGE_SL0 0x36 +#define HID_USAGE_SL1 0x37 +#define HID_USAGE_WHL 0x38 +#define HID_USAGE_POV 0x39 + + +#endif + + diff --git a/PC/include/settings.h b/PC/include/settings.h new file mode 100644 index 0000000..769eb50 --- /dev/null +++ b/PC/include/settings.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "keys.h" + +enum analogue { + mouse, + joystick1, + joystick2, + keys, +}; + +enum dPad { + key, + pov, + cPov, +}; + +struct settings { + int port; + int throttle; + enum analogue circlePad; + enum analogue cStick; + enum analogue touch; + enum dPad dPad; + int mouseSpeed; + UINT vJoyDevice; + struct keyMapping A, B, X, Y, L, R, ZL, ZR, Start, Select, Tap, Left, Right, Up, Down, PadLeft, PadRight, PadUp, PadDown, CSLeft, CSRight, CSUp, CSDown; +}; + +extern struct settings settings; +extern struct settings defaultSettings; + +bool readSettings(void); diff --git a/PC/include/vjoyinterface.h b/PC/include/vjoyinterface.h new file mode 100644 index 0000000..0127577 --- /dev/null +++ b/PC/include/vjoyinterface.h @@ -0,0 +1,104 @@ +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the VJOYINTERFACE_EXPORTS +// symbol defined on the command line. this symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// VJOYINTERFACE_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef VJOYINTERFACE_EXPORTS +#define VJOYINTERFACE_API __declspec(dllexport) +#else +#define VJOYINTERFACE_API __declspec(dllimport) +#endif + +///////////////////////////// vJoy device (collection) status //////////////////////////////////////////// +#ifndef VJDSTAT +#define VJDSTAT +enum VjdStat /* Declares an enumeration data type called BOOLEAN */ +{ + VJD_STAT_OWN, // The vJoy Device is owned by this application. + VJD_STAT_FREE, // The vJoy Device is NOT owned by any application (including this one). + VJD_STAT_BUSY, // The vJoy Device is owned by another application. It cannot be acquired by this application. + VJD_STAT_MISS, // The vJoy Device is missing. It either does not exist or the driver is down. + VJD_STAT_UNKN // Unknown +}; + +/* Error codes for some of the functions */ +#define NO_HANDLE_BY_INDEX -1 +#define BAD_PREPARSED_DATA -2 +#define NO_CAPS -3 +#define BAD_N_BTN_CAPS -4 +#define BAD_CALLOC -5 +#define BAD_BTN_CAPS -6 +#define BAD_BTN_RANGE -7 +#define BAD_N_VAL_CAPS -8 +#define BAD_ID_RANGE -9 +#define NO_SUCH_AXIS -10 + +/* Environment Variables */ +#define INTERFACE_LOG_LEVEL "VJOYINTERFACELOGLEVEL" +#define INTERFACE_LOG_FILE "VJOYINTERFACELOGFILE" +#define INTERFACE_DEF_LOG_FILE "vJoyInterface.log" + +struct DEV_INFO { + BYTE DeviceID; // Device ID: Valid values are 1-16 + BYTE nImplemented; // Number of implemented device: Valid values are 1-16 + BYTE isImplemented; // Is this device implemented? + BYTE MaxDevices; // Maximum number of devices that may be implemented (16) + BYTE DriverFFB; // Does this driver support FFB (False) + BYTE DeviceFFB; // Does this device support FFB (False) +} ; + +typedef void (CALLBACK *RemovalCB)(BOOL, BOOL, PVOID); + +#endif +///////////////////////////// vJoy device (collection) Control interface ///////////////////////////////// +/* + These functions allow writing feeders and other applications that interface with vJoy + It is assumed that only one vJoy top-device (= Raw PDO) exists. + This top-level device can have up to 16 siblings (=top-level Reports/collections) + Each sibling is refered to as a "vJoy Device" and is attributed a unique Report ID (Range: 1-16). + + Naming convetion: + VJD = vJoy Device + rID = Report ID +*/ + +///// General driver data +VJOYINTERFACE_API SHORT __cdecl GetvJoyVersion(void); +VJOYINTERFACE_API BOOL __cdecl vJoyEnabled(void); +VJOYINTERFACE_API PVOID __cdecl GetvJoyProductString(void); +VJOYINTERFACE_API PVOID __cdecl GetvJoyManufacturerString(void); +VJOYINTERFACE_API PVOID __cdecl GetvJoySerialNumberString(void); +VJOYINTERFACE_API BOOL __cdecl DriverMatch(WORD * DllVer, WORD * DrvVer); +VJOYINTERFACE_API VOID __cdecl RegisterRemovalCB(RemovalCB cb, PVOID data); + + +///// vJoy Device properties +VJOYINTERFACE_API int __cdecl GetVJDButtonNumber(UINT rID); // Get the number of buttons defined in the specified VDJ +VJOYINTERFACE_API int __cdecl GetVJDDiscPovNumber(UINT rID); // Get the number of descrete-type POV hats defined in the specified VDJ +VJOYINTERFACE_API int __cdecl GetVJDContPovNumber(UINT rID); // Get the number of descrete-type POV hats defined in the specified VDJ +VJOYINTERFACE_API BOOL __cdecl GetVJDAxisExist(UINT rID, UINT Axis); // Test if given axis defined in the specified VDJ +VJOYINTERFACE_API BOOL __cdecl GetVJDAxisMax(UINT rID, UINT Axis, LONG * Max); // Get logical Maximum value for a given axis defined in the specified VDJ +VJOYINTERFACE_API BOOL __cdecl GetVJDAxisMin(UINT rID, UINT Axis, LONG * Min); // Get logical Minimum value for a given axis defined in the specified VDJ + +///// Write access to vJoy Device - Basic +VJOYINTERFACE_API BOOL __cdecl AcquireVJD(UINT rID); // Acquire the specified vJoy Device. +VJOYINTERFACE_API VOID __cdecl RelinquishVJD(UINT rID); // Relinquish the specified vJoy Device. +VJOYINTERFACE_API BOOL __cdecl UpdateVJD(UINT rID, PVOID pData); // Update the position data of the specified vJoy Device. +VJOYINTERFACE_API enum VjdStat __cdecl GetVJDStatus(UINT rID); // Get the status of the specified vJoy Device. + +///// Write access to vJoy Device - Modifyiers +// This group of functions modify the current value of the position data +// They replace the need to create a structure of position data then call UpdateVJD + +//// Reset functions +VJOYINTERFACE_API BOOL __cdecl ResetVJD(UINT rID); // Reset all controls to predefined values in the specified VDJ +VJOYINTERFACE_API VOID __cdecl ResetAll(void); // Reset all controls to predefined values in all VDJ +VJOYINTERFACE_API BOOL __cdecl ResetButtons(UINT rID); // Reset all buttons (To 0) in the specified VDJ +VJOYINTERFACE_API BOOL __cdecl ResetPovs(UINT rID); // Reset all POV Switches (To -1) in the specified VDJ + +// Write data +VJOYINTERFACE_API BOOL __cdecl SetAxis(LONG Value, UINT rID, UINT Axis); // Write Value to a given axis defined in the specified VDJ +VJOYINTERFACE_API BOOL __cdecl SetBtn(BOOL Value, UINT rID, UCHAR nBtn); // Write Value to a given button defined in the specified VDJ +VJOYINTERFACE_API BOOL __cdecl SetDiscPov(int Value, UINT rID, UCHAR nPov); // Write Value to a given descrete POV defined in the specified VDJ +VJOYINTERFACE_API BOOL __cdecl SetContPov(DWORD Value, UINT rID, UCHAR nPov); // Write Value to a given continuous POV defined in the specified VDJ diff --git a/PC/include/wireless.h b/PC/include/wireless.h new file mode 100644 index 0000000..4bff563 --- /dev/null +++ b/PC/include/wireless.h @@ -0,0 +1,84 @@ +#pragma once + +#ifndef WINVER + #define WINVER 0x0500 +#endif + +#include +#include + +#include + +#define SCREENSHOT_CHUNK 4000 + +#define IP INADDR_ANY + +enum NET_COMMANDS { + CONNECT, + KEYS, + SCREENSHOT, +}; + +// It is deliberately set up to have an anonymous struct as well as a named struct for convenience, not a mistake! +struct packet { + struct packetHeader { + unsigned char command; + unsigned char keyboardActive; + }; + struct packetHeader packetHeader; + + union { + // CONNECT + struct connectPacket { + }; + struct connectPacket connectPacket; + + // KEYS + struct keysPacket { + unsigned int keys; + + struct { + short x; + short y; + } circlePad; + + struct { + unsigned short x; + unsigned short y; + } touch; + + struct { + short x; + short y; + } cStick; + + unsigned int volume; + }; + struct keysPacket keysPacket; + + // SCREENSHOT + struct screenshotPacket { + unsigned short offset; + unsigned char data[SCREENSHOT_CHUNK]; + }; + struct screenshotPacket screenshotPacket; + }; +}; + +extern SOCKET listener; +extern SOCKET client; + +extern struct sockaddr_in client_in; + +extern int sockaddr_in_sizePtr; + +extern struct packet buffer; +extern char hostName[80]; + +void initNetwork(void); +void printIPs(void); +void startListening(void); +void sendBuffer(int length); +int receiveBuffer(int length); + +void sendScreenshot(void); diff --git a/PC/lib/vJoyInterface.lib b/PC/lib/vJoyInterface.lib new file mode 100644 index 0000000..7752ec4 Binary files /dev/null and b/PC/lib/vJoyInterface.lib differ diff --git a/PC/source/general.c b/PC/source/general.c new file mode 100644 index 0000000..bf7e137 --- /dev/null +++ b/PC/source/general.c @@ -0,0 +1,18 @@ +#include "wireless.h" + +#include "general.h" + +void error(const char *functionName) { + char errorMsg[92]; + ZeroMemory(errorMsg, 92); + + sprintf(errorMsg, "Call to %s returned error %d!", (char *)functionName, WSAGetLastError()); + + MessageBox(NULL, errorMsg, "socketIndication", MB_OK); + + closesocket(client); + closesocket(listener); + WSACleanup(); + + exit(0); +} diff --git a/PC/source/joystick.c b/PC/source/joystick.c new file mode 100644 index 0000000..bdf4f2c --- /dev/null +++ b/PC/source/joystick.c @@ -0,0 +1,26 @@ +#include +#include + +#include "joystick.h" + +int ContPovNumber; +//BOOL ContinuousPOV = FALSE; + +JOYSTICK_POSITION iReport; + +BOOL updateJoystick(iInterface) { + BYTE id = (BYTE)iInterface; + + iReport.bDevice = id; + + if(!UpdateVJD(iInterface, (PVOID)&iReport)) { + /*printf("vJoy device %d failed - try to enable device\n", iInterface); + printf("PRESS ENTER TO CONTINUE\n"); + getchar(); + AcquireVJD(iInterface); + ContinuousPOV = (BOOL)GetVJDContPovNumber(iInterface);*/ + return false; + } + + return true; +} diff --git a/PC/source/keyboard.c b/PC/source/keyboard.c new file mode 100644 index 0000000..a2b52d2 --- /dev/null +++ b/PC/source/keyboard.c @@ -0,0 +1,21 @@ +#include + +#include "keys.h" + +#include "keyboard.h" + +unsigned char keyboardActive = false; +unsigned char keyboardToggle = true; + +inline char currentKeyboardKey(void) { + const char chars[60] = "!1234567890\x08QWERTYUIOP\13\13ASDFGHJKL-\13\13ZXCVBNM,.?\13\13\0\0\0 \0\0\0\0"; + + if(currentTouch.x >= 1 && currentTouch.x <= 312 && currentTouch.y >= 78 && currentTouch.y <= 208) { + int x = (int)((float)currentTouch.x * 12.0f / 320.0f); + int y = (int)((float)(currentTouch.y - 78) * 12.0f / 320.0f); + + return chars[x + y * 12]; + } + + else return 0; +} diff --git a/PC/source/keys.c b/PC/source/keys.c new file mode 100644 index 0000000..75b019f --- /dev/null +++ b/PC/source/keys.c @@ -0,0 +1,80 @@ +#include "keys.h" +#include "joystick.h" + +// Sideband comunication with vJoy Device +//{781EF630-72B2-11d2-B852-00C04FAD5101} +//DEFINE_GUID(GUID_DEVINTERFACE_VJOY, 0x781EF630, 0x72B2, 0x11d2, 0xB8, 0x52, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x01); + +unsigned int lastKeys; +unsigned int currentKeys; +unsigned int volume; //slider + +struct circlePad circlePad; +struct cStick cStick; +struct touch lastTouch; +struct touch currentTouch; + +inline unsigned int mapVirtualKey(unsigned int key) { + return MapVirtualKey(key, MAPVK_VK_TO_VSC); +} + +void simulateKeyNewpress(unsigned int key) { + if(!key) return; + + unsigned char unshift = 0; + + INPUT ip = { 0 }; + + if(key == VK_LBUTTON || key == VK_RBUTTON) { + ip.type = INPUT_MOUSE; + ip.mi.dwFlags = key == VK_LBUTTON ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN; + } + else { + if(key == '!') { + key = '1'; + simulateKeyNewpress(VK_SHIFT); + unshift = 1; + } + else if(key == '?') { + key = VK_DIVIDE; + simulateKeyNewpress(VK_SHIFT); + unshift = 1; + } + + else if(key == '-') key = VK_OEM_MINUS; + else if(key == ',') key = VK_OEM_COMMA; + else if(key == '\13') key = VK_RETURN; + + ip.type = INPUT_KEYBOARD; + ip.ki.wScan = mapVirtualKey(key); + ip.ki.time = 0; + ip.ki.dwExtraInfo = 0; + ip.ki.wVk = 0; + ip.ki.dwFlags = KEYEVENTF_SCANCODE; + } + + SendInput(1, &ip, sizeof(INPUT)); + + if(unshift) simulateKeyRelease(VK_SHIFT); +} + +void simulateKeyRelease(unsigned int key) { + if(!key) return; + + INPUT ip = { 0 }; + + if(key == VK_LBUTTON || key == VK_RBUTTON) { + ip.type = INPUT_MOUSE; + ip.mi.dwFlags = key == VK_LBUTTON ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP; + } + else { + ip.type = INPUT_KEYBOARD; + ip.ki.wScan = mapVirtualKey(key); + ip.ki.time = 0; + ip.ki.dwExtraInfo = 0; + ip.ki.wVk = 0; + ip.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; + } + + SendInput(1, &ip, sizeof(INPUT)); +} \ No newline at end of file diff --git a/PC/source/main.c b/PC/source/main.c new file mode 100644 index 0000000..54504b3 --- /dev/null +++ b/PC/source/main.c @@ -0,0 +1,300 @@ +// 3DS Controller Server + +#define VERSION "0.7.2" + +#include +#include +#include + +#include "wireless.h" +#include "keys.h" +#include "general.h" +#include "joystick.h" +#include "settings.h" +#include "keyboard.h" + +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow) { + printf("3DS Controller Server %s\n", VERSION); + + DWORD screenWidth = GetSystemMetrics(SM_CXSCREEN); + DWORD screenHeight = GetSystemMetrics(SM_CYSCREEN); + + double widthMultiplier = screenWidth / 320.0; + double heightMultiplier = screenHeight / 240.0; + + if(!readSettings()) { + printf("Couldn't read settings file, using default key bindings.\n"); + } + + bool vJoy = true; + UINT iInterface = settings.vJoyDevice; + + iReport.wAxisX = JOY_MIDDLE; + iReport.wAxisY = JOY_MIDDLE; + iReport.wAxisZ = JOY_MIDDLE; + iReport.wAxisXRot = JOY_MIDDLE; + iReport.wAxisYRot = JOY_MIDDLE; + iReport.wAxisZRot = JOY_MIDDLE; + iReport.wSlider = JOY_MIDDLE; + iReport.wDial = JOY_MIDDLE; + iReport.lButtons = 0; + iReport.bHats = -1; + + if(vJoy && !vJoyEnabled()) { + printf("vJoy failed (1)! Buttons will still work, but joy stick won't work.\n"); + vJoy = false; + } + + enum VjdStat status = GetVJDStatus(iInterface); + if(vJoy && (status == VJD_STAT_OWN || (status == VJD_STAT_FREE && !AcquireVJD(iInterface)))) { + printf("vJoy failed (2)! Buttons will still work, but joy stick won't work.\n"); + vJoy = false; + } + + ContPovNumber = GetVJDContPovNumber(iInterface); + //int DiscPovNumber = GetVJDDiscPovNumber(iInterface); + + if((settings.dPad == pov) && !(ContPovNumber == 0)) settings.dPad = cPov; + + if(vJoy && !updateJoystick(iInterface)) { + printf("vJoy failed (3)! Buttons will still work, but joystick won't work.\nIs vJoy device %d configured?\n",iInterface); + vJoy = false; + } else printf("Connected to vJoy device %d\n",iInterface); + + initNetwork(); + + char nButtons = GetVJDButtonNumber(iInterface); + if(vJoy && nButtons <16) printf("Your vJoy has %d buttons, 3DSController supports 16!\n", nButtons); + + printf("Port: %d\n", settings.port); + + printf("Running on: %s\n", hostName); + + printf("Your local IP(s):\n"); + printIPs(); + + printf("\n"); + + startListening(); + + while(1) { + memset(&buffer, 0, sizeof(struct packet)); + + while(receiveBuffer(sizeof(struct packet)) <= 0) { + // Waiting + + Sleep(settings.throttle); + } + + keyboardActive = buffer.keyboardActive; + + switch(buffer.command) { + case CONNECT: + lastKeys = 0; + currentKeys = 0; + circlePad.x = 0; + circlePad.y = 0; + lastTouch.x = 0; + lastTouch.y = 0; + currentTouch.x = 0; + currentTouch.y = 0; + cStick.x = 0; + cStick.y = 0; + + buffer.command = CONNECT; + printf("3DS Connected!\n"); + + Sleep(50); + sendBuffer(1); + + Sleep(50); + sendBuffer(1); + + Sleep(50); + sendBuffer(1); + break; + + case KEYS: + lastKeys = currentKeys; + if(currentKeys & KEY_TOUCH) lastTouch = currentTouch; + + memcpy(¤tKeys, &buffer.keys, 4); + memcpy(&circlePad, &buffer.circlePad, 4); + memcpy(¤tTouch, &buffer.touch, 4); + memcpy(&cStick, &buffer.cStick, 4); + memcpy(&volume, &buffer.volume, 4); + //printf("\rVolume is currently: %x ", volume); //test + + handleKey(KEY_A, settings.A); + handleKey(KEY_B, settings.B); + handleKey(KEY_SELECT, settings.Select); + handleKey(KEY_START, settings.Start); + if(settings.dPad == key) { //Handle normally if not using POV in settings. + handleKey(KEY_DRIGHT, settings.Right); + handleKey(KEY_DLEFT, settings.Left); + handleKey(KEY_DUP, settings.Up); + handleKey(KEY_DDOWN, settings.Down); + } + handleKey(KEY_R, settings.R); + handleKey(KEY_L, settings.L); + handleKey(KEY_ZR, settings.ZR); + handleKey(KEY_ZL, settings.ZL); + handleKey(KEY_X, settings.X); + handleKey(KEY_Y, settings.Y); + + if(settings.circlePad == keys) { + handleKey(KEY_CPAD_RIGHT, settings.PadRight); + handleKey(KEY_CPAD_LEFT, settings.PadLeft); + handleKey(KEY_CPAD_UP, settings.PadUp); + handleKey(KEY_CPAD_DOWN, settings.PadDown); + } + + if(settings.cStick == keys) { + handleKey(KEY_CSTICK_RIGHT, settings.CSRight); + handleKey(KEY_CSTICK_LEFT, settings.CSLeft); + handleKey(KEY_CSTICK_UP, settings.CSUp); + handleKey(KEY_CSTICK_DOWN, settings.CSDown); + } + + //handleKey(KEY_LID, 'I'); + + if(newpress(KEY_TOUCH)) { + lastTouch.x = currentTouch.x; + lastTouch.y = currentTouch.y; + } + + if((currentKeys & KEY_TOUCH)) { + if(keyboardActive) { + if(newpress(KEY_TOUCH)) { + char letter = currentKeyboardKey(); + if(letter) { + simulateKeyNewpress(letter); + simulateKeyRelease(letter); + } + } + } + else if(settings.touch == mouse) { + if(settings.mouseSpeed) { + POINT p; + GetCursorPos(&p); + SetCursorPos(p.x + (currentTouch.x - lastTouch.x) * settings.mouseSpeed, p.y + (currentTouch.y - lastTouch.y) * settings.mouseSpeed); + } + else { + SetCursorPos((int)((double)currentTouch.x * widthMultiplier), (int)((double)currentTouch.y * heightMultiplier)); + } + } + else if(settings.touch == joystick1) { //made a little bit more accurate to the screen size. + joyX = (int)((float)(currentTouch.x) * 102.3f); + joyY = (int)((float)(currentTouch.y) * 136.5f); + } + + else if(settings.touch == joystick2) { + joyRX = (int)((float)(currentTouch.x) * 102.3f); + joyRY = (int)((float)(currentTouch.y) * 136.5f); + } + else { + handleKey(KEY_TOUCH, settings.Tap); + } + } else { //If we are not touching, move to center (Like if you release the joystick on a normal controller). + if(settings.touch == joystick1) { + joyX = 16383; //Halfway between the x + joyY = 16383; //Halfway between the y + } + + else if(settings.touch == joystick2) { + joyRX = 16383; //Halfway between the rx + joyRY = 16383; //Halfway between the ry + } + } + + if(settings.circlePad == mouse) { + if(abs(circlePad.x) < settings.mouseSpeed * 3) circlePad.x = 0; + if(abs(circlePad.y) < settings.mouseSpeed * 3) circlePad.y = 0; + + POINT p; + GetCursorPos(&p); + SetCursorPos(p.x + (circlePad.x * settings.mouseSpeed) / 32, p.y - (circlePad.y * settings.mouseSpeed) / 32); + } + else if(settings.circlePad == joystick1) { + joyX = (circlePad.x + 128) * 128; + joyY = (128 - circlePad.y) * 128; + } + + else if(settings.circlePad == joystick2) { + joyRX = (circlePad.x + 128) * 128; + joyRY = (128 - circlePad.y) * 128; + } + + if(settings.cStick == mouse) { + if(abs(cStick.x) < settings.mouseSpeed * 3) cStick.x = 0; + if(abs(cStick.y) < settings.mouseSpeed * 3) cStick.y = 0; + + POINT p; + GetCursorPos(&p); + SetCursorPos(p.x + (cStick.x * settings.mouseSpeed) / 32, p.y - (cStick.y * settings.mouseSpeed) / 32); + } + + else if(settings.cStick == joystick1) { + joyX = (cStick.x + 128) * 128; + joyY = (128 - cStick.y) * 128; + } + + else if(settings.cStick == joystick2) { + joyRX = (cStick.x + 128) * 128; + joyRY = (128 - cStick.y) * 128; + } + + + if(settings.dPad == cPov) { + if((currentKeys & KEY_DUP) && !(currentKeys & KEY_DLEFT)) { + if((currentKeys & KEY_DRIGHT)) { + povHat = 4500; + } else { + povHat = 0; + } + } else if((currentKeys & KEY_DRIGHT)) { + if((currentKeys & KEY_DDOWN)) { + povHat = 13500; + } else { + povHat = 9000; + } + } else if((currentKeys & KEY_DDOWN)) { + if((currentKeys & KEY_DLEFT)) { + povHat = 22500; + } else { + povHat = 18000; + } + } else if((currentKeys & KEY_DLEFT)) { + if ((currentKeys & KEY_DUP)) { + povHat = 31500; + } else { + povHat = 27000; + } + } + + if(!((currentKeys & KEY_DUP) || (currentKeys & KEY_DRIGHT) || (currentKeys & KEY_DDOWN) || (currentKeys & KEY_DLEFT))) { + //If none are pressed, reset the POV hat + povHat = -1; + } + + } + + else if(settings.dPad == pov) { + if((currentKeys & KEY_DUP) && !(currentKeys & KEY_DLEFT)) iReport.bHats = 0; + else if(currentKeys & KEY_DRIGHT) iReport.bHats = 1; + else if (currentKeys & KEY_DDOWN) iReport.bHats = 2; + else if (currentKeys & KEY_DLEFT) iReport.bHats = 3; + else iReport.bHats = -1; + } + + joyVolume = volume * 512; + + break; + } + + if(vJoy) updateJoystick(iInterface); + } + + error("accept()"); + return 0; +} diff --git a/PC/source/settings.c b/PC/source/settings.c new file mode 100644 index 0000000..8cb13e7 --- /dev/null +++ b/PC/source/settings.c @@ -0,0 +1,201 @@ +#include +#include +#include + +#include "keys.h" +#include "wireless.h" + +#include "settings.h" + +struct settings settings; + +struct settings defaultSettings = { + port: 8889, + throttle: 20, + circlePad: joystick1, + cStick: joystick2, + touch: mouse, + mouseSpeed: 4, + vJoyDevice: 1, + A: { 1, {'A'} }, + B: { 1, {'B'} }, + X: { 1, {'X'} }, + Y: { 1, {'Y'} }, + L: { 1, {'L'} }, + R: { 1, {'R'} }, + ZL: { 1, {'Q'} }, + ZR: { 1, {'W'} }, + Left: { 1, {VK_LEFT} }, + Right: { 1, {VK_RIGHT} }, + Up: { 1, {VK_UP} }, + Down: { 1, {VK_DOWN} }, + Start: { 1, {VK_RETURN} }, + Select: { 1, {VK_BACK} }, + Tap: { 1, {'T'} }, +}; + +static bool getSetting(char *name, char *src, char *dest) { + char *start = strstr(src, name); + + if(start) { + start += strlen(name); + + char *end = start + strlen(start); + if(strstr(start, "\n") - 1 < end) end = strstr(start, "\n") - 1; + size_t size = (size_t)end - (size_t)start; + + strncpy(dest, start, size); + dest[size] = '\0'; + + return true; + } + + return false; +} + +static struct keyMapping getButton(char *string) { + struct keyMapping k = { 1, {0} }; + + k.useJoypad = 0; + if(strcmp(string, "SPACE") == 0) k.virtualKey = VK_SPACE; + else if(strcmp(string, "CLICK") == 0) k.virtualKey = VK_LBUTTON; + else if(strcmp(string, "RIGHT CLICK") == 0) k.virtualKey = VK_RBUTTON; + else if(strcmp(string, "ENTER") == 0) k.virtualKey = VK_RETURN; + else if(strcmp(string, "BACKSPACE") == 0) k.virtualKey = VK_BACK; + else if(strcmp(string, "SHIFT") == 0) k.virtualKey = VK_SHIFT; + else if(strcmp(string, "TAB") == 0) k.virtualKey = VK_TAB; + else if(strcmp(string, "LEFT") == 0) k.virtualKey = VK_LEFT; + else if(strcmp(string, "RIGHT") == 0) k.virtualKey = VK_RIGHT; + else if(strcmp(string, "UP") == 0) k.virtualKey = VK_UP; + else if(strcmp(string, "DOWN") == 0) k.virtualKey = VK_DOWN; + else if(strcmp(string, "PAGE UP") == 0) k.virtualKey = VK_PRIOR; + else if(strcmp(string, "PAGE DOWN") == 0) k.virtualKey = VK_NEXT; + else if(strcmp(string, "WINDOWS") == 0) k.virtualKey = VK_LWIN; + else if(strcmp(string, "ESCAPE") == 0) k.virtualKey = VK_ESCAPE; + else if(strcmp(string, "CONTROL") == 0) k.virtualKey = VK_CONTROL; + else if(strcmp(string, "ALT") == 0) k.virtualKey = VK_MENU; + else if(strcmp(string, "NONE") == 0) k.virtualKey = 0; + + else if(strcmp(string, "JOY1") == 0) { k.useJoypad = 1; k.joypadButton = 1 << 0; } + else if(strcmp(string, "JOY2") == 0) { k.useJoypad = 1; k.joypadButton = 1 << 1; } + else if(strcmp(string, "JOY3") == 0) { k.useJoypad = 1; k.joypadButton = 1 << 2; } + else if(strcmp(string, "JOY4") == 0) { k.useJoypad = 1; k.joypadButton = 1 << 3; } + else if(strcmp(string, "JOY5") == 0) { k.useJoypad = 1; k.joypadButton = 1 << 4; } + else if(strcmp(string, "JOY6") == 0) { k.useJoypad = 1; k.joypadButton = 1 << 5; } + else if(strcmp(string, "JOY7") == 0) { k.useJoypad = 1; k.joypadButton = 1 << 6; } + else if(strcmp(string, "JOY8") == 0) { k.useJoypad = 1; k.joypadButton = 1 << 7; } + else if(strcmp(string, "JOY9") == 0) { k.useJoypad = 2; k.joypadButton = 1 << 0; } + else if(strcmp(string, "JOY10") == 0) { k.useJoypad = 2; k.joypadButton = 1 << 1; } + else if(strcmp(string, "JOY11") == 0) { k.useJoypad = 2; k.joypadButton = 1 << 2; } + else if(strcmp(string, "JOY12") == 0) { k.useJoypad = 2; k.joypadButton = 1 << 3; } + else if(strcmp(string, "JOY13") == 0) { k.useJoypad = 2; k.joypadButton = 1 << 4; } + else if(strcmp(string, "JOY14") == 0) { k.useJoypad = 2; k.joypadButton = 1 << 5; } + else if(strcmp(string, "JOY15") == 0) { k.useJoypad = 2; k.joypadButton = 1 << 6; } + else if(strcmp(string, "JOY16") == 0) { k.useJoypad = 2; k.joypadButton = 1 << 7; } + + else k.virtualKey = (int)string[0]; + + return k; +} + +bool readSettings(void) { + FILE *f; + size_t len = 0; + char *buffer = NULL; + + memcpy(&settings, &defaultSettings, sizeof(struct settings)); + + f = fopen("3DSController.ini", "rb"); + if(!f) { + return false; + } + + fseek(f, 0, SEEK_END); + len = ftell(f); + rewind(f); + + buffer = malloc(len); + if(!buffer) { + fclose(f); + return false; + } + + fread(buffer, 1, len, f); + + char setting[64] = { '\0' }; + + if(getSetting("Port: ", buffer, setting)) { + sscanf(setting, "%d", &settings.port); + } + + if(getSetting("Throttle: ", buffer, setting)) { + sscanf(setting, "%d", &settings.throttle); + } + + if(getSetting("Circle Pad: ", buffer, setting)) { + if(strcmp(setting, "MOUSE") == 0) settings.circlePad = mouse; + else if(strcmp(setting, "JOYSTICK1") == 0) settings.circlePad = joystick1; + else if(strcmp(setting, "JOYSTICK2") == 0) settings.circlePad = joystick2; + else if(strcmp(setting, "KEYS") == 0) settings.circlePad = keys; + } + + if(getSetting("C Stick: ", buffer, setting)) { + if(strcmp(setting, "MOUSE") == 0) settings.cStick = mouse; + else if(strcmp(setting, "JOYSTICK1") == 0) settings.cStick = joystick1; + else if(strcmp(setting, "JOYSTICK2") == 0) settings.cStick = joystick2; + else if(strcmp(setting, "KEYS") == 0) settings.cStick = keys; + } + + if(getSetting("D Pad: ", buffer, setting)) { + if(strcmp(setting, "KEYS") == 0) settings.dPad = key; + if(strcmp(setting, "POV") == 0) settings.dPad = pov; + } + + if(getSetting("Touch: ", buffer, setting)) { + if(strcmp(setting, "MOUSE") == 0) settings.touch = mouse; + else if(strcmp(setting, "JOYSTICK1") == 0) settings.touch = joystick1; + else if(strcmp(setting, "JOYSTICK2") == 0) settings.touch = joystick2; + } + + if(getSetting("Mouse Speed: ", buffer, setting)) { + sscanf(setting, "%d", &settings.mouseSpeed); + } + + if(getSetting("vJoy Device: ", buffer, setting)) { + sscanf(setting, "%d", &settings.vJoyDevice); + } + + if(getSetting("A: ", buffer, setting)) settings.A = getButton(setting); + if(getSetting("B: ", buffer, setting)) settings.B = getButton(setting); + if(getSetting("X: ", buffer, setting)) settings.X = getButton(setting); + if(getSetting("Y: ", buffer, setting)) settings.Y = getButton(setting); + if(getSetting("L: ", buffer, setting)) settings.L = getButton(setting); + if(getSetting("R: ", buffer, setting)) settings.R = getButton(setting); + if(getSetting("ZL: ", buffer, setting)) settings.ZL = getButton(setting); + if(getSetting("ZR: ", buffer, setting)) settings.ZR = getButton(setting); + if(getSetting("Left: ", buffer, setting)) settings.Left = getButton(setting); + if(getSetting("Right: ", buffer, setting)) settings.Right = getButton(setting); + if(getSetting("Up: ", buffer, setting)) settings.Up = getButton(setting); + if(getSetting("Down: ", buffer, setting)) settings.Down = getButton(setting); + if(getSetting("Start: ", buffer, setting)) settings.Start = getButton(setting); + if(getSetting("Select: ", buffer, setting)) settings.Select = getButton(setting); + if(getSetting("Tap: ", buffer, setting)) settings.Tap = getButton(setting); + + if(settings.circlePad == keys) { + if(getSetting("Pad Left: ", buffer, setting)) settings.PadLeft = getButton(setting); + if(getSetting("Pad Right: ", buffer, setting)) settings.PadRight = getButton(setting); + if(getSetting("Pad Up: ", buffer, setting)) settings.PadUp = getButton(setting); + if(getSetting("Pad Down: ", buffer, setting)) settings.PadDown = getButton(setting); + } + + if(settings.cStick == keys) { + if(getSetting("C Stick Left: ", buffer, setting)) settings.CSLeft = getButton(setting); + if(getSetting("C Stick Right: ", buffer, setting)) settings.CSRight = getButton(setting); + if(getSetting("C Stick Up: ", buffer, setting)) settings.CSUp = getButton(setting); + if(getSetting("C Stick Down: ", buffer, setting)) settings.CSDown = getButton(setting); + } + + fclose(f); + + return true; +} diff --git a/PC/source/wireless.c b/PC/source/wireless.c new file mode 100644 index 0000000..389989a --- /dev/null +++ b/PC/source/wireless.c @@ -0,0 +1,80 @@ +#include + +#include "general.h" + +#include "settings.h" + +#include "wireless.h" + +SOCKET listener; +SOCKET client; + +struct sockaddr_in client_in; + +int sockaddr_in_sizePtr = (int)sizeof(struct sockaddr_in); + +struct packet buffer; +char hostName[80]; + +void initNetwork(void) { + WSADATA wsaData; + + WSAStartup(MAKEWORD(2, 2), &wsaData); + + if(gethostname(hostName, sizeof(hostName)) == SOCKET_ERROR) { + error("gethostname()"); + } +} + +void printIPs(void) { + struct hostent *phe = gethostbyname(hostName); + if(phe == 0) { + error("gethostbyname()"); + } + + int i; + for(i = 0; phe->h_addr_list[i] != 0; i++) { + struct in_addr addr; + memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); + printf("%s\n", inet_ntoa(addr)); + } + + if(i) { + printf("Usually you want the first one.\n"); + } +} + +void startListening(void) { + int nret; + + listener = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if(listener == INVALID_SOCKET) { + error("socket()"); + } + + SOCKADDR_IN serverInfo; + + serverInfo.sin_family = AF_INET; + serverInfo.sin_addr.s_addr = IP; + serverInfo.sin_port = htons(settings.port); + + u_long one = 1; + ioctlsocket(listener, FIONBIO, &one); + + nret = bind(listener, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr)); + + if(nret == SOCKET_ERROR) { + error("bind()"); + } +} + +void sendBuffer(int length) { + if(sendto(listener, (char *)&buffer, length, 0, (struct sockaddr *)&client_in, sizeof(struct sockaddr_in)) != length) { + error("sendto"); + } +} + +int receiveBuffer(int length) { + return recvfrom(listener, (char *)&buffer, length, 0, (struct sockaddr *)&client_in, &sockaddr_in_sizePtr); +} diff --git a/PC/vJoyInterface.dll b/PC/vJoyInterface.dll new file mode 100644 index 0000000..8832f23 Binary files /dev/null and b/PC/vJoyInterface.dll differ diff --git a/README.md b/README.md index b89c863..f6383b2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,46 @@ -3DSController -============= +3DSController ![](/3DS/cxi/icon48x48.png?raw=true) +=== +A 3DS homebrew application which allows you to use your 3DS as a wireless controller for Windows. + +### Download +The latest release will always be downloadable from [here](https://github.com/CTurt/3DSController/releases/). + +If you are updating to 0.6 from an older version, you will need to make sure you update vJoy to the recommended version. + +### Setup and Usage +Firstly, if you want to be able to register the circle pad or touch screen as a joystick you will need to install [vJoy (version 2.0.5-120515 is preferable)](http://sourceforge.net/projects/vjoystick/files/Beta%202.x/2.0.5-120515/vJoy_205_050515.exe/download). However, if you just want to use keyboard buttons, this is not necessary. + +Extract the archive and copy the executable in the `3DS` directory with the extension that applies to your loader: `3DSController.3dsx` and `3DSController.smdh` for Ninjhax, `3DSController.3ds` for flashcards, or `3DSController.cia` for CFWs, into your 3DS's SD card or flashcard's micro SD card. + +Copy the file `3DS/3DSController.ini` to the root of your 3DS's SD card, and change the line that says `IP: 192.168.0.4` to match your computer's local IP. + +If you are unsure of your local IP address, run `3DSController.exe` and it will tell you. + +Run `3DSController.exe` on your computer. If you are prompted, make sure to allow it through your firewall. + +Start the application on your 3DS, there is no GUI, it will automatically try to connect to the IP address you put in `3DSController.ini`. + +If it wasn't able to read the IP from `3DSController.ini`, it will notify you and quit. + +Otherwise, you should just see a black screen, this is a good sign. To see if it works, open Notepad and press some buttons on the 3DS, they should show up. You can also test if the joystick works by going to Configure USB Game Controllers in Control Panel, it shows up as vJoy. + +If using version 0.4 or above you can press L, R and X to bring up the keyboard. Press L, R and X again to close it. + +If using version 0.6 or above, up to 16 joystick buttons are available. If you wish to use more than 8, you need to configure vJoy. Search in your start menu for vJoyConfig and set buttons to 16. + +If using version 0.7 or above, the output vJoy device is configurable. To output to a device other than 1 it must first be enabled in vJoyConfig. + +If using Ninjhax press Start and Select to return to the Homebrew Loader, otherwise you can just exit with the Home button. + +### Configuration +Find the line `Port: 8889` and change it to your desired port, do this for both the 3DS's `3DSController.ini` and the PC's `3DSController.ini`. + +To use custom key bindings, just change the PC's `3DSController.ini` file, it should be straight forward. + +### Troubleshooting +- Make sure that you are using the 3DS and PC application from the same release, +- Make sure your 3DS has internet access (turn on the switch on the side of the 3DS so that an orange light shows) and is on the same network as your PC, +- Make sure that the `3DSController.ini` is in the root of your 3DS's SD card (not flashcard micro SD), +- Make sure that the `3DSController.ini` has the local IP of your computer, not your public IP, +- Make sure your firewall isn't blocking the application, +- Try using a different port (change the port for both the 3DS and PC's .ini file), \ No newline at end of file