Merge pull request #1 from bunnei/initial-app

Initial commit of utility to dump shared system font.
This commit is contained in:
bunnei 2014-12-10 20:25:14 -05:00
commit 2cf22ef10d
14 changed files with 806 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
build/
*.3dsx
*.3ds
*.elf
.DS_Store
.swp

146
Makefile Normal file
View File

@ -0,0 +1,146 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
# SPECS is the directory containing the important build and link files
#---------------------------------------------------------------------------------
export TARGET := $(shell basename $(CURDIR))
BUILD := build
SOURCES := source source/utils source/utils/shared_font
DATA := data
INCLUDES := source #include
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv6k -mtune=mpcore
CFLAGS := -g -Wall -O2 -mword-relocations -save-temps \
-fomit-frame-pointer -ffast-math -mfloat-abi=softfp \
$(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,$(TARGET).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)
.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 $(TARGET).elf
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(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)
# not the right way to do this
#---------------------------------------------------------------------------------
%.vsh.o : %.vsh
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@python $(AEMSTRO)/aemstro_as.py $< ../$(notdir $<).shbin
@bin2s ../$(notdir $<).shbin | arm-none-eabi-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
#---------------------------------------------------------------------------------------

BIN
data/font.bin Normal file

Binary file not shown.

21
send-exec.py Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
### TO USE:
#### Press Y on the HBMenu (opens the NetLoader)
#### Change host/port combination
#### Run the script
import socket
import sys
import time
TCP_IP = '192.168.xx.xx'
TCP_PORT = 9000
MESSAGE = open("citra-3dsutils.3dsx", "rb").read();
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(MESSAGE)
time.sleep(10)
s.close()

9
source/font.cpp Normal file
View File

@ -0,0 +1,9 @@
#include <3ds.h>
#include "font.h"
font_s fontDefault = {
font1Data,
font1Desc,
16,
{ 0xFF, 0xFF, 0xFF }
};

34
source/font.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
struct Glyph {
// Glyph representation
char c;
// x and y origin of the character.
int x, y;
// width and height in pixels.
int w, h;
// x and y offset
int xo, yo;
// Pixels after this character to begin
// drawing the next one.
int xa;
// Glyph data.
u8* data;
};
struct font_s {
u8* data;
Glyph* desc;
u8 height;
u8 color[3];
};
extern u8 font1Data[];
extern Glyph font1Desc[];
extern font_s fontDefault;

261
source/font1.cpp Normal file

File diff suppressed because one or more lines are too long

60
source/main.cpp Normal file
View File

@ -0,0 +1,60 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <stdlib.h>
#include <stdio.h>
#include <3ds.h>
#include "output.h"
#include "utils/shared_font/shared_font.h"
static unsigned int util_counter = 0;
static void (*utils[]) (void) = {
SharedFont::Dump,
};
int main()
{
srvInit();
aptInit();
hidInit(NULL);
fsInit();
gfxInit();
gfxSet3D(false);
clearScreens();
print(GFX_TOP, "Press A to begin...\n");
while (aptMainLoop()) {
drawFrames();
hidScanInput();
if (hidKeysDown() & KEY_START) {
break;
} else if (hidKeysDown() & KEY_A) {
clearScreen(GFX_TOP);
if (util_counter < (sizeof(utils) / sizeof(utils[0]))) {
utils[util_counter]();
util_counter++;
} else {
break;
}
print(GFX_TOP, "\n");
print(GFX_TOP, "Press A to continue...\n");
}
gspWaitForEvent(GSPEVENT_VBlank0, false);
}
clearScreens();
gfxExit();
fsExit();
hidExit();
aptExit();
srvExit();
return 0;
}

98
source/output.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "output.h"
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <cstdarg>
#include <cstdio>
#include <3ds.h>
#include "text.h"
static std::string bufferTop;
static std::string bufferBottom;
static int countLines(const std::string& str)
{
if (str.empty())
return 0;
return 1 + std::count_if(str.begin(), str.end(), [](char c) { return c == '\n'; });
}
static void deleteFirstLine(std::string* str)
{
if (str->empty())
return;
size_t linebreak = str->find_first_of('\n');
if (linebreak == std::string::npos || linebreak + 1 > str->length()) {
*str = {};
return;
}
*str = str->substr(linebreak + 1);
}
static void drawFrame(gfxScreen_t screen, char b, char g, char r)
{
int screenHeight = 240;
int screenWidth = (screen == GFX_TOP) ? 400 : 320;
std::string& textBuffer = (screen == GFX_TOP) ? bufferTop : bufferBottom;
u8* bufAdr = gfxGetFramebuffer(screen, GFX_LEFT, nullptr, nullptr);
for (int i = 0; i < screenWidth * screenHeight * 3; i += 3) {
bufAdr[i] = b;
bufAdr[i+1] = g;
bufAdr[i+2] = r;
}
int lines = countLines(textBuffer);
while (lines > (screenHeight / fontDefault.height - 3)) {
deleteFirstLine(&textBuffer);
lines--;
}
gfxDrawText(screen, GFX_LEFT, nullptr, textBuffer, screenHeight - fontDefault.height * 3, 10);
}
void drawFrames()
{
drawFrame(GFX_TOP, 0x88, 0x66, 0x00);
drawFrame(GFX_BOTTOM, 0x00, 0x00, 0x00);
gfxFlushBuffers();
gfxSwapBuffers();
}
void print(gfxScreen_t screen, const char* format, ...)
{
std::string& textBuffer = (screen == GFX_TOP) ? bufferTop : bufferBottom;
va_list arguments;
char *vaStr;
va_start(arguments, format);
vasprintf(&vaStr, format, arguments);
va_end(arguments);
textBuffer += std::string(vaStr);
svcOutputDebugString(vaStr, strlen(vaStr));
free(vaStr);
drawFrames();
}
void clearScreen(gfxScreen_t screen)
{
std::string& textBuffer = (screen == GFX_TOP) ? bufferTop : bufferBottom;
textBuffer.clear();
drawFrames();
}
void clearScreens()
{
clearScreen(GFX_TOP);
clearScreen(GFX_BOTTOM);
}

8
source/output.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <3ds.h>
void drawFrames();
void print(gfxScreen_t screen, const char* format, ...);
void clearScreen(gfxScreen_t screen);
void clearScreens();

80
source/text.cpp Normal file
View File

@ -0,0 +1,80 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "text.h"
#include "font.h"
//this code is not meant to be readable
int drawCharacter(u8* fb, font_s* font, char c, s16 x, s16 y, u16 w, u16 h)
{
Glyph* cd = &font->desc[(int)c];
if (!cd->data)
return 0;
x += cd->xo; y += font->height - cd->yo - cd->h;
if (x < 0 || x + cd->w >= w || y < -cd->h || y >= h + cd->h)
return 0;
u8* charData = cd->data;
s16 cy = y, ch = cd->h, cyo = 0;
if (y < 0) {
cy = 0;
cyo = -y;
ch = cd->h-cyo;
} else if (y + ch > h) {
ch = h - y;
}
fb += (x * h + cy) * 3;
const u8 r = font->color[0];
const u8 g = font->color[1];
const u8 b = font->color[2];
for (int i = 0; i < cd->w; i++) {
charData += cyo;
for (int j = 0; j < ch; j++) {
u8 v = *(charData++);
if (v) {
fb[0] = (fb[0] * (0xFF - v) + (b * v)) >> 8;
fb[1] = (fb[1] * (0xFF - v) + (g * v)) >> 8;
fb[2] = (fb[2] * (0xFF - v) + (r * v)) >> 8;
}
fb += 3;
}
charData += (cd->h - (cyo + ch));
fb += (h - ch) * 3;
}
return cd->xa;
}
void drawString(u8* fb, font_s* f, const std::string& str, s16 x, s16 y, u16 w, u16 h)
{
if (!f || !fb)
return;
int dx = 0, dy = 0;
for (const char& c : str)
{
dx += drawCharacter(fb, f, c, x + dx, y + dy, w, h);
if (c == '\n') {
dx = 0;
dy -= f->height;
}
}
}
void gfxDrawText(gfxScreen_t screen, gfx3dSide_t side, font_s* font, const std::string& str, s16 x, s16 y)
{
if (!font)
font = &fontDefault;
u16 fbWidth, fbHeight;
u8* fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight);
drawString(fbAdr, font, str, y, x, fbHeight, fbWidth);
}

9
source/text.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <string>
#include "font.h"
int drawCharacter(u8* fb, font_s* f, char c, s16 x, s16 y, u16 w, u16 h);
void drawString(u8* fb, font_s* f, const std::string& str, s16 x, s16 y, u16 w, u16 h);
void gfxDrawText(gfxScreen_t screen, gfx3dSide_t side, font_s* f, const std::string& str, s16 x, s16 y);

View File

@ -0,0 +1,61 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <3ds.h>
#include "output.h"
#include "utils/shared_font/shared_font.h"
namespace SharedFont {
static const u32 SHARED_FONT_ADDR = 0x18000000;
static const u32 SHARED_FONT_SIZE = 0x300000;
void Dump() {
static const char* path = "/shared_font.bin";
print(GFX_TOP, "Dumping shared system font (%s)... ", path);
// Connect to APT service...
Handle apt_handle;
srvGetServiceHandle(&apt_handle, "APT:U");
u32* cmdbuf=getThreadCommandBuffer();
// Call APT::GetSharedFont function to load font into memory...
cmdbuf[0] = 0x00440000;
svcSendSyncRequest(apt_handle);
Handle mem_handle = cmdbuf[4];
// Close APT handle...
svcCloseHandle(apt_handle);
// Map shared font memory...
svcMapMemoryBlock(mem_handle, 0, MEMPERM_READ, MEMPERM_MAX);
// Dump shared font to SDMC...
Handle file_handle;
u32 bytes_written = 0;
static const FS_path fs_path = FS_makePath(PATH_CHAR, path);
FS_archive sdmc_archive = (FS_archive) { 0x00000009, { PATH_EMPTY, 1, (u8*) "" } };
FSUSER_OpenArchive(NULL, &sdmc_archive);
FSUSER_OpenFile(NULL, &file_handle, sdmc_archive, fs_path, FS_OPEN_CREATE | FS_OPEN_WRITE, FS_ATTRIBUTE_NONE);
Result res = FSFILE_Write(file_handle, &bytes_written, 0x0, (u32*)SHARED_FONT_ADDR, SHARED_FONT_SIZE, FS_WRITE_FLUSH);
FSFILE_Close(file_handle);
FSUSER_CloseArchive(NULL, &sdmc_archive);
// Check result...
if (res == 0 && bytes_written == SHARED_FONT_SIZE)
print(GFX_TOP, "Done!\n");
else
print(GFX_TOP, "Failed!\n");
}
} // namespace

View File

@ -0,0 +1,11 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
namespace SharedFont {
void Dump();
}