Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Libretro: add gameboy printer support (save to png / real printer on DevTerm) #2886

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ if(NOT LIBMGBA_ONLY)
set(BUILD_QT ON CACHE BOOL "Build Qt frontend")
set(BUILD_SDL ON CACHE BOOL "Build SDL frontend")
set(BUILD_LIBRETRO OFF CACHE BOOL "Build libretro core")
set(USE_DEVTERM OFF CACHE BOOL "Connect Game Boy core to DevTerm thermal printer")
if(APPLE)
set(BUILD_OPENEMU OFF CACHE BOOL "Build OpenEmu core")
endif()
Expand Down Expand Up @@ -987,11 +988,19 @@ if(BUILD_PYTHON)
endif()

if(BUILD_LIBRETRO)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/mgba-util/image)
file(GLOB RETRO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/libretro/*.c)
add_library(${BINARY_NAME}_libretro SHARED ${CORE_SRC} ${RETRO_SRC})
add_library(${BINARY_NAME}_libretro SHARED ${CORE_SRC} ${GB_SIO_SRC} ${UTIL_SRC} ${RETRO_SRC})
add_dependencies(${BINARY_NAME}_libretro ${BINARY_NAME}-version-info)
set_target_properties(${BINARY_NAME}_libretro PROPERTIES PREFIX "" COMPILE_DEFINITIONS "__LIBRETRO__;COLOR_16_BIT;COLOR_5_6_5;DISABLE_THREADING;MGBA_STANDALONE;${OS_DEFINES};${FUNCTION_DEFINES};MINIMAL_CORE=2")
target_link_libraries(${BINARY_NAME}_libretro ${OS_LIB})
set(LIBRETRO_DEFINES "__LIBRETRO__;COLOR_16_BIT;COLOR_5_6_5;DISABLE_THREADING;MGBA_STANDALONE;${OS_DEFINES};${FUNCTION_DEFINES};MINIMAL_CORE=2")
if (USE_PNG)
string(APPEND LIBRETRO_DEFINES ";USE_PNG")
endif()
if (USE_DEVTERM)
string(APPEND LIBRETRO_DEFINES ";USE_DEVTERM")
endif()
set_target_properties(${BINARY_NAME}_libretro PROPERTIES PREFIX "" COMPILE_DEFINITIONS "${LIBRETRO_DEFINES}")
target_link_libraries(${BINARY_NAME}_libretro ${DEPENDENCY_LIBS} ${OS_LIB})
if(MSVC)
install(TARGETS ${BINARY_NAME}_libretro RUNTIME DESTINATION ${LIBRETRO_LIBDIR} COMPONENT ${BINARY_NAME}_libretro)
else()
Expand Down
3 changes: 3 additions & 0 deletions include/mgba/core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ CXX_GUARD_START
#ifdef USE_DEBUGGERS
#include <mgba/debugger/debugger.h>
#endif
#ifdef USE_PNG
#include <mgba-util/image/png-io.h>
#endif

enum mPlatform {
mPLATFORM_NONE = -1,
Expand Down
112 changes: 112 additions & 0 deletions src/platform/libretro/libretro.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/mbc.h>
#include <mgba/internal/gb/overrides.h>
#include <mgba/internal/gb/sio/printer.h>
#endif
#ifdef M_CORE_GBA
#include <mgba/gba/core.h>
Expand Down Expand Up @@ -108,6 +109,8 @@ static bool audioLowPassEnabled = false;
static int32_t audioLowPassRange = 0;
static int32_t audioLowPassLeftPrev = 0;
static int32_t audioLowPassRightPrev = 0;
static bool printerInitDone;
static struct GBPrinter gbPrinter;

static const int keymap[] = {
RETRO_DEVICE_ID_JOYPAD_A,
Expand Down Expand Up @@ -204,6 +207,112 @@ static void _initSensors(void) {

sensorsInitDone = true;
}
#ifdef USE_PNG
static void _printImage(struct GBPrinter* printer, int height, const uint8_t* data) {
struct mImage* image = mImageCreate(GB_VIDEO_HORIZONTAL_PIXELS, height, mCOLOR_RGB565);

uint32_t colors[4] = {
0xF8F8F8F8,
0xA8A8A8A8,
0x50505050,
0x00000000
};
for (int y = 0; y < height*2; ++y) {
for (int x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
uint8_t byte = data[(x + y * GB_VIDEO_HORIZONTAL_PIXELS) / 4];
mImageSetPixel(image, x + 0, y, colors[(byte & 0xC0) >> 6]);
mImageSetPixel(image, x + 1, y, colors[(byte & 0x30) >> 4]);
mImageSetPixel(image, x + 2, y, colors[(byte & 0x0C) >> 2]);
mImageSetPixel(image, x + 3, y, colors[(byte & 0x03) >> 0]);
}
}
uint64_t* creationUsec = malloc(sizeof(*creationUsec));
if (creationUsec) {
#ifndef _MSC_VER
struct timeval tv;
if (!gettimeofday(&tv, 0)) {
uint64_t usec = tv.tv_usec;
usec += tv.tv_sec * 1000000LL;
STORE_64LE(usec, 0, creationUsec);
}
#else
struct timespec ts;
if (timespec_get(&ts, TIME_UTC)) {
uint64_t usec = ts.tv_nsec / 1000;
usec += ts.tv_sec * 1000000LL;
STORE_64LE(usec, 0, creationUsec);
}
#endif
else {
free(creationUsec);
creationUsec = 0;
}
}
const char* saveDir = 0;
environCallback(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &saveDir);
char buf[512];
snprintf(buf, 512, "%sgbprinter-%llu.png", saveDir, *creationUsec);
free(creationUsec);
bool status = mImageSave(image, buf, NULL);
mImageDestroy(image);
GBPrinterDonePrinting(printer);
}
#endif
#ifdef USE_DEVTERM
static void _printDevTerm(struct GBPrinter* printer, int height, const uint8_t* data) {
int fd = open("/tmp/DEVTERM_PRINTER_IN", O_RDWR);

/* upscale 2x */
char header[14] = {
0x1b, 'a', '1', /*align center*/
0x12, '#', 9, /*set print density*/
0x1d, 'v', '0', 'p', 0,0,0,0 /*image header*/};
header[10] = (GB_VIDEO_HORIZONTAL_PIXELS * 2 / 8) % 256;
header[11] = (GB_VIDEO_HORIZONTAL_PIXELS * 2 / 8) / 256;
header[12] = (height * 2) % 256;
header[13] = (height * 2) / 256;
write(fd, header, 14);

char line_buffer [GB_VIDEO_HORIZONTAL_PIXELS / 4];
for (int y = 0; y < height * 2; ++y) {
for (int x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
line_buffer[x/4] =
((((data[(x + 0 + y / 2 * GB_VIDEO_HORIZONTAL_PIXELS)/4] & 0xC0) >> 6) > 1) << 7) +
((((data[(x + 0 + y / 2 * GB_VIDEO_HORIZONTAL_PIXELS)/4] & 0xC0) >> 6) > 1) << 6) +
((((data[(x + 1 + y / 2 * GB_VIDEO_HORIZONTAL_PIXELS)/4] & 0x30) >> 4) > 1) << 5) +
((((data[(x + 1 + y / 2 * GB_VIDEO_HORIZONTAL_PIXELS)/4] & 0x30) >> 4) > 1) << 4) +
((((data[(x + 2 + y / 2 * GB_VIDEO_HORIZONTAL_PIXELS)/4] & 0x0C) >> 2) > 1) << 3) +
((((data[(x + 2 + y / 2 * GB_VIDEO_HORIZONTAL_PIXELS)/4] & 0x0C) >> 2) > 1) << 2) +
((((data[(x + 3 + y / 2 * GB_VIDEO_HORIZONTAL_PIXELS)/4] & 0x03) >> 0) > 1) << 1) +
((((data[(x + 3 + y / 2 * GB_VIDEO_HORIZONTAL_PIXELS)/4] & 0x03) >> 0) > 1) << 0) ;
}
write(fd, line_buffer, GB_VIDEO_HORIZONTAL_PIXELS / 4);
}
close(fd);
GBPrinterDonePrinting(printer);
}
#endif
static void _initPrinter(void) {

if (printerInitDone) {
return;
}
#ifdef M_CORE_GB
if (core->platform(core) == mPLATFORM_GB) {
struct GB* gb = core->board;
GBPrinterCreate(&gbPrinter);
#ifdef USE_DEVTERM
gbPrinter.print = _printDevTerm;
#else
#ifdef USE_PNG
gbPrinter.print = _printImage;
#endif
#endif
GBSIOSetDriver(&gb->sio, &(gbPrinter.d));
printerInitDone = true;
}
#endif
}

static void _initRumble(void) {
if (rumbleInitDone) {
Expand Down Expand Up @@ -295,6 +404,9 @@ static void _reloadSettings(void) {
}

_updateGbPal();

printerInitDone = false;
_initPrinter();
#endif

var.key = "mgba_use_bios";
Expand Down
6 changes: 5 additions & 1 deletion src/util/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ bool mImageSave(const struct mImage* image, const char* path, const char* format

#ifdef USE_PNG
bool mImageSavePNG(const struct mImage* image, struct VFile* vf) {
#ifndef COLOR_16_BIT
#ifndef COLOR_5_6_5
if (image->format != mCOLOR_XBGR8 && image->format != mCOLOR_ABGR8) {
struct mImage* newImage;
if (mColorFormatHasAlpha(image->format)) {
Expand All @@ -219,12 +221,14 @@ bool mImageSavePNG(const struct mImage* image, struct VFile* vf) {
mImageDestroy(newImage);
return ret;
}
#endif
#endif

png_structp png = PNGWriteOpen(vf);
png_infop info = NULL;
bool ok = false;
if (png) {
if (image->format == mCOLOR_XBGR8) {
if (image->format == mCOLOR_XBGR8 || (image->format == mCOLOR_RGB565 || image->format == mCOLOR_BGR565 /*valid only with COLOR_5_6_5*/)) {
info = PNGWriteHeader(png, image->width, image->height);
if (info) {
ok = PNGWritePixels(png, image->width, image->height, image->stride, image->data);
Expand Down