mirror of
https://github.com/wiiu-env/ftpiiu_plugin.git
synced 2024-12-23 03:11:49 +01:00
NDS support
This commit is contained in:
parent
fc02e1ee38
commit
214ab229c6
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,9 +1,12 @@
|
||||
*.nds
|
||||
*.nds.xz
|
||||
*.3dsx
|
||||
*.3dsx.xz
|
||||
*.cia
|
||||
*.cia.xz
|
||||
*.elf
|
||||
*.nacp
|
||||
*.nds
|
||||
*.nro
|
||||
*.nro.xz
|
||||
*.nso
|
||||
@ -11,9 +14,12 @@
|
||||
*.smdh
|
||||
.gdb_history
|
||||
3ds/build
|
||||
3ds-classic/build
|
||||
3ds/romfs/*.t3x
|
||||
linux/build
|
||||
linux/ftpd
|
||||
nds/build
|
||||
switch/build
|
||||
switch-classic/build
|
||||
switch/romfs/*.zst
|
||||
switch/romfs/shaders/*.dksh
|
||||
|
116
Makefile
116
Makefile
@ -1,24 +1,20 @@
|
||||
.PHONY: all nro 3dsx cia clean linux 3dslink nxlink format release release-3dsx release-cia release-3ds release-nro
|
||||
.PHONY: all all-classic format clean
|
||||
.PHONY: dslink 3dslink 3dslink-classic nxlink-classic
|
||||
.PHONY: nds 3dsx cia nro linux
|
||||
.PHONY: 3dsx-classic cia-classic nro-classic
|
||||
.PHONY: release release-nds release-3dsx release-cia release-nro
|
||||
.PHONY: release-3dsx-classic release-cia-classic release-nro-classic
|
||||
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
|
||||
export GITREV := $(shell git rev-parse HEAD 2>/dev/null | cut -c1-8)
|
||||
export GITREV := $(shell git rev-parse HEAD 2>/dev/null | cut -c1-6)
|
||||
export VERSION_MAJOR := 3
|
||||
export VERSION_MINOR := 0
|
||||
export VERSION_MICRO := 0
|
||||
export VERSION := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_MICRO)-rc3
|
||||
export VERSION := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_MICRO)
|
||||
|
||||
ifneq ($(strip $(GITREV)),)
|
||||
export VERSION := $(VERSION)-$(GITREV)
|
||||
endif
|
||||
###########################################################################
|
||||
all: nds 3dsx nro linux
|
||||
|
||||
all: 3dsx nro linux
|
||||
|
||||
nxlink:
|
||||
@$(MAKE) -f Makefile.switch nxlink
|
||||
|
||||
3dslink:
|
||||
@$(MAKE) -f Makefile.3ds 3dslink
|
||||
all-classic: nds 3dsx-classic nro-classic linux
|
||||
|
||||
format:
|
||||
@clang-format -style=file -i $(filter-out \
|
||||
@ -40,40 +36,86 @@ format:
|
||||
source/imgui/imgui_internal.h, \
|
||||
$(shell find source include -type f -name \*.c -o -name \*.cpp -o -name \*.h))
|
||||
|
||||
release: release-3ds release-nro
|
||||
@xz -c <3ds/$(TARGET).3dsx >ftpd.3dsx.xz
|
||||
@xz -c <3ds/$(TARGET).cia >ftpd.cia.xz
|
||||
@xz -c <switch/$(TARGET).nro >ftpd.nro.xz
|
||||
clean:
|
||||
@$(MAKE) -f Makefile.nds clean
|
||||
@$(MAKE) -f Makefile.3ds clean
|
||||
@$(MAKE) -f Makefile.3ds clean CLASSIC="-DCLASSIC"
|
||||
@$(MAKE) -f Makefile.switch clean
|
||||
@$(MAKE) -f Makefile.switch clean CLASSIC="-DCLASSIC"
|
||||
@$(MAKE) -f Makefile.linux clean
|
||||
@$(RM) ftpd.nds.xz ftpd*.3dsx.xz ftpd*.cia.xz ftpd*.nro.xz
|
||||
|
||||
nro:
|
||||
@$(MAKE) -f Makefile.switch all
|
||||
###########################################################################
|
||||
dslink:
|
||||
@$(MAKE) -f Makefile.nds dslink
|
||||
|
||||
release-nro:
|
||||
@$(MAKE) DEFINES=-DNDEBUG -f Makefile.switch all
|
||||
3dslink:
|
||||
@$(MAKE) -f Makefile.3ds 3dslink
|
||||
|
||||
3dslink-classic:
|
||||
@$(MAKE) -f Makefile.3ds 3dslink CLASSIC="-DCLASSIC"
|
||||
|
||||
nxlink:
|
||||
@$(MAKE) -f Makefile.switch nxlink
|
||||
|
||||
nxlink-classic:
|
||||
@$(MAKE) -f Makefile.switch nxlink CLASSIC="-DCLASSIC"
|
||||
|
||||
###########################################################################
|
||||
nds:
|
||||
@$(MAKE) -f Makefile.nds CLASSIC="-DCLASSIC"
|
||||
|
||||
3dsx:
|
||||
@$(MAKE) -f Makefile.3ds 3dsx
|
||||
|
||||
release-3dsx:
|
||||
@$(MAKE) DEFINES=-DNDEBUG -f Makefile.3ds 3dsx
|
||||
3dsx-classic:
|
||||
@$(MAKE) -f Makefile.3ds 3dsx CLASSIC="-DCLASSIC"
|
||||
|
||||
cia: 3dsx
|
||||
@$(MAKE) -f Makefile.3ds cia
|
||||
|
||||
release-cia: release-3dsx
|
||||
@$(MAKE) DEFINES=-NDEBUG -f Makefile.3ds cia
|
||||
cia-classic: 3dsx-classic
|
||||
@$(MAKE) -f Makefile.3ds cia CLASSIC="-DCLASSIC"
|
||||
|
||||
release-3ds:
|
||||
# can't let these run in parallel with each other due to using same
|
||||
# .elf file name
|
||||
@$(MAKE) DEFINES=-DNDEBUG -f Makefile.3ds 3dsx
|
||||
@$(MAKE) DEFINES=-DNDEBUG -f Makefile.3ds cia
|
||||
nro:
|
||||
@$(MAKE) -f Makefile.switch all
|
||||
|
||||
nro-classic:
|
||||
@$(MAKE) -f Makefile.switch all CLASSIC="-DCLASSIC"
|
||||
|
||||
linux:
|
||||
@$(MAKE) -f Makefile.linux
|
||||
|
||||
clean:
|
||||
@$(MAKE) -f Makefile.switch clean
|
||||
@$(MAKE) -f Makefile.3ds clean
|
||||
@$(MAKE) -f Makefile.linux clean
|
||||
@$(RM) ftpd.3dsx.xz ftpd.cia.xz ftpd.nro.xz
|
||||
###########################################################################
|
||||
release: release-nds \
|
||||
release-3dsx release-3dsx-classic \
|
||||
release-cia release-cia-classic \
|
||||
release-nro release-nro-classic
|
||||
@xz -c <nds/ftpd.nds >ftpd.nds.xz
|
||||
@xz -c <3ds/ftpd.3dsx >ftpd.3dsx.xz
|
||||
@xz -c <3ds-classic/ftpd-classic.3dsx >ftpd-classic.3dsx.xz
|
||||
@xz -c <3ds/ftpd.cia >ftpd.cia.xz
|
||||
@xz -c <3ds-classic/ftpd-classic.cia >ftpd-classic.cia.xz
|
||||
@xz -c <switch/ftpd.nro >ftpd.nro.xz
|
||||
@xz -c <switch-classic/ftpd-classic.nro >ftpd-classic.nro.xz
|
||||
|
||||
release-nds:
|
||||
@$(MAKE) -f Makefile.nds DEFINES=-DNDEBUG
|
||||
|
||||
release-3dsx:
|
||||
@$(MAKE) -f Makefile.3ds 3dsx DEFINES=-DNDEBUG
|
||||
|
||||
release-3dsx-classic:
|
||||
@$(MAKE) -f Makefile.3ds 3dsx DEFINES=-DNDEBUG CLASSIC="-DCLASSIC"
|
||||
|
||||
release-cia: release-3dsx
|
||||
@$(MAKE) -f Makefile.3ds cia DEFINES=-DNDEBUG
|
||||
|
||||
release-cia-classic: release-3dsx-classic
|
||||
@$(MAKE) -f Makefile.3ds cia DEFINES=-DNDEBUG CLASSIC="-DCLASSIC"
|
||||
|
||||
release-nro:
|
||||
@$(MAKE) -f Makefile.switch all DEFINES=-DNDEBUG
|
||||
|
||||
release-nro-classic:
|
||||
@$(MAKE) -f Makefile.switch all DEFINES=-DNDEBUG CLASSIC="-DCLASSIC"
|
||||
|
34
Makefile.3ds
34
Makefile.3ds
@ -31,16 +31,24 @@ include $(DEVKITARM)/3ds_rules
|
||||
# - icon.png
|
||||
# - <libctru folder>/default_icon.png
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := 3ds/$(notdir $(CURDIR))
|
||||
BUILD := 3ds/build
|
||||
SOURCES := source source/3ds source/imgui
|
||||
SOURCES := source source/3ds
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
GRAPHICS := 3ds/gfx
|
||||
ROMFS := 3ds/romfs
|
||||
GFXBUILD := $(ROMFS)
|
||||
|
||||
APP_TITLE := ftpd
|
||||
ifeq ($(strip $(CLASSIC)),)
|
||||
APP_TITLE := ftpd pro
|
||||
TARGET := 3ds/ftpd
|
||||
BUILD := 3ds/build
|
||||
SOURCES += source/imgui
|
||||
GRAPHICS := 3ds/gfx
|
||||
ROMFS := 3ds/romfs
|
||||
GFXBUILD := $(ROMFS)
|
||||
else
|
||||
APP_TITLE := ftpd classic
|
||||
TARGET := 3ds-classic/ftpd-classic
|
||||
BUILD := 3ds-classic/build
|
||||
endif
|
||||
|
||||
APP_DESCRIPTION := v$(VERSION)
|
||||
APP_AUTHOR := mtheall
|
||||
|
||||
@ -52,16 +60,17 @@ RSF_FILE := meta/ftpd-cia.rsf
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
OPTIMIZE := -O2
|
||||
OPTIMIZE := -O0
|
||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||
|
||||
CFLAGS := -g -Wall $(OPTIMIZE) -mword-relocations \
|
||||
-fomit-frame-pointer -ffunction-sections -fdata-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
$(ARCH) $(DEFINES) $(CLASSIC)
|
||||
|
||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS \
|
||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1
|
||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||
-DNO_IPV6
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||
|
||||
@ -96,11 +105,14 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
|
||||
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
|
||||
GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
ifeq ($(strip $(CLASSIC)),)
|
||||
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
|
@ -2,8 +2,9 @@ TARGET := linux/ftpd
|
||||
BUILD := linux/build
|
||||
|
||||
CFILES := $(wildcard source/linux/*.c)
|
||||
OFILES := $(patsubst source/%,$(BUILD)/%,$(CFILES:.c=.c.o))
|
||||
CXXFILES := $(wildcard source/*.cpp source/imgui/*.cpp source/linux/*.cpp)
|
||||
|
||||
OFILES := $(patsubst source/%,$(BUILD)/%,$(CFILES:.c=.c.o))
|
||||
OXXFILES := $(patsubst source/%,$(BUILD)/%,$(CXXFILES:.cpp=.cpp.o))
|
||||
|
||||
CPPFLAGS := -g -Wall -pthread -Iinclude -Isource/linux \
|
||||
|
219
Makefile.nds
Normal file
219
Makefile.nds
Normal file
@ -0,0 +1,219 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/ds_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
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
# DATA is a list of directories containing binary files embedded using bin2o
|
||||
# GRAPHICS is a list of directories containing image files to be converted with grit
|
||||
# AUDIO is a list of directories containing audio to be converted by maxmod
|
||||
# ICON is the image used to create the game icon, leave blank to use default rule
|
||||
# NITRO is a directory that will be accessible via NitroFS
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := nds/ftpd
|
||||
BUILD := nds/build
|
||||
SOURCES := source source/nds
|
||||
INCLUDES := include
|
||||
DATA :=
|
||||
GRAPHICS :=
|
||||
AUDIO :=
|
||||
ICON :=
|
||||
|
||||
# specify a directory which contains the nitro filesystem
|
||||
# this is relative to the Makefile
|
||||
NITRO :=
|
||||
|
||||
# These set the information text in the nds file
|
||||
GAME_TITLE := ftpd classic
|
||||
GAME_SUBTITLE1 := v$(VERSION)
|
||||
GAME_SUBTITLE2 := (c) mtheall
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -marm -mthumb-interwork -march=armv5te -mtune=arm946e-s
|
||||
|
||||
CFLAGS := -g -Wall -Os \
|
||||
$(ARCH) $(INCLUDE) -DARM9 -DNDS \
|
||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||
-DIMGUI_DISABLE_INCLUDE_IMCONFIG_H=1 \
|
||||
-DNO_IPV6 -DCLASSIC
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project (order is important)
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -lfat -ldswifi9 -lnds9
|
||||
|
||||
# automatigically add libraries for NitroFS
|
||||
ifneq ($(strip $(NITRO)),)
|
||||
LIBS := -lfilesystem -lfat $(LIBS)
|
||||
endif
|
||||
# automagically add maxmod library
|
||||
ifneq ($(strip $(AUDIO)),)
|
||||
LIBS := -lmm9 $(LIBS)
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(LIBNDS) $(PORTLIBS)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(TOPDIR)/$(BUILD),$(CURDIR))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(CURDIR)/$(subst /,,$(dir $(ICON)))\
|
||||
$(foreach dir,$(SOURCES),$(CURDIR)/$(dir))\
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))\
|
||||
$(foreach dir,$(GRAPHICS),$(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)))
|
||||
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
# prepare NitroFS directory
|
||||
ifneq ($(strip $(NITRO)),)
|
||||
export NITRO_FILES := $(CURDIR)/$(NITRO)
|
||||
endif
|
||||
|
||||
# get audio list for maxmod
|
||||
ifneq ($(strip $(AUDIO)),)
|
||||
export MODFILES := $(foreach dir,$(notdir $(wildcard $(AUDIO)/*.*)),$(CURDIR)/$(AUDIO)/$(dir))
|
||||
|
||||
# place the soundbank file in NitroFS if using it
|
||||
ifneq ($(strip $(NITRO)),)
|
||||
export SOUNDBANK := $(NITRO_FILES)/soundbank.bin
|
||||
|
||||
# otherwise, needs to be loaded from memory
|
||||
else
|
||||
export SOUNDBANK := soundbank.bin
|
||||
BINFILES += $(SOUNDBANK)
|
||||
endif
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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))\
|
||||
$(PNGFILES:.png=.o)\
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir))\
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include)\
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
ifeq ($(strip $(ICON)),)
|
||||
icons := $(wildcard *.bmp)
|
||||
|
||||
ifneq (,$(findstring $(TARGET).bmp,$(icons)))
|
||||
export GAME_ICON := $(CURDIR)/$(TARGET).bmp
|
||||
else
|
||||
ifneq (,$(findstring icon.bmp,$(icons)))
|
||||
export GAME_ICON := $(CURDIR)/icon.bmp
|
||||
endif
|
||||
endif
|
||||
else
|
||||
ifeq ($(suffix $(ICON)), .grf)
|
||||
export GAME_ICON := $(CURDIR)/$(ICON)
|
||||
else
|
||||
export GAME_ICON := $(CURDIR)/$(BUILD)/$(notdir $(basename $(ICON))).grf
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean dslink
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD):
|
||||
@mkdir -p $@
|
||||
@$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile.nds
|
||||
|
||||
dslink: $(BUILD)
|
||||
@dslink $(OUTPUT).nds
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds $(SOUNDBANK)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT).nds: $(OUTPUT).elf $(GAME_ICON)
|
||||
$(OUTPUT).elf: $(OFILES)
|
||||
|
||||
# need to build soundbank first
|
||||
$(OFILES): $(SOUNDBANK)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# rule to build solution from music files
|
||||
#---------------------------------------------------------------------------------
|
||||
$(SOUNDBANK) : $(MODFILES)
|
||||
#---------------------------------------------------------------------------------
|
||||
mmutil $^ -d -o$@ -hsoundbank.h
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o: %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# This rule creates assembly source files using grit
|
||||
# grit takes an image file and a .grit describing how the file is to be processed
|
||||
# add additional rules like this for each image extension
|
||||
# you use in the graphics folders
|
||||
#---------------------------------------------------------------------------------
|
||||
%.s %.h: %.png %.grit
|
||||
#---------------------------------------------------------------------------------
|
||||
grit $< -fts -o$*
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# Convert non-GRF game icon to GRF if needed
|
||||
#---------------------------------------------------------------------------------
|
||||
$(GAME_ICON): $(notdir $(ICON))
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo convert $(notdir $<)
|
||||
@grit $< -g -gt -gB4 -gT FF00FF -m! -p -pe 16 -fh! -ftr
|
||||
|
||||
-include $(DEPSDIR)/*.d
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
@ -37,18 +37,26 @@ include $(DEVKITPRO)/libnx/switch_rules
|
||||
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||
# NACP building is skipped as well.
|
||||
#---------------------------------------------------------------------------------
|
||||
APP_TITLE := ftpd snap! $(VERSION)
|
||||
APP_AUTHOR := mtheall, TuxSH, WinterMute
|
||||
ICON := meta/ftpd.jpg
|
||||
APP_VERSION := $(VERSION)
|
||||
|
||||
TARGET := switch/$(notdir $(CURDIR))
|
||||
BUILD := switch/build
|
||||
SOURCES := source source/imgui source/switch
|
||||
SOURCES := source source/switch
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
|
||||
ifeq ($(strip $(CLASSIC)),)
|
||||
APP_TITLE := ftpd pro $(VERSION)
|
||||
TARGET := switch/ftpd
|
||||
BUILD := switch/build
|
||||
SOURCES += source/imgui
|
||||
GRAPHICS := switch/gfx
|
||||
ROMFS := switch/romfs
|
||||
else
|
||||
APP_TITLE := ftpd classic $(VERSION)
|
||||
TARGET := switch-classic/ftpd-classic
|
||||
BUILD := switch-classic/build
|
||||
endif
|
||||
|
||||
# Output folders for autogenerated files in romfs
|
||||
OUT_SHADERS := shaders
|
||||
@ -61,7 +69,7 @@ ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -Wno-narrowing $(OPTIMIZE) \
|
||||
-ffunction-sections -fdata-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
$(ARCH) $(DEFINES) $(CLASSIC)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__ \
|
||||
-DSTATUS_STRING="\"ftpd v$(VERSION)\"" \
|
||||
|
26
README.md
26
README.md
@ -9,6 +9,7 @@ FTP Server for 3DS/Switch/Linux.
|
||||
- Cutting-edge graphics.
|
||||
|
||||
## Latest Builds
|
||||
NDS: https://mtheall.com/~mtheall/ftpd.nds
|
||||
|
||||
CIA: https://mtheall.com/~mtheall/ftpd.cia
|
||||
|
||||
@ -18,12 +19,35 @@ NRO: https://mtheall.com/~mtheall/ftpd.nro
|
||||
|
||||
CIA QR Code
|
||||
|
||||
![ftpd.cia](https://github.com/mtheall/ftpd/raw/master/ftpd_qr.png)
|
||||
![ftpd.cia](https://github.com/mtheall/ftpd/raw/feature/v3.0.0/ftpd-qr.png)
|
||||
|
||||
## Classic Builds
|
||||
|
||||
CIA: https://mtheall.com/~mtheall/ftpd-classic.cia
|
||||
|
||||
3DSX: https://mtheall.com/~mtheall/ftpd-classic.3dsx
|
||||
|
||||
NRO: https://mtheall.com/~mtheall/ftpd-classic.nro
|
||||
|
||||
CIA QR Code
|
||||
|
||||
![ftpd-classic.cia](https://github.com/mtheall/ftpd/raw/feature/v3.0.0/ftpd-classic-qr.png)
|
||||
|
||||
## Build and install
|
||||
|
||||
You must set up the [development environment](https://devkitpro.org/wiki/Getting_Started).
|
||||
|
||||
### NDS
|
||||
|
||||
The following pacman packages are required to build `nds/ftpd.nds`:
|
||||
|
||||
devkitARM
|
||||
dswifi
|
||||
libfat-nds
|
||||
libnds
|
||||
|
||||
They are available as part of the `nds-dev` meta-package.
|
||||
|
||||
### 3DSX
|
||||
|
||||
The following pacman packages are required to build `3ds/ftpd.3dsx`:
|
||||
|
BIN
ftpd-classic-qr.png
Normal file
BIN
ftpd-classic-qr.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
ftpd-qr.png
Normal file
BIN
ftpd-qr.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
ftpd_qr.png
BIN
ftpd_qr.png
Binary file not shown.
Before Width: | Height: | Size: 621 B |
@ -69,11 +69,13 @@ private:
|
||||
/// \brief Thread entry point
|
||||
void threadFunc ();
|
||||
|
||||
#ifndef NDS
|
||||
/// \brief Thread
|
||||
platform::Thread m_thread;
|
||||
|
||||
/// \brief Mutex
|
||||
platform::Mutex m_lock;
|
||||
#endif
|
||||
|
||||
/// \brief Listen socket
|
||||
UniqueSocket m_socket;
|
||||
|
@ -58,16 +58,30 @@ private:
|
||||
/// \brief Command buffer size
|
||||
constexpr static auto COMMAND_BUFFERSIZE = 4096;
|
||||
|
||||
#ifdef NDS
|
||||
/// \brief Response buffer size
|
||||
constexpr static auto RESPONSE_BUFFERSIZE = 4096;
|
||||
|
||||
/// \brief Transfer buffersize
|
||||
constexpr static auto XFER_BUFFERSIZE = 8192;
|
||||
#else
|
||||
/// \brief Response buffer size
|
||||
constexpr static auto RESPONSE_BUFFERSIZE = 32768;
|
||||
|
||||
/// \brief Transfer buffersize
|
||||
constexpr static auto XFER_BUFFERSIZE = 65536;
|
||||
#endif
|
||||
|
||||
/// \brief File buffersize
|
||||
constexpr static auto FILE_BUFFERSIZE = 4 * XFER_BUFFERSIZE;
|
||||
|
||||
#ifdef _3DS
|
||||
#if defined(NDS)
|
||||
/// \brief Socket buffer size
|
||||
constexpr static auto SOCK_BUFFERSIZE = 4096;
|
||||
|
||||
/// \brief Amount of file position history to keep
|
||||
constexpr static auto POSITION_HISTORY = 60;
|
||||
#elif defined(_3DS)
|
||||
/// \brief Socket buffer size
|
||||
constexpr static auto SOCK_BUFFERSIZE = 32768;
|
||||
|
||||
@ -184,8 +198,10 @@ private:
|
||||
/// \brief Transfer upload
|
||||
bool storeTransfer ();
|
||||
|
||||
#ifndef NDS
|
||||
/// \brief Mutex
|
||||
platform::Mutex m_lock;
|
||||
#endif
|
||||
|
||||
/// \brief Command socket
|
||||
SharedSocket m_commandSocket;
|
||||
|
@ -20,8 +20,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _3DS
|
||||
#if defined(NDS)
|
||||
#include <nds.h>
|
||||
#elif defined(_3DS)
|
||||
#include <3ds.h>
|
||||
#elif defined(__SWITCH__)
|
||||
#include <switch.h>
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
@ -29,6 +33,12 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#ifdef CLASSIC
|
||||
extern PrintConsole g_statusConsole;
|
||||
extern PrintConsole g_logConsole;
|
||||
extern PrintConsole g_sessionConsole;
|
||||
#endif
|
||||
|
||||
namespace platform
|
||||
{
|
||||
/// \brief Initialize platform
|
||||
@ -66,16 +76,14 @@ struct steady_clock
|
||||
constexpr static bool is_steady = true;
|
||||
|
||||
/// \brief Current timestamp
|
||||
static time_point now () noexcept
|
||||
{
|
||||
return time_point (duration (svcGetSystemTick ()));
|
||||
}
|
||||
static time_point now () noexcept;
|
||||
};
|
||||
#else
|
||||
/// \brief Steady clock
|
||||
using steady_clock = std::chrono::steady_clock;
|
||||
#endif
|
||||
|
||||
#ifndef NDS
|
||||
/// \brief Platform thread
|
||||
class Thread
|
||||
{
|
||||
@ -132,4 +140,5 @@ private:
|
||||
/// \brief pimpl
|
||||
std::unique_ptr<privateData_t> m_d;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
@ -25,6 +25,14 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef NDS
|
||||
struct sockaddr_storage
|
||||
{
|
||||
unsigned short ss_family;
|
||||
char ss_data[sizeof (struct sockaddr_in) - sizeof (unsigned short)];
|
||||
};
|
||||
#endif
|
||||
|
||||
/// \brief Socket address
|
||||
class SockAddr
|
||||
{
|
||||
|
@ -26,6 +26,28 @@
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#ifdef NDS
|
||||
struct pollfd
|
||||
{
|
||||
int fd;
|
||||
int events;
|
||||
int revents;
|
||||
};
|
||||
|
||||
using socklen_t = int;
|
||||
using nfds_t = unsigned int;
|
||||
|
||||
extern "C" int poll (struct pollfd *fds_, nfds_t nfds_, int timeout_);
|
||||
|
||||
#define POLLIN (1 << 0)
|
||||
#define POLLPRI (1 << 1)
|
||||
#define POLLOUT (1 << 2)
|
||||
#define POLLERR (1 << 3)
|
||||
#define POLLHUP (1 << 4)
|
||||
#else
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
class Socket;
|
||||
using UniqueSocket = std::unique_ptr<Socket>;
|
||||
using SharedSocket = std::shared_ptr<Socket>;
|
||||
|
@ -18,6 +18,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef CLASSIC
|
||||
#include "imgui_citro3d.h"
|
||||
|
||||
#include <citro3d.h>
|
||||
@ -692,3 +693,4 @@ void imgui::citro3d::render (C3D_RenderTarget *const top_, C3D_RenderTarget *con
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSIC
|
||||
#include <citro3d.h>
|
||||
|
||||
namespace imgui
|
||||
@ -35,3 +36,4 @@ void exit ();
|
||||
void render (C3D_RenderTarget *top_, C3D_RenderTarget *bottom_);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -18,6 +18,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef CLASSIC
|
||||
#include "imgui_ctru.h"
|
||||
|
||||
#include "imgui.h"
|
||||
@ -25,8 +26,6 @@
|
||||
#include "fs.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
@ -174,3 +173,4 @@ void imgui::ctru::newFrame ()
|
||||
updateTouch (io);
|
||||
updateGamepads (io);
|
||||
}
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSIC
|
||||
namespace imgui
|
||||
{
|
||||
namespace ctru
|
||||
@ -31,3 +32,4 @@ bool init ();
|
||||
void newFrame ();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -28,20 +28,29 @@
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include <3ds.h>
|
||||
#include <citro3d.h>
|
||||
#include <tex3ds.h>
|
||||
|
||||
#ifndef CLASSIC
|
||||
#include "gfx.h"
|
||||
#endif
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef CLASSIC
|
||||
PrintConsole g_statusConsole;
|
||||
PrintConsole g_logConsole;
|
||||
PrintConsole g_sessionConsole;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
/// \brief Thread stack size
|
||||
@ -60,6 +69,9 @@ u32 *s_socuBuffer = nullptr;
|
||||
/// \brief ac:u fence
|
||||
platform::Mutex s_acuFence;
|
||||
|
||||
#ifdef CLASSIC
|
||||
in_addr_t s_addr = 0;
|
||||
#else
|
||||
/// \brief Clear color
|
||||
constexpr auto CLEAR_COLOR = 0x204B7AFF;
|
||||
|
||||
@ -103,6 +115,7 @@ C3D_RenderTarget *s_bottom = nullptr;
|
||||
C3D_Tex s_gfxTexture;
|
||||
/// \brief Texture atlas metadata
|
||||
Tex3DS_Texture s_gfxT3x;
|
||||
#endif
|
||||
|
||||
/// \brief Get network visibility
|
||||
bool getNetworkVisibility ()
|
||||
@ -113,7 +126,20 @@ bool getNetworkVisibility ()
|
||||
// get wifi status
|
||||
std::uint32_t wifi = 0;
|
||||
if (R_FAILED (ACU_GetWifiStatus (&wifi)) || !wifi)
|
||||
{
|
||||
#ifdef CLASSIC
|
||||
s_addr = 0;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CLASSIC
|
||||
if (!s_addr)
|
||||
s_addr = gethostid ();
|
||||
|
||||
if (s_addr == INADDR_BROADCAST)
|
||||
s_addr = 0;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -146,6 +172,7 @@ void startNetwork ()
|
||||
/// \brief Draw citro3d logo
|
||||
void drawLogo ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
// get citro3d logo subtexture
|
||||
auto subTex = Tex3DS_GetSubTexture (s_gfxT3x, gfx_c3dlogo_idx);
|
||||
|
||||
@ -176,11 +203,13 @@ void drawLogo ()
|
||||
ImVec2 (x2, y2 + screenHeight * 0.5f),
|
||||
uv1,
|
||||
uv2);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Draw status
|
||||
void drawStatus ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
constexpr unsigned batteryLevels[] = {
|
||||
gfx_battery0_idx,
|
||||
gfx_battery0_idx,
|
||||
@ -250,13 +279,37 @@ void drawStatus ()
|
||||
// draw wifi icon
|
||||
ImGui::GetForegroundDrawList ()->AddImage (
|
||||
&s_gfxTexture, p3, p4, uv3, uv4, ImGui::GetColorU32 (ImGuiCol_Text));
|
||||
#endif
|
||||
|
||||
// draw current timestamp
|
||||
char buffer[64];
|
||||
char timeBuffer[16];
|
||||
auto const now = std::time (nullptr);
|
||||
std::strftime (buffer, sizeof (buffer), "%H:%M:%S", std::localtime (&now));
|
||||
ImGui::GetForegroundDrawList ()->AddText (
|
||||
ImVec2 (p3.x - 65.0f, style.FramePadding.y), ImGui::GetColorU32 (ImGuiCol_Text), buffer);
|
||||
std::strftime (timeBuffer, sizeof (timeBuffer), "%H:%M:%S", std::localtime (&now));
|
||||
|
||||
#ifdef CLASSIC
|
||||
static std::string statusString;
|
||||
|
||||
std::string newStatusString (256, '\0');
|
||||
newStatusString.resize (std::sprintf (&newStatusString[0],
|
||||
"\x1b[0;0H\x1b[32;1m%s \x1b[36;1m%s%s \x1b[37;1m%s\x1b[K",
|
||||
STATUS_STRING,
|
||||
s_addr ? inet_ntoa (in_addr{s_addr}) : "Waiting",
|
||||
s_addr ? ":5000" : "",
|
||||
timeBuffer));
|
||||
|
||||
if (newStatusString != statusString)
|
||||
{
|
||||
statusString = std::move (newStatusString);
|
||||
|
||||
consoleSelect (&g_statusConsole);
|
||||
std::fputs (statusString.c_str (), stdout);
|
||||
std::fflush (stdout);
|
||||
}
|
||||
#else
|
||||
ImGui::GetForegroundDrawList ()->AddText (ImVec2 (p3.x - 65.0f, style.FramePadding.y),
|
||||
ImGui::GetColorU32 (ImGuiCol_Text),
|
||||
timeBuffer);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,16 +320,28 @@ bool platform::init ()
|
||||
|
||||
acInit ();
|
||||
ptmuInit ();
|
||||
#ifndef CLASSIC
|
||||
romfsInit ();
|
||||
#endif
|
||||
gfxInitDefault ();
|
||||
gfxSet3D (false);
|
||||
sdmcWriteSafe (false);
|
||||
|
||||
#ifdef CLASSIC
|
||||
consoleInit (GFX_TOP, &g_statusConsole);
|
||||
consoleInit (GFX_TOP, &g_logConsole);
|
||||
consoleInit (GFX_BOTTOM, &g_sessionConsole);
|
||||
|
||||
consoleSetWindow (&g_statusConsole, 0, 0, 50, 1);
|
||||
consoleSetWindow (&g_logConsole, 0, 1, 50, 29);
|
||||
consoleSetWindow (&g_sessionConsole, 0, 0, 40, 30);
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
consoleDebugInit (debugDevice_SVC);
|
||||
std::setvbuf (stderr, nullptr, _IOLBF, 0);
|
||||
#endif
|
||||
|
||||
#ifndef CLASSIC
|
||||
// initialize citro3d
|
||||
C3D_Init (C3D_DEFAULT_CMDBUF_SIZE);
|
||||
|
||||
@ -317,6 +382,7 @@ bool platform::init ()
|
||||
// citro3d logo doesn't quite show with the default transparency
|
||||
style.Colors[ImGuiCol_WindowBg].w = 0.8f;
|
||||
style.ScaleAllSizes (0.5f);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -342,6 +408,7 @@ bool platform::loop ()
|
||||
if (hidKeysDown () & KEY_START)
|
||||
return false;
|
||||
|
||||
#ifndef CLASSIC
|
||||
auto &io = ImGui::GetIO ();
|
||||
|
||||
// setup display metrics
|
||||
@ -350,6 +417,7 @@ bool platform::loop ()
|
||||
|
||||
imgui::ctru::newFrame ();
|
||||
ImGui::NewFrame ();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -359,6 +427,12 @@ void platform::render ()
|
||||
drawLogo ();
|
||||
drawStatus ();
|
||||
|
||||
#ifdef CLASSIC
|
||||
drawLog ();
|
||||
gfxFlushBuffers ();
|
||||
gspWaitForVBlank ();
|
||||
gfxSwapBuffers ();
|
||||
#else
|
||||
ImGui::Render ();
|
||||
|
||||
C3D_FrameBegin (C3D_FRAME_SYNCDRAW);
|
||||
@ -370,10 +444,12 @@ void platform::render ()
|
||||
imgui::citro3d::render (s_top, s_bottom);
|
||||
|
||||
C3D_FrameEnd (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void platform::exit ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
imgui::citro3d::exit ();
|
||||
|
||||
// free graphics
|
||||
@ -386,6 +462,7 @@ void platform::exit ()
|
||||
|
||||
// deinitialize citro3d
|
||||
C3D_Fini ();
|
||||
#endif
|
||||
|
||||
if (s_socuActive)
|
||||
socExit ();
|
||||
@ -393,11 +470,19 @@ void platform::exit ()
|
||||
std::free (s_socuBuffer);
|
||||
|
||||
gfxExit ();
|
||||
#ifndef CLASSIC
|
||||
romfsExit ();
|
||||
#endif
|
||||
ptmuExit ();
|
||||
acExit ();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
platform::steady_clock::time_point platform::steady_clock::now () noexcept
|
||||
{
|
||||
return time_point (duration (svcGetSystemTick ()));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Platform thread pimpl
|
||||
class platform::Thread::privateData_t
|
||||
|
@ -23,11 +23,15 @@
|
||||
#include "fs.h"
|
||||
#include "log.h"
|
||||
#include "platform.h"
|
||||
#include "socket.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#ifdef NDS
|
||||
#include <dswifi9.h>
|
||||
#endif
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
@ -37,20 +41,26 @@
|
||||
#include <thread>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#ifdef NDS
|
||||
#define LOCKED(x) x
|
||||
#else
|
||||
#define LOCKED(x) \
|
||||
do \
|
||||
{ \
|
||||
auto const lock = std::scoped_lock (m_lock); \
|
||||
x; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
/// \brief Application start time
|
||||
auto const s_startTime = std::time (nullptr);
|
||||
|
||||
#ifndef NDS
|
||||
/// \brief Mutex for s_freeSpace
|
||||
platform::Mutex s_lock;
|
||||
#endif
|
||||
|
||||
/// \brief Free space string
|
||||
std::string s_freeSpace;
|
||||
@ -61,16 +71,54 @@ FtpServer::~FtpServer ()
|
||||
{
|
||||
m_quit = true;
|
||||
|
||||
#ifndef NDS
|
||||
m_thread.join ();
|
||||
#endif
|
||||
}
|
||||
|
||||
FtpServer::FtpServer (std::uint16_t const port_) : m_port (port_), m_quit (false)
|
||||
{
|
||||
#ifndef NDS
|
||||
m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
void FtpServer::draw ()
|
||||
{
|
||||
#ifdef NDS
|
||||
loop ();
|
||||
#endif
|
||||
|
||||
#ifdef CLASSIC
|
||||
{
|
||||
#ifndef NDS
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
if (!s_freeSpace.empty ())
|
||||
{
|
||||
consoleSelect (&g_statusConsole);
|
||||
std::printf ("\x1b[0;%uH\x1b[32;1m%s",
|
||||
static_cast<unsigned> (g_statusConsole.windowWidth - s_freeSpace.size () + 1),
|
||||
s_freeSpace.c_str ());
|
||||
std::fflush (stdout);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
#ifndef NDS
|
||||
auto lock = std::scoped_lock (m_lock);
|
||||
#endif
|
||||
consoleSelect (&g_sessionConsole);
|
||||
std::fputs ("\x1b[2J", stdout);
|
||||
for (auto &session : m_sessions)
|
||||
{
|
||||
session->draw ();
|
||||
if (&session != &m_sessions.back ())
|
||||
std::fputc ('\n', stdout);
|
||||
std::fflush (stdout);
|
||||
}
|
||||
}
|
||||
#else
|
||||
auto const &io = ImGui::GetIO ();
|
||||
auto const width = io.DisplaySize.x;
|
||||
auto const height = io.DisplaySize.y;
|
||||
@ -135,6 +183,7 @@ void FtpServer::draw ()
|
||||
}
|
||||
|
||||
ImGui::End ();
|
||||
#endif
|
||||
}
|
||||
|
||||
UniqueFtpServer FtpServer::create (std::uint16_t const port_)
|
||||
@ -150,8 +199,11 @@ void FtpServer::updateFreeSpace ()
|
||||
if (::statvfs ("sdmc:/", &st) != 0)
|
||||
return;
|
||||
|
||||
auto freeSpace = fs::printSize (static_cast<std::uint64_t> (st.f_bsize) * st.f_bfree);
|
||||
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
s_freeSpace = fs::printSize (static_cast<std::uint64_t> (st.f_bsize) * st.f_bfree);
|
||||
if (freeSpace != s_freeSpace)
|
||||
s_freeSpace = std::move (freeSpace);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -164,7 +216,9 @@ void FtpServer::handleNetworkFound ()
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
#if defined(_3DS) || defined(__SWITCH__)
|
||||
#if defined(NDS)
|
||||
addr.sin_addr = Wifi_GetIPInfo (nullptr, nullptr, nullptr, nullptr);
|
||||
#elif defined(_3DS) || defined(__SWITCH__)
|
||||
addr.sin_addr.s_addr = gethostid ();
|
||||
#else
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
@ -234,8 +288,10 @@ void FtpServer::loop ()
|
||||
std::vector<UniqueFtpSession> deadSessions;
|
||||
{
|
||||
// remove dead sessions
|
||||
#ifndef NDS
|
||||
auto lock = std::scoped_lock (m_lock);
|
||||
auto it = std::begin (m_sessions);
|
||||
#endif
|
||||
auto it = std::begin (m_sessions);
|
||||
while (it != std::end (m_sessions))
|
||||
{
|
||||
auto &session = *it;
|
||||
@ -256,9 +312,11 @@ void FtpServer::loop ()
|
||||
if (!FtpSession::poll (m_sessions))
|
||||
handleNetworkLost ();
|
||||
}
|
||||
#ifndef NDS
|
||||
// avoid busy polling in background thread
|
||||
else
|
||||
platform::Thread::sleep (16ms);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FtpServer::threadFunc ()
|
||||
|
@ -21,21 +21,12 @@
|
||||
#include "ftpSession.h"
|
||||
|
||||
#include "ftpServer.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#ifdef _3DS
|
||||
#include <3ds.h>
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#endif
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <poll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -49,16 +40,20 @@
|
||||
#include <mutex>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#if defined(_3DS) || defined(__SWITCH__)
|
||||
#if defined(NDS) || defined(_3DS) || defined(__SWITCH__)
|
||||
#define lstat stat
|
||||
#endif
|
||||
|
||||
#ifdef NDS
|
||||
#define LOCKED(x) x
|
||||
#else
|
||||
#define LOCKED(x) \
|
||||
do \
|
||||
{ \
|
||||
auto const lock = std::scoped_lock (m_lock); \
|
||||
x; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -297,7 +292,9 @@ FtpSession::FtpSession (UniqueSocket commandSocket_)
|
||||
|
||||
bool FtpSession::dead ()
|
||||
{
|
||||
#ifndef NDS
|
||||
auto const lock = std::scoped_lock (m_lock);
|
||||
#endif
|
||||
if (m_commandSocket || m_pasvSocket || m_dataSocket)
|
||||
return false;
|
||||
|
||||
@ -306,8 +303,19 @@ bool FtpSession::dead ()
|
||||
|
||||
void FtpSession::draw ()
|
||||
{
|
||||
#ifndef NDS
|
||||
auto const lock = std::scoped_lock (m_lock);
|
||||
#endif
|
||||
|
||||
#ifdef CLASSIC
|
||||
if (m_filePosition)
|
||||
{
|
||||
std::fputs (fs::printSize (m_filePosition).c_str (), stdout);
|
||||
std::fputc (' ', stdout);
|
||||
}
|
||||
|
||||
std::fputs (m_workItem.empty () ? m_cwd.c_str () : m_workItem.c_str (), stdout);
|
||||
#else
|
||||
#ifdef _3DS
|
||||
ImGui::BeginChild (m_windowName.c_str (), ImVec2 (0.0f, 45.0f), true);
|
||||
#else
|
||||
@ -362,6 +370,7 @@ void FtpSession::draw ()
|
||||
}
|
||||
|
||||
ImGui::EndChild ();
|
||||
#endif
|
||||
}
|
||||
|
||||
UniqueFtpSession FtpSession::create (UniqueSocket commandSocket_)
|
||||
@ -571,7 +580,9 @@ void FtpSession::setState (State const state_, bool const closePasv_, bool const
|
||||
if (state_ == State::COMMAND)
|
||||
{
|
||||
{
|
||||
#ifndef NDS
|
||||
auto lock = std::scoped_lock (m_lock);
|
||||
#endif
|
||||
|
||||
m_restartPosition = 0;
|
||||
m_fileSize = 0;
|
||||
@ -1294,6 +1305,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
|
||||
|
||||
void FtpSession::readCommand (int const events_)
|
||||
{
|
||||
#ifndef NDS
|
||||
// check out-of-band data
|
||||
if (events_ & POLLPRI)
|
||||
{
|
||||
@ -1333,6 +1345,7 @@ void FtpSession::readCommand (int const events_)
|
||||
m_commandBuffer.clear ();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (events_ & POLLIN)
|
||||
{
|
||||
@ -1591,9 +1604,9 @@ bool FtpSession::listTransfer ()
|
||||
auto const dp = static_cast<DIR *> (m_dir);
|
||||
auto const magic = *reinterpret_cast<u32 *> (dp->dirData->dirStruct);
|
||||
|
||||
if (magic == SDMC_DIRITER_MAGIC)
|
||||
if (magic == ARCHIVE_DIRITER_MAGIC)
|
||||
{
|
||||
auto const dir = reinterpret_cast<sdmc_dir_t const *> (dp->dirData->dirStruct);
|
||||
auto const dir = reinterpret_cast<archive_dir_t const *> (dp->dirData->dirStruct);
|
||||
auto const entry = &dir->entry_data[dir->index];
|
||||
|
||||
if (entry->attributes & FS_ATTRIBUTE_DIRECTORY)
|
||||
@ -1619,7 +1632,7 @@ bool FtpSession::listTransfer ()
|
||||
if (getmtime)
|
||||
{
|
||||
std::uint64_t mtime = 0;
|
||||
auto const rc = sdmc_getmtime (fullPath.c_str (), &mtime);
|
||||
auto const rc = archive_getmtime (fullPath.c_str (), &mtime);
|
||||
if (rc != 0)
|
||||
error ("sdmc_getmtime %s 0x%lx\n", fullPath.c_str (), rc);
|
||||
else
|
||||
@ -2060,7 +2073,7 @@ void FtpSession::PASV (char const *args_)
|
||||
|
||||
// create an address to bind
|
||||
struct sockaddr_in addr = m_commandSocket->sockName ();
|
||||
#ifdef _3DS
|
||||
#if defined(NDS) || defined(_3DS)
|
||||
static std::uint16_t ephemeralPort = 5001;
|
||||
if (ephemeralPort > 10000)
|
||||
ephemeralPort = 5001;
|
||||
@ -2256,7 +2269,7 @@ void FtpSession::RMD (char const *args_)
|
||||
// remove the directory
|
||||
if (::rmdir (path.c_str ()) != 0)
|
||||
{
|
||||
sendResponse ("550 %s\r\n", std::strerror (errno));
|
||||
sendResponse ("550 %d %s\r\n", __LINE__, std::strerror (errno));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,10 @@ constexpr auto MAX_LOGS = 250;
|
||||
constexpr auto MAX_LOGS = 10000;
|
||||
#endif
|
||||
|
||||
#ifdef CLASSIC
|
||||
bool s_logUpdated = true;
|
||||
#endif
|
||||
|
||||
/// \brief Message prefix
|
||||
static char const *const s_prefix[] = {
|
||||
[DEBUG] = "[DEBUG]",
|
||||
@ -66,21 +70,62 @@ struct Message
|
||||
/// \brief Log messages
|
||||
std::vector<Message> s_messages;
|
||||
|
||||
#ifndef NDS
|
||||
/// \brief Log lock
|
||||
platform::Mutex s_lock;
|
||||
#endif
|
||||
}
|
||||
|
||||
void drawLog ()
|
||||
{
|
||||
#ifndef NDS
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
|
||||
if (s_messages.size () > MAX_LOGS)
|
||||
#ifdef CLASSIC
|
||||
if (!s_logUpdated)
|
||||
return;
|
||||
|
||||
s_logUpdated = false;
|
||||
#endif
|
||||
|
||||
auto const maxLogs =
|
||||
#ifdef CLASSIC
|
||||
g_logConsole.windowHeight;
|
||||
#else
|
||||
MAX_LOGS;
|
||||
#endif
|
||||
|
||||
if (s_messages.size () > static_cast<unsigned> (maxLogs))
|
||||
{
|
||||
auto const begin = std::begin (s_messages);
|
||||
auto const end = std::next (begin, s_messages.size () - MAX_LOGS);
|
||||
auto const end = std::next (begin, s_messages.size () - maxLogs);
|
||||
s_messages.erase (begin, end);
|
||||
}
|
||||
|
||||
#ifdef CLASSIC
|
||||
char const *const s_colors[] = {
|
||||
[DEBUG] = "\x1b[33;1m", // yellow
|
||||
[INFO] = "\x1b[37;1m", // white
|
||||
[ERROR] = "\x1b[31;1m", // red
|
||||
[COMMAND] = "\x1b[32;1m", // green
|
||||
[RESPONSE] = "\x1b[36;1m", // cyan
|
||||
};
|
||||
|
||||
auto it = std::begin (s_messages);
|
||||
if (s_messages.size () > static_cast<unsigned> (g_logConsole.windowHeight))
|
||||
it = std::next (it, s_messages.size () - g_logConsole.windowHeight);
|
||||
|
||||
consoleSelect (&g_logConsole);
|
||||
while (it != std::end (s_messages))
|
||||
{
|
||||
std::fputs (s_colors[it->level], stdout);
|
||||
std::fputs (it->message.c_str (), stdout);
|
||||
++it;
|
||||
}
|
||||
std::fflush (stdout);
|
||||
s_messages.clear ();
|
||||
#else
|
||||
ImVec4 const s_colors[] = {
|
||||
[DEBUG] = ImVec4 (1.0f, 1.0f, 0.4f, 1.0f), // yellow
|
||||
[INFO] = ImGui::GetStyleColorVec4 (ImGuiCol_Text), // normal
|
||||
@ -101,6 +146,7 @@ void drawLog ()
|
||||
// auto-scroll if scroll bar is at end
|
||||
if (ImGui::GetScrollY () >= ImGui::GetScrollMaxY ())
|
||||
ImGui::SetScrollHereY (1.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
void debug (char const *const fmt_, ...)
|
||||
@ -157,17 +203,25 @@ void addLog (LogLevel const level_, char const *const fmt_, va_list ap_)
|
||||
return;
|
||||
#endif
|
||||
|
||||
thread_local static char buffer[1024];
|
||||
#ifndef NDS
|
||||
thread_local
|
||||
#endif
|
||||
static char buffer[1024];
|
||||
|
||||
std::vsnprintf (buffer, sizeof (buffer), fmt_, ap_);
|
||||
buffer[sizeof (buffer) - 1] = '\0';
|
||||
|
||||
#ifndef NDS
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
std::fprintf (stderr, "%s", s_prefix[level_]);
|
||||
std::fputs (buffer, stderr);
|
||||
// std::fprintf (stderr, "%s", s_prefix[level_]);
|
||||
// std::fputs (buffer, stderr);
|
||||
#endif
|
||||
s_messages.emplace_back (level_, buffer);
|
||||
#ifdef CLASSIC
|
||||
s_logUpdated = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void addLog (LogLevel const level_, std::string_view const message_)
|
||||
@ -185,10 +239,15 @@ void addLog (LogLevel const level_, std::string_view const message_)
|
||||
c = '?';
|
||||
}
|
||||
|
||||
#ifndef NDS
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
std::fprintf (stderr, "%s", s_prefix[level_]);
|
||||
std::fwrite (msg.data (), 1, msg.size (), stderr);
|
||||
// std::fprintf (stderr, "%s", s_prefix[level_]);
|
||||
// std::fwrite (msg.data (), 1, msg.size (), stderr);
|
||||
#endif
|
||||
s_messages.emplace_back (level_, msg);
|
||||
#ifdef CLASSIC
|
||||
s_logUpdated = true;
|
||||
#endif
|
||||
}
|
||||
|
@ -29,19 +29,25 @@
|
||||
|
||||
int main (int argc_, char *argv_[])
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
IMGUI_CHECKVERSION ();
|
||||
ImGui::CreateContext ();
|
||||
#endif
|
||||
|
||||
if (!platform::init ())
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
ImGui::DestroyContext ();
|
||||
#endif
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#ifndef CLASSIC
|
||||
auto &style = ImGui::GetStyle ();
|
||||
|
||||
// turn off window rounding
|
||||
style.WindowRounding = 0.0f;
|
||||
#endif
|
||||
|
||||
auto server = FtpServer::create (5000);
|
||||
|
||||
@ -56,5 +62,8 @@ int main (int argc_, char *argv_[])
|
||||
server.reset ();
|
||||
|
||||
platform::exit ();
|
||||
|
||||
#ifndef CLASSIC
|
||||
ImGui::DestroyContext ();
|
||||
#endif
|
||||
}
|
||||
|
130
source/nds/platform.cpp
Normal file
130
source/nds/platform.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
// ftpd is a server implementation based on the following:
|
||||
// - RFC 959 (https://tools.ietf.org/html/rfc959)
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include <dswifi9.h>
|
||||
#include <fat.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifndef CLASSIC
|
||||
#error "NDS must be built in classic mode"
|
||||
#endif
|
||||
|
||||
PrintConsole g_statusConsole;
|
||||
PrintConsole g_logConsole;
|
||||
PrintConsole g_sessionConsole;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct in_addr s_addr = {0};
|
||||
}
|
||||
|
||||
bool platform::networkVisible ()
|
||||
{
|
||||
switch (Wifi_AssocStatus ())
|
||||
{
|
||||
case ASSOCSTATUS_DISCONNECTED:
|
||||
case ASSOCSTATUS_CANNOTCONNECT:
|
||||
s_addr.s_addr = 0;
|
||||
Wifi_AutoConnect ();
|
||||
break;
|
||||
|
||||
case ASSOCSTATUS_SEARCHING:
|
||||
case ASSOCSTATUS_AUTHENTICATING:
|
||||
case ASSOCSTATUS_ASSOCIATING:
|
||||
case ASSOCSTATUS_ACQUIRINGDHCP:
|
||||
s_addr.s_addr = 0;
|
||||
break;
|
||||
|
||||
case ASSOCSTATUS_ASSOCIATED:
|
||||
if (!s_addr.s_addr)
|
||||
s_addr = Wifi_GetIPInfo (nullptr, nullptr, nullptr, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool platform::init ()
|
||||
{
|
||||
sassert (fatInitDefault (), "Failed to initialize fat");
|
||||
|
||||
videoSetMode (MODE_0_2D);
|
||||
videoSetModeSub (MODE_0_2D);
|
||||
|
||||
vramSetBankA (VRAM_A_MAIN_BG);
|
||||
vramSetBankC (VRAM_C_SUB_BG);
|
||||
|
||||
consoleInit (&g_statusConsole, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 0, true, true);
|
||||
g_logConsole = g_statusConsole;
|
||||
consoleInit (&g_sessionConsole, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 0, false, true);
|
||||
|
||||
consoleSetWindow (&g_statusConsole, 0, 0, 32, 1);
|
||||
consoleSetWindow (&g_logConsole, 0, 1, 32, 23);
|
||||
consoleSetWindow (&g_sessionConsole, 0, 0, 32, 24);
|
||||
|
||||
consoleDebugInit (DebugDevice_NOCASH);
|
||||
std::setvbuf (stderr, nullptr, _IONBF, 0);
|
||||
|
||||
Wifi_InitDefault (INIT_ONLY);
|
||||
Wifi_AutoConnect ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool platform::loop ()
|
||||
{
|
||||
scanKeys ();
|
||||
|
||||
if (keysDown () & KEY_START)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void platform::render ()
|
||||
{
|
||||
swiWaitForVBlank ();
|
||||
consoleSelect (&g_statusConsole);
|
||||
std::printf ("\n%s %s%s",
|
||||
STATUS_STRING,
|
||||
s_addr.s_addr ? inet_ntoa (s_addr) : "Waiting on WiFi",
|
||||
s_addr.s_addr ? ":5000" : "");
|
||||
std::fflush (stdout);
|
||||
drawLog ();
|
||||
}
|
||||
|
||||
void platform::exit ()
|
||||
{
|
||||
info ("Press any key to exit\n");
|
||||
render ();
|
||||
|
||||
do
|
||||
{
|
||||
swiWaitForVBlank ();
|
||||
scanKeys ();
|
||||
} while (!keysDown ());
|
||||
}
|
@ -47,7 +47,7 @@ SockAddr::SockAddr (struct sockaddr const &addr_)
|
||||
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in));
|
||||
break;
|
||||
|
||||
#ifndef _3DS
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in6));
|
||||
break;
|
||||
@ -114,7 +114,7 @@ std::uint16_t SockAddr::port () const
|
||||
case AF_INET:
|
||||
return ntohs (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_port);
|
||||
|
||||
#ifndef _3DS
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
return ntohs (reinterpret_cast<struct sockaddr_in6 const *> (&m_addr)->sin6_port);
|
||||
#endif
|
||||
@ -130,12 +130,16 @@ char const *SockAddr::name (char *buffer_, std::size_t size_) const
|
||||
switch (m_addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
#ifdef NDS
|
||||
return inet_ntoa (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr);
|
||||
#else
|
||||
return inet_ntop (AF_INET,
|
||||
&reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr,
|
||||
buffer_,
|
||||
size_);
|
||||
#endif
|
||||
|
||||
#ifndef _3DS
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
return inet_ntop (AF_INET6,
|
||||
&reinterpret_cast<struct sockaddr_in6 const *> (&m_addr)->sin6_addr,
|
||||
@ -151,11 +155,15 @@ char const *SockAddr::name (char *buffer_, std::size_t size_) const
|
||||
|
||||
char const *SockAddr::name () const
|
||||
{
|
||||
#if defined(_3DS)
|
||||
#ifdef NDS
|
||||
return inet_ntoa (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr);
|
||||
#else
|
||||
#ifdef NO_IPV6
|
||||
thread_local static char buffer[INET_ADDRSTRLEN];
|
||||
#else
|
||||
thread_local static char buffer[INET6_ADDRSTRLEN];
|
||||
#endif
|
||||
|
||||
return name (buffer, sizeof (buffer));
|
||||
#endif
|
||||
}
|
||||
|
@ -26,8 +26,6 @@
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
@ -42,8 +40,13 @@ Socket::~Socket ()
|
||||
if (m_connected)
|
||||
info ("Closing connection to [%s]:%u\n", m_peerName.name (), m_peerName.port ());
|
||||
|
||||
#ifdef NDS
|
||||
if (::closesocket (m_fd) != 0)
|
||||
error ("closesocket: %s\n", std::strerror (errno));
|
||||
#else
|
||||
if (::close (m_fd) != 0)
|
||||
error ("close: %s\n", std::strerror (errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
Socket::Socket (int const fd_) : m_fd (fd_), m_listening (false), m_connected (false)
|
||||
@ -77,11 +80,17 @@ UniqueSocket Socket::accept ()
|
||||
|
||||
int Socket::atMark ()
|
||||
{
|
||||
#ifdef NDS
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#else
|
||||
auto const rc = ::sockatmark (m_fd);
|
||||
|
||||
if (rc < 0)
|
||||
error ("sockatmark: %s\n", std::strerror (errno));
|
||||
|
||||
return rc;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Socket::bind (SockAddr const &addr_)
|
||||
@ -96,7 +105,7 @@ bool Socket::bind (SockAddr const &addr_)
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef _3DS
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
if (::bind (m_fd, addr_, sizeof (struct sockaddr_in6)) != 0)
|
||||
{
|
||||
@ -171,11 +180,16 @@ bool Socket::shutdown (int const how_)
|
||||
|
||||
bool Socket::setLinger (bool const enable_, std::chrono::seconds const time_)
|
||||
{
|
||||
#ifdef NDS
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#else
|
||||
struct linger linger;
|
||||
linger.l_onoff = enable_;
|
||||
linger.l_linger = time_.count ();
|
||||
|
||||
if (::setsockopt (m_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger)) != 0)
|
||||
auto const rc = ::setsockopt (m_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger));
|
||||
if (rc != 0)
|
||||
{
|
||||
error ("setsockopt(SO_LINGER, %s, %lus): %s\n",
|
||||
enable_ ? "on" : "off",
|
||||
@ -185,10 +199,21 @@ bool Socket::setLinger (bool const enable_, std::chrono::seconds const time_)
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Socket::setNonBlocking (bool const nonBlocking_)
|
||||
{
|
||||
#ifdef NDS
|
||||
unsigned long enable = nonBlocking_;
|
||||
|
||||
auto const rc = ::ioctl (m_fd, FIONBIO, &enable);
|
||||
if (rc != 0)
|
||||
{
|
||||
error ("fcntl(FIONBIO, %d): %s\n", nonBlocking_, std::strerror (errno));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
auto flags = ::fcntl (m_fd, F_GETFL, 0);
|
||||
if (flags == -1)
|
||||
{
|
||||
@ -206,6 +231,7 @@ bool Socket::setNonBlocking (bool const nonBlocking_)
|
||||
error ("fcntl(F_SETFL, %d): %s\n", flags, std::strerror (errno));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -335,3 +361,61 @@ int Socket::poll (PollInfo *const info_,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef NDS
|
||||
extern "C" int poll (struct pollfd *const fds_, nfds_t const nfds_, int const timeout_)
|
||||
{
|
||||
fd_set readFds;
|
||||
fd_set writeFds;
|
||||
fd_set exceptFds;
|
||||
|
||||
FD_ZERO (&readFds);
|
||||
FD_ZERO (&writeFds);
|
||||
FD_ZERO (&exceptFds);
|
||||
|
||||
for (nfds_t i = 0; i < nfds_; ++i)
|
||||
{
|
||||
if (fds_[i].events & POLLIN)
|
||||
FD_SET (fds_[i].fd, &readFds);
|
||||
if (fds_[i].events & POLLOUT)
|
||||
FD_SET (fds_[i].fd, &writeFds);
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout_ / 1000;
|
||||
tv.tv_usec = (timeout_ % 1000) * 1000;
|
||||
auto const rc = ::select (nfds_, &readFds, &writeFds, &exceptFds, &tv);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
int count = 0;
|
||||
for (nfds_t i = 0; i < nfds_; ++i)
|
||||
{
|
||||
bool counted = false;
|
||||
fds_[i].revents = 0;
|
||||
|
||||
if (FD_ISSET (fds_[i].fd, &readFds))
|
||||
{
|
||||
counted = true;
|
||||
fds_[i].revents |= POLLIN;
|
||||
}
|
||||
|
||||
if (FD_ISSET (fds_[i].fd, &writeFds))
|
||||
{
|
||||
counted = true;
|
||||
fds_[i].revents |= POLLOUT;
|
||||
}
|
||||
|
||||
if (FD_ISSET (fds_[i].fd, &exceptFds))
|
||||
{
|
||||
counted = true;
|
||||
fds_[i].revents |= POLLERR;
|
||||
}
|
||||
|
||||
if (counted)
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
@ -18,6 +18,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef CLASSIC
|
||||
#include "imgui_deko3d.h"
|
||||
|
||||
#include "fs.h"
|
||||
@ -528,3 +529,4 @@ void imgui::deko3d::render (dk::UniqueDevice &device_,
|
||||
// submit final commands
|
||||
queue_.submitCommands (cmdBuf_.finishList ());
|
||||
}
|
||||
#endif
|
||||
|
@ -18,6 +18,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef CLASSIC
|
||||
#include "imgui_nx.h"
|
||||
|
||||
#include "imgui.h"
|
||||
@ -25,8 +26,6 @@
|
||||
#include "fs.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
@ -1512,3 +1511,4 @@ void imgui::nx::exit ()
|
||||
// deinitialize applet hooks
|
||||
appletUnhook (&s_appletHookCookie);
|
||||
}
|
||||
#endif
|
||||
|
@ -63,7 +63,7 @@ void userAppInit ()
|
||||
return;
|
||||
|
||||
#ifndef NDEBUG
|
||||
s_fd = nxlinkStdio ();
|
||||
// s_fd = nxlinkStdioForDebug ();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "platform.h"
|
||||
|
||||
#include "fs.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "imgui_deko3d.h"
|
||||
#include "imgui_nx.h"
|
||||
@ -29,8 +30,7 @@
|
||||
|
||||
#include <zstd.h>
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <cerrno>
|
||||
@ -41,8 +41,17 @@
|
||||
#include <numeric>
|
||||
#include <thread>
|
||||
|
||||
#ifdef CLASSIC
|
||||
PrintConsole g_statusConsole;
|
||||
PrintConsole g_logConsole;
|
||||
PrintConsole g_sessionConsole;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
#ifdef CLASSIC
|
||||
in_addr_t s_addr = 0;
|
||||
#else
|
||||
/// \brief Texture index
|
||||
enum TextureIndex
|
||||
{
|
||||
@ -385,26 +394,52 @@ void deko3dExit ()
|
||||
s_depthMemBlock = nullptr;
|
||||
s_device = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// \brief Draw time status
|
||||
void drawTimeStatus ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
auto const &io = ImGui::GetIO ();
|
||||
auto const &style = ImGui::GetStyle ();
|
||||
#endif
|
||||
|
||||
// draw current timestamp
|
||||
char buffer[64];
|
||||
char timeBuffer[64];
|
||||
auto const now = std::time (nullptr);
|
||||
std::strftime (buffer, sizeof (buffer), "%H:%M:%S", std::localtime (&now));
|
||||
std::strftime (timeBuffer, sizeof (timeBuffer), "%H:%M:%S", std::localtime (&now));
|
||||
|
||||
#ifdef CLASSIC
|
||||
static std::string statusString;
|
||||
|
||||
std::string newStatusString (256, '\0');
|
||||
newStatusString.resize (std::sprintf (&newStatusString[0],
|
||||
"\x1b[0;0H\x1b[32;1m%s \x1b[36;1m%s%s \x1b[37;1m%s\x1b[K",
|
||||
STATUS_STRING,
|
||||
s_addr ? inet_ntoa (in_addr{s_addr}) : "Waiting",
|
||||
s_addr ? ":5000" : "",
|
||||
timeBuffer));
|
||||
|
||||
if (newStatusString != statusString)
|
||||
{
|
||||
statusString = std::move (newStatusString);
|
||||
|
||||
consoleSelect (&g_statusConsole);
|
||||
std::fputs (statusString.c_str (), stdout);
|
||||
std::fflush (stdout);
|
||||
}
|
||||
#else
|
||||
ImGui::GetForegroundDrawList ()->AddText (
|
||||
ImVec2 (io.DisplaySize.x - 240.0f, style.FramePadding.y),
|
||||
ImGui::GetColorU32 (ImGuiCol_Text),
|
||||
buffer);
|
||||
timeBuffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Draw network status
|
||||
void drawNetworkStatus ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
TextureIndex netIcon = AIRPLANE_ICON;
|
||||
|
||||
NifmInternetConnectionType type;
|
||||
@ -447,11 +482,13 @@ void drawNetworkStatus ()
|
||||
ImVec2 (0, 0),
|
||||
ImVec2 (1, 1),
|
||||
ImGui::GetColorU32 (ImGuiCol_Text));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Draw power status
|
||||
void drawPowerStatus ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
std::uint32_t batteryCharge = 0;
|
||||
psmGetBatteryChargePercentage (&batteryCharge);
|
||||
|
||||
@ -480,8 +517,10 @@ void drawPowerStatus ()
|
||||
|
||||
char buffer[16];
|
||||
std::sprintf (buffer, "%3u%%", batteryCharge);
|
||||
|
||||
ImGui::GetForegroundDrawList ()->AddText (
|
||||
ImVec2 (x1 - 70.0f, y1), ImGui::GetColorU32 (ImGuiCol_Text), buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \brief Draw status
|
||||
@ -495,10 +534,21 @@ void drawStatus ()
|
||||
|
||||
bool platform::init ()
|
||||
{
|
||||
#ifdef CLASSIC
|
||||
consoleInit (&g_statusConsole);
|
||||
consoleInit (&g_logConsole);
|
||||
consoleInit (&g_sessionConsole);
|
||||
|
||||
consoleSetWindow (&g_statusConsole, 0, 0, 80, 1);
|
||||
consoleSetWindow (&g_logConsole, 0, 1, 80, 29);
|
||||
consoleSetWindow (&g_sessionConsole, 0, 30, 80, 15);
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::setvbuf (stderr, nullptr, _IOLBF, 0);
|
||||
#endif
|
||||
|
||||
#ifndef CLASSIC
|
||||
if (!imgui::nx::init ())
|
||||
return false;
|
||||
|
||||
@ -511,6 +561,7 @@ bool platform::init ()
|
||||
s_imageDescriptors[0],
|
||||
dkMakeTextureHandle (0, 0),
|
||||
FB_NUM);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -523,6 +574,11 @@ bool platform::networkVisible ()
|
||||
if (R_FAILED (nifmGetInternetConnectionStatus (&type, &wifi, &status)))
|
||||
return false;
|
||||
|
||||
#ifdef CLASSIC
|
||||
if (!s_addr)
|
||||
s_addr = gethostid ();
|
||||
#endif
|
||||
|
||||
return status == NifmInternetConnectionStatus_Connected;
|
||||
}
|
||||
|
||||
@ -537,6 +593,7 @@ bool platform::loop ()
|
||||
if (keysDown & KEY_PLUS)
|
||||
return false;
|
||||
|
||||
#ifndef CLASSIC
|
||||
imgui::nx::newFrame ();
|
||||
ImGui::NewFrame ();
|
||||
|
||||
@ -554,6 +611,7 @@ bool platform::loop ()
|
||||
imgui::deko3d::makeTextureID (dkMakeTextureHandle (1, 1)),
|
||||
ImVec2 (x1, y1),
|
||||
ImVec2 (x2, y2));
|
||||
#endif
|
||||
|
||||
drawStatus ();
|
||||
|
||||
@ -562,6 +620,12 @@ bool platform::loop ()
|
||||
|
||||
void platform::render ()
|
||||
{
|
||||
#ifdef CLASSIC
|
||||
drawLog ();
|
||||
consoleUpdate (&g_statusConsole);
|
||||
consoleUpdate (&g_logConsole);
|
||||
consoleUpdate (&g_sessionConsole);
|
||||
#else
|
||||
ImGui::Render ();
|
||||
|
||||
auto &io = ImGui::GetIO ();
|
||||
@ -595,10 +659,16 @@ void platform::render ()
|
||||
|
||||
// present image
|
||||
s_queue.presentImage (s_swapchain, slot);
|
||||
#endif
|
||||
}
|
||||
|
||||
void platform::exit ()
|
||||
{
|
||||
#ifdef CLASSIC
|
||||
consoleExit (&g_sessionConsole);
|
||||
consoleExit (&g_logConsole);
|
||||
consoleExit (&g_statusConsole);
|
||||
#else
|
||||
imgui::nx::exit ();
|
||||
|
||||
// wait for queue to be idle
|
||||
@ -606,6 +676,7 @@ void platform::exit ()
|
||||
|
||||
imgui::deko3d::exit ();
|
||||
deko3dExit ();
|
||||
#endif
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user