diff --git a/CMakeLists.txt b/CMakeLists.txt index af45bd9b689..93d7c2aa6a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() @@ -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() diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 7a6e89f5687..2b47c087acb 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -21,6 +21,9 @@ CXX_GUARD_START #ifdef USE_DEBUGGERS #include #endif +#ifdef USE_PNG +#include +#endif enum mPlatform { mPLATFORM_NONE = -1, diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 8c08e7bb621..54a2b60a917 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -18,6 +18,7 @@ #include #include #include +#include #endif #ifdef M_CORE_GBA #include @@ -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, @@ -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) { @@ -295,6 +404,9 @@ static void _reloadSettings(void) { } _updateGbPal(); + + printerInitDone = false; + _initPrinter(); #endif var.key = "mgba_use_bios"; diff --git a/src/util/image.c b/src/util/image.c index ed070f65867..05484b0fc28 100644 --- a/src/util/image.c +++ b/src/util/image.c @@ -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)) { @@ -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);