initial commit

This commit is contained in:
mtheall 2014-11-23 16:39:00 -06:00
commit 6d4b259324
19 changed files with 3228 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build/
*.3dsx
*.smdh
*.elf

168
Makefile Normal file
View File

@ -0,0 +1,168 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
CTRULIB:=/home/mtheall/workspace/ninjhax/ctrulib/libctru
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
ifeq ($(strip $(CTRULIB)),)
# THIS IS TEMPORARY - in the future it should be at $(DEVKITPRO)/libctru
$(error "Please set CTRULIB in your environment. export CTRULIB=<path to>libctru")
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):
# - <Project name>.png
# - icon.png
# - <libctru folder>/default_icon.png
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
APP_TITLE := ftBRONY
APP_DESCRIPTION := Like ftPONY but magical.
APP_AUTHOR := mtheall
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=softfp
CFLAGS := -g -Wall -O3 -mword-relocations \
-fomit-frame-pointer -ffast-math \
$(ARCH) \
-DSTATUS_STRING="\"ftpd v1.0\""
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,$(TARGET).map
LIBS := -lctru
#---------------------------------------------------------------------------------
# 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).smdh $(TARGET).elf
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(NO_SMDH)),)
.PHONY: all
all : $(OUTPUT).3dsx $(OUTPUT).smdh
endif
$(OUTPUT).3dsx: $(OUTPUT).elf
$(OUTPUT).elf: $(OFILES)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o: %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

BIN
data/sans.10.kerning.bin Normal file

Binary file not shown.

BIN
data/sans.10.render.bin Normal file

Binary file not shown.

BIN
data/sans.12.kerning.bin Normal file

Binary file not shown.

BIN
data/sans.12.render.bin Normal file

Binary file not shown.

BIN
data/sans.14.kerning.bin Normal file

Binary file not shown.

BIN
data/sans.14.render.bin Normal file

Binary file not shown.

BIN
data/sans.16.kerning.bin Normal file

Binary file not shown.

BIN
data/sans.16.render.bin Normal file

Binary file not shown.

BIN
data/sans.8.kerning.bin Normal file

Binary file not shown.

BIN
data/sans.8.render.bin Normal file

Binary file not shown.

BIN
ftbrony.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

12
include/console.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
void console_init(void);
void console_exit(void);
__attribute__((format(printf,1,2)))
void console_set_status(const char *fmt, ...);
__attribute__((format(printf,1,2)))
void console_print(const char *fmt, ...);
void console_render(void);

54
include/debug.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include <string.h>
#ifdef _3DS
#include <3ds.h>
#endif
/*! print debug message
*
* @param[in] fmt format string
* @param[in] ap varargs list
*
* @returns number of characters written
*/
static inline int
vdebug(const char *fmt,
va_list ap)
{
#ifdef _3DS
int rc;
char buffer[256];
memset(buffer, 0, sizeof(buffer));
/* print to buffer */
rc = vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
/* call debug service with buffer */
svcOutputDebugString(buffer, rc < sizeof(buffer) ? rc : sizeof(buffer));
return rc;
#else
/* just print to stdout */
return vprintf(fmt, ap);
#endif
}
__attribute__((format(printf,1,2)))
/*! print debug message
*
* @param[in] fmt format string
* @param[in] ... format arguments
*
* @returns number of characters written
*/
static inline int
debug(const char *fmt, ...)
{
int rc;
va_list ap;
va_start(ap, fmt);
rc = vdebug(fmt, ap);
va_end(ap);
return rc;
}

5
include/ftp.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
int ftp_init(void);
int ftp_loop(void);
void ftp_exit(void);

735
source/console.c Normal file
View File

@ -0,0 +1,735 @@
#include "console.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#ifdef _3DS
#include <3ds.h>
#endif
#include "debug.h"
#ifdef _3DS
#include "sans_8_kerning_bin.h"
#include "sans_8_render_bin.h"
#include "sans_10_kerning_bin.h"
#include "sans_10_render_bin.h"
#include "sans_12_kerning_bin.h"
#include "sans_12_render_bin.h"
#include "sans_14_kerning_bin.h"
#include "sans_14_render_bin.h"
#include "sans_16_kerning_bin.h"
#include "sans_16_render_bin.h"
/* TODO: add support for non-ASCII characters */
/*! rendering information */
typedef struct
{
int c; /*!< character */
int y_off; /*!< vertical offset */
int width; /*!< width */
int height; /*!< height */
int x_adv; /*!< horizontal advance */
u8 data[]; /*!< width*height bitmap */
} render_info_t;
/*! kerning information */
typedef struct
{
int prev; /*!< previous character */
int next; /*!< next character */
int x_off; /*!< horizontal adjustment */
} kerning_info_t;
/*! font data */
typedef struct
{
const char *name; /*!< font name */
render_info_t **render_info; /*!< render information list */
kerning_info_t *kerning_info; /*!< kerning information list */
size_t num_render_info; /*!< number of render information nodes */
size_t num_kerning_info; /*!< number of kerning information nodes */
int pt; /*!< font size */
} font_t;
/*! font information */
typedef struct
{
const char *name; /*!< font name */
int pt; /*!< font size */
const u8 *render_data; /*!< render data */
const u8 *kerning_data; /*!< kerning data */
const u32 *render_data_size; /*!< render data size */
const u32 *kerning_data_size; /*!< kerning data size */
} font_info_t;
/*! font descriptors */
static font_info_t font_info[] =
{
#define FONT_INFO(name, pt) \
{ #name, pt, name##_##pt##_render_bin, name##_##pt##_kerning_bin, \
&name##_##pt##_render_bin_size, &name##_##pt##_kerning_bin_size, }
FONT_INFO(sans, 8),
FONT_INFO(sans, 10),
FONT_INFO(sans, 12),
FONT_INFO(sans, 14),
FONT_INFO(sans, 16),
};
/*! number of font descriptors */
static const size_t num_font_info = sizeof(font_info)/sizeof(font_info[0]);
/*! find next render info
*
* @param[in] info current render info
*
* @returns next render info
*/
static render_info_t*
next_render_info(render_info_t *info)
{
char *ptr = (char*)info;
ptr += sizeof(*info) + info->width*info->height;
ptr = (char*)(((int)ptr + sizeof(int)-1) & ~(sizeof(int)-1));
return (render_info_t*)ptr;
}
/*! free font info
*
* @param[in] font
*/
static void
free_font(font_t *font)
{
free(font->render_info);
free(font);
}
/*! load font info
*
* @param[in] name
* @param[in] pt
* @param[in] render_data
* @param[in] render_data_size
* @param[in] kerning data
* @param[in] kerning_data_size
*
* @returns font info
*/
static font_t*
load_font(const char *name,
int pt,
const u8 *render_data,
size_t render_data_size,
const u8 *kerning_data,
size_t kerning_data_size)
{
size_t i;
render_info_t *rinfo;
font_t *font;
/* allocate new font info */
font = (font_t*)calloc(1, sizeof(font_t));
if(font != NULL)
{
/* count number of render entries */
rinfo = (render_info_t*)render_data;
while((u8*)rinfo < render_data + render_data_size)
{
++font->num_render_info;
rinfo = next_render_info(rinfo);
}
/* allocate array of render info pointers */
font->render_info = (render_info_t**)calloc(font->num_render_info, sizeof(render_info_t));
if(font->render_info != NULL)
{
/* fill in the pointer list */
rinfo = (render_info_t*)render_data;
i = 0;
while((u8*)rinfo < render_data + render_data_size)
{
font->render_info[i++] = rinfo;
rinfo = next_render_info(rinfo);
}
/* fill in the kerning info */
font->kerning_info = (kerning_info_t*)kerning_data;
font->num_kerning_info = kerning_data_size / sizeof(kerning_info_t);
/* set font size and name */
font->pt = pt;
font->name = name;
}
else
{
/* failed to allocate render info list */
free_font(font);
font = NULL;
}
}
return font;
}
/*! list of font info entries */
static font_t **fonts;
/*! number of font info entries */
static size_t num_fonts = 0;
/*! compare two fonts
*
* @param[in] p1 left side of comparison (font_t**)
* @param[in] p2 right side of comparison (font_t**)
*
* @returns <0 if p1 < p2
* @returns 0 if p1 == p2
* @returns >0 if p1 > p2
*/
static int
font_cmp(const void *p1,
const void *p2)
{
/* interpret parameters */
font_t *f1 = *(font_t**)p1;
font_t *f2 = *(font_t**)p2;
/* major key is font name */
int rc = strcmp(f1->name, f2->name);
if(rc != 0)
return rc;
/* minor key is font size */
if(f1->pt < f2->pt)
return -1;
if(f1->pt > f2->pt)
return 1;
return 0;
}
/*! search for a font by name and size
*
* @param[in] name font name
* @param[in] pt font size
*
* @returns matching font
*/
static font_t*
find_font(const char *name,
int pt)
{
/* create a key to search for */
font_t key, *keyptr;
key.name = name;
key.pt = pt;
keyptr = &key;
/* search for the key */
void *font = bsearch(&keyptr, fonts, num_fonts, sizeof(font_t*), font_cmp);
if(font == NULL)
return NULL;
/* found it */
return *(font_t**)font;
}
/*! initialize console subsystem */
void
console_init(void)
{
size_t i;
/* allocate font list */
fonts = (font_t**)calloc(num_font_info, sizeof(font_t*));
if(fonts == NULL)
return;
/* load fonts */
for(i = 0; i < num_font_info; ++i)
{
font_info_t *info = &font_info[i];
fonts[num_fonts] = load_font(info->name, info->pt,
info->render_data,
*info->render_data_size,
info->kerning_data,
*info->kerning_data_size);
if(fonts[num_fonts] != NULL)
++num_fonts;
}
/* sort the list for bsearch later */
qsort(fonts, num_fonts, sizeof(font_t*), font_cmp);
}
/*! deinitialize console subsystem */
void
console_exit(void)
{
int i;
/* free the font info */
for(i = 0; i < num_fonts; ++i)
free_font(fonts[i]);
/* free the font info list */
free(fonts);
fonts = NULL;
}
/*! status bar contents */
static char status[64];
/*! console buffer */
static char buffer[8192];
/*! pointer to end of buffer */
static char *buffer_end = buffer + sizeof(buffer);
/*! pointer to end of console contents */
static char *end = buffer;
/*! count lines in console contents */
static size_t
count_lines(void)
{
size_t lines = 0;
char *p = buffer;
/* search for each newline character */
while(p < end && (p = strchr(p, '\n')) != NULL)
{
++lines;
++p;
}
return lines;
}
/*! remove lines that have "scrolled" off screen */
static void
reduce_lines(void)
{
int lines = count_lines();
char *p = buffer;
/* we can fit 18 lines on the screen */
/* TODO make based on pt size */
while(lines > 18)
{
p = strchr(p, '\n');
++p;
--lines;
}
/* move the new beginning to where it needs to be */
ptrdiff_t distance = p - buffer;
memmove(buffer, buffer+distance, end - p);
end -= distance;
*end = 0;
}
/*! set status bar contents
*
* @param[in] fmt format string
* @param[in] ... format arguments
*/
void
console_set_status(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
memset(status, 0, sizeof(status));
vsnprintf(status, sizeof(status)-1, fmt, ap);
va_end(ap);
}
/*! add text to the console
*
* @param[in] fmt format string
* @param[in] ... format arguments
*/
void
console_print(const char *fmt, ...)
{
int rc;
va_list ap;
/* append to the end of the console buffer */
va_start(ap, fmt);
rc = vsnprintf(end, buffer_end - end - 1, fmt, ap);
va_end(ap);
/* null terminate buffer */
end += rc;
if(end >= buffer_end)
end = buffer_end - 1;
*end = 0;
/* scroll */
reduce_lines();
}
/*! compare render information
*
* @param[in] p1 left side of comparison (render_info_t**)
* @param[in] p2 right side of comparison (render_info_t**)
*
* @returns <0 if p1 < p2
* @returns 0 if p1 == p2
* @returns >0 if p1 > p2
*/
static int
render_info_cmp(const void *p1,
const void *p2)
{
/* interpret parameters */
render_info_t *r1 = *(render_info_t**)p1;
render_info_t *r2 = *(render_info_t**)p2;
/* ordered by character */
if(r1->c < r2->c)
return -1;
else if(r1->c > r2->c)
return 1;
return 0;
}
/*! search for render info by character
*
* @param[in] font font info
* @param[in] char character
*
* @returns matching render info
*/
static render_info_t*
find_render_info(font_t *font,
char c)
{
/* create a key to search for */
render_info_t key, *keyptr;
key.c = c;
keyptr = &key;
/* search for the key */
void *info = bsearch(&keyptr, font->render_info, font->num_render_info,
sizeof(render_info_t*), render_info_cmp);
if(info == NULL)
return NULL;
/* found it */
return *(render_info_t**)info;
}
/*! compare kerning information
*
* @param[in] p1 left side of comparison (kerning_info_t*)
* @param[in] p2 right side of comparison (kerning_info_t*)
*
* @returns <0 if p1 < p2
* @returns 0 if p1 == p2
* @returns >0 if p1 > p2
*/
static int
kerning_info_cmp(const void *p1,
const void *p2)
{
/* interpret parameters */
kerning_info_t *k1 = (kerning_info_t*)p1;
kerning_info_t *k2 = (kerning_info_t*)p2;
/* major key is prev */
if(k1->prev < k2->prev)
return -1;
if(k1->prev > k2->prev)
return 1;
/* minor key is next */
if(k1->next < k2->next)
return -1;
if(k1->next > k2->next)
return 1;
return 0;
}
/*! search for kerning info by character pair
*
* @param[in] font font info
* @param[in] prev prev character
* @param[in] next next character
*
* @returns matching render info
*/
static kerning_info_t*
find_kerning_info(font_t *font,
char prev,
char next)
{
/* create a key to search for */
kerning_info_t key;
key.prev = prev;
key.next = next;
/* search for the key */
void *info = bsearch(&key, font->kerning_info, font->num_kerning_info,
sizeof(kerning_info_t), kerning_info_cmp);
if(info == NULL)
return NULL;
/* found it */
return (kerning_info_t*)info;
}
/*! clear framebuffer
*
* @param[in] screen screen to clear
* @param[in] side which side on the stereoscopic display
* @param[in] rgbColor clear color
*/
static void
clear_screen(gfxScreen_t screen,
gfx3dSide_t side,
u8 rgbColor[3])
{
/* get the framebuffer information */
u16 fbWidth, fbHeight;
u8 *fb = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight);
/* fill the framebuffer with the clear color */
int i;
for(i = 0; i < fbWidth*fbHeight; ++i)
{
*(fb++) = rgbColor[2];
*(fb++) = rgbColor[1];
*(fb++) = rgbColor[0];
}
}
/*! draw a quad
*
* @param[in] screen screen to draw to
* @param[in] side which side on the stereoscopic display
* @param[in] data quad data
* @param[in] x quad x position
* @param[in] y quad y position
* @param[in] w quad width
* @param[in] h quad height
*
* @note this quad data is 8-bit alpha-only
* @note uses framebuffer native coordinates
*/
static void
draw_quad(gfxScreen_t screen,
gfx3dSide_t side,
const u8 *data,
int x,
int y,
int w,
int h)
{
int i, j;
int index = 0;
int stride = w;
/* get the framebuffer information */
u16 width, height;
u8 *fb = gfxGetFramebuffer(screen, side, &width, &height);
/* this quad is totally offscreen; don't draw */
if(x > width || y > height)
return;
/* this quad is totally offscreen; don't draw */
if(x + w < 0 || y + h < 0)
return;
/* adjust parameters for partially visible quad */
if(x < 0)
{
index -= x;
w += x;
x = 0;
}
/* adjust parameters for partially visible quad */
if(y < 0)
{
index -= y*stride;
h += y;
y = 0;
}
/* adjust parameters for partially visible quad */
if(x + w > width)
w = width - x;
/* adjust parameters for partially visible quad */
if(y + h > height)
h = height - y;
/* move framebuffer pointer to quad start position */
fb += (y*width + x)*3;
/* fill in data */
for(j = 0; j < h; ++j)
{
for(i = 0; i < w; ++i)
{
/* alpha blending; assuming color is white */
int v = data[index];
fb[0] = fb[0]*(0xFF-v)/0xFF + v;
fb[1] = fb[1]*(0xFF-v)/0xFF + v;
fb[2] = fb[2]*(0xFF-v)/0xFF + v;
++index;
fb += 3;
}
index += (stride-w);
fb += (width-w)*3;
}
}
/*! draw text to framebuffer
*
* @param[in] screen screen to draw to
* @param[in] side which side on the stereoscopic display
* @param[in] font font to use when rendering
* @param[in] data quad data
* @param[in] x quad x position
* @param[in] y quad y position
*
* @note uses intuitive coordinates
*/
static void
draw_text(gfxScreen_t screen,
gfx3dSide_t side,
font_t *font,
const char *data,
int x,
int y)
{
render_info_t *rinfo;
kerning_info_t *kinfo;
const char *p;
int xoff = x, yoff = y;
char prev = 0;
/* draw each character */
for(p = data; *p != 0; ++p)
{
/* newline; move down a line and all the way left */
if(*p == '\n')
{
xoff = x;
yoff += font->pt + font->pt/2;
prev = 0;
continue;
}
/* look up the render info for this character */
rinfo = find_render_info(font, *p);
/* couldn't find it; just ignore it */
if(rinfo == NULL)
continue;
/* find kerning data */
kinfo = NULL;
if(prev != 0)
kinfo = find_kerning_info(font, prev, *p);
/* adjust for kerning */
if(kinfo != NULL)
xoff += kinfo->x_off >> 6;
/* save this character for next kerning lookup */
prev = *p;
/* render character */
if(rinfo->width != 0 && rinfo->height != 0)
{
int x, y;
/* get framebuffer info */
u16 width, height;
gfxGetFramebuffer(screen, side, &width, &height);
/* transform intuitive coordinates to framebuffer-native */
x = width - yoff - font->pt - 2 - (rinfo->height - rinfo->y_off);
y = xoff;
/* draw character */
draw_quad(screen, side, rinfo->data, x, y,
rinfo->height, rinfo->width);
}
/* advance to next character coordinate */
xoff += rinfo->x_adv >> 6;
}
}
/*! draw console to screen */
void
console_render(void)
{
font_t *font;
/* clear all screens */
u8 bluish[] = { 0, 0, 127 };
clear_screen(GFX_TOP, GFX_LEFT, bluish);
clear_screen(GFX_BOTTOM, GFX_LEFT, bluish);
/* look up font for status bar and draw status bar */
font = find_font("sans", 10);
if(font != NULL)
draw_text(GFX_TOP, GFX_LEFT, font, status, 4, 4);
else
debug("%s: couldn't find 'sans 10pt'\n", __func__);
/* look up font for console and draw console */
font = find_font("sans", 8);
if(font != NULL)
draw_text(GFX_TOP, GFX_LEFT, font, buffer, 4, 20);
else
debug("%s: couldn't find 'sans 8pt'\n", __func__);
/* flush framebuffer */
gfxFlushBuffers();
gspWaitForVBlank();
gfxSwapBuffers();
}
#else
/* this is a lot easier when you have a real console */
void
console_init(void)
{
}
void
console_exit(void)
{
}
void
console_set_status(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
fputc('\n', stdout);
}
void
console_print(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
void console_render(void)
{
}
#endif

2136
source/ftp.c Normal file

File diff suppressed because it is too large Load Diff

114
source/main.c Normal file
View File

@ -0,0 +1,114 @@
#include <malloc.h>
#include <stdarg.h>
#include <stdio.h>
#ifdef _3DS
#include <3ds.h>
#endif
#include "console.h"
#include "ftp.h"
/*! looping mechanism
*
* @param[in] callback function to call during each iteration
*/
static void
loop(int (*callback)(void))
{
#ifdef _3DS
int rc;
APP_STATUS status;
/* check apt status */
while((status = aptGetStatus()) != APP_EXITING)
{
rc = 0;
if(status == APP_RUNNING)
rc = callback();
else if(status == APP_SUSPENDING)
aptReturnToMenu();
else if(status == APP_SLEEPMODE)
aptWaitStatusEvent();
if(rc == 0)
console_render();
else
return;
}
#else
for(;;)
callback();
#endif
}
/*! wait until the B button is pressed
*
* @returns -1 if B was pressed
*/
static int
wait_for_b(void)
{
#ifdef _3DS
/* update button state */
hidScanInput();
/* check if B was pressed */
if(hidKeysDown() & KEY_B)
return -1;
/* B was not pressed */
return 0;
#else
return -1;
#endif
}
/*! entry point
*
* @param[in] argc unused
* @param[in] argv unused
*
* returns exit status
*/
int
main(int argc,
char *argv[])
{
#ifdef _3DS
/* initialize needed 3DS services */
srvInit();
aptInit();
hidInit(NULL);
irrstInit(NULL);
gfxInit();
gfxSet3D(false);
#endif
/* initialize console subsystem */
console_init();
console_set_status(STATUS_STRING);
/* initialize ftp subsystem */
if(ftp_init() == 0)
{
/* ftp loop */
loop(ftp_loop);
/* done with ftp */
ftp_exit();
}
console_print("Press B to exit\n");
loop(wait_for_b);
console_exit();
#ifdef _3DS
/* deinitialize 3DS services */
gfxExit();
irrstExit();
hidExit();
aptExit();
srvExit();
#endif
return 0;
}