From 33cb13dee5a527ac445ea1b13d42723e4eb3e3b0 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Mon, 13 Oct 2014 21:00:47 -0400 Subject: Xworld - Another World interpreter for Rockbox Co-conspirators: Franklin Wei, Benjamin Brown -------------------------------------------------------------------- This work is based on: - Fabien Sanglard's "Fabother World" based on - Piotr Padkowski's newRaw interpreter which was based on - Gregory Montoir's reverse engineering of - Eric Chahi's assembly code -------------------------------------------------------------------- Progress: * The plugin runs pretty nicely (with sound!) on most color targets * Keymaps for color LCD targets are complete * The manual entry is finished * Grayscale/monochrome support is NOT PLANNED - the game looks horrible in grayscale! :p -------------------------------------------------------------------- Notes: * The original game strings were built-in to the executable, and were copyrighted and could not be used. * This port ships with an alternate set of strings by default, but can load the "official" strings from a file at runtime. -------------------------------------------------------------------- To be done (in descending order of importance): * vertical stride compatibility <30% done> * optimization <10% done> Change-Id: I3155b0d97c2ac470cb8a2040f40d4139ddcebfa5 Reviewed-on: http://gerrit.rockbox.org/1077 Reviewed-by: Michael Giacomelli --- apps/plugins/xworld/video.c | 1141 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1141 insertions(+) create mode 100644 apps/plugins/xworld/video.c (limited to 'apps/plugins/xworld/video.c') diff --git a/apps/plugins/xworld/video.c b/apps/plugins/xworld/video.c new file mode 100644 index 0000000000..b4182df7a7 --- /dev/null +++ b/apps/plugins/xworld/video.c @@ -0,0 +1,1141 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2014 Franklin Wei, Benjamin Brown + * Copyright (C) 2004 Gregory Montoir + * + * 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 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "video.h" +#include "video_data.h" +#include "resource.h" +#include "serializer.h" +#include "sys.h" +#include "file.h" + +void polygon_readVertices(struct Polygon* g, const uint8_t *p, uint16_t zoom) { + g->bbw = (*p++) * zoom / 64; + g->bbh = (*p++) * zoom / 64; + g->numPoints = *p++; + assert((g->numPoints & 1) == 0 && g->numPoints < MAX_POINTS); + + //Read all points, directly from bytecode segment + for (int i = 0; i < g->numPoints; ++i) { + struct Point *pt = &g->points[i]; + pt->x = (*p++) * zoom / 64; + pt->y = (*p++) * zoom / 64; + } +} + +void video_create(struct Video* v, struct Resource* res, struct System* sys) +{ + v->res = res; + v->sys = sys; +} + +void video_init(struct Video* v) { + v->paletteIdRequested = NO_PALETTE_CHANGE_REQUESTED; + v->page_data = v->res->_memPtrStart + MEM_BLOCK_SIZE; + + rb->memset(v->page_data, 0, 4 * VID_PAGE_SIZE); + + for (int i = 0; i < 4; ++i) { + v->_pagePtrs[i] = v->page_data + i * VID_PAGE_SIZE; + } + + v->_curPagePtr3 = video_getPagePtr(v, 1); + v->_curPagePtr2 = video_getPagePtr(v, 2); + + video_changePagePtr1(v, 0xFE); + + v->_interpTable[0] = 0x4000; + + for (int i = 1; i < 0x400; ++i) { + v->_interpTable[i] = 0x4000 / i; + } +} + +void video_setDataBuffer(struct Video* v, uint8_t *dataBuf, uint16_t offset) { + + v->_dataBuf = dataBuf; + v->_pData.pc = dataBuf + offset; +} + + +/* A shape can be given in two different ways: + + - A list of screenspace vertices. + - A list of objectspace vertices, based on a delta from the first vertex. + + This is a recursive function. */ +void video_readAndDrawPolygon(struct Video* v, uint8_t color, uint16_t zoom, const struct Point *pt) { + + uint8_t i = scriptPtr_fetchByte(&v->_pData); + + //This is + if (i >= 0xC0) { // 0xc0 = 192 + + // WTF ? + if (color & 0x80) { //0x80 = 128 (1000 0000) + color = i & 0x3F; //0x3F = 63 (0011 1111) + } + + // pc is misleading here since we are not reading bytecode but only + // vertices informations. + polygon_readVertices(&v->polygon, v->_pData.pc, zoom); + + video_fillPolygon(v, color, zoom, pt); + + + + } else { + i &= 0x3F; //0x3F = 63 + if (i == 1) { + warning("video_readAndDrawPolygon() ec=0x%X (i != 2)", 0xF80); + } else if (i == 2) { + video_readAndDrawPolygonHierarchy(v, zoom, pt); + + } else { + warning("video_readAndDrawPolygon() ec=0x%X (i != 2)", 0xFBB); + } + } + + + +} + +void video_fillPolygon(struct Video* v, uint16_t color, uint16_t zoom, const struct Point *pt) { + + (void) zoom; + + if (v->polygon.bbw == 0 && v->polygon.bbh == 1 && v->polygon.numPoints == 4) { + video_drawPoint(v, color, pt->x, pt->y); + + return; + } + + int16_t x1 = pt->x - v->polygon.bbw / 2; + int16_t x2 = pt->x + v->polygon.bbw / 2; + int16_t y1 = pt->y - v->polygon.bbh / 2; + int16_t y2 = pt->y + v->polygon.bbh / 2; + + if (x1 > 319 || x2 < 0 || y1 > 199 || y2 < 0) + return; + + v->_hliney = y1; + + uint16_t i, j; + i = 0; + j = v->polygon.numPoints - 1; + + x2 = v->polygon.points[i].x + x1; + x1 = v->polygon.points[j].x + x1; + + ++i; + --j; + + drawLine drawFct; + if (color < 0x10) { + drawFct = &video_drawLineN; + } else if (color > 0x10) { + drawFct = &video_drawLineP; + } else { + drawFct = &video_drawLineBlend; + } + + uint32_t cpt1 = x1 << 16; + uint32_t cpt2 = x2 << 16; + + while (1) { + v->polygon.numPoints -= 2; + if (v->polygon.numPoints == 0) { +#if TRACE_FRAMEBUFFER + video_dumpFrameBuffers(v, "fillPolygonEnd"); +#endif +#if TRACE_BG_BUFFER + video_dumpBackGroundBuffer(v); +#endif + break; + } + uint16_t h; + int32_t step1 = video_calcStep(v, &v->polygon.points[j + 1], &v->polygon.points[j], &h); + int32_t step2 = video_calcStep(v, &v->polygon.points[i - 1], &v->polygon.points[i], &h); + + ++i; + --j; + + cpt1 = (cpt1 & 0xFFFF0000) | 0x7FFF; + cpt2 = (cpt2 & 0xFFFF0000) | 0x8000; + + if (h == 0) { + cpt1 += step1; + cpt2 += step2; + } else { + for (; h != 0; --h) { + if (v->_hliney >= 0) { + x1 = cpt1 >> 16; + x2 = cpt2 >> 16; + if (x1 <= 319 && x2 >= 0) { + if (x1 < 0) x1 = 0; + if (x2 > 319) x2 = 319; + (*drawFct)(v, x1, x2, color); + } + } + cpt1 += step1; + cpt2 += step2; + ++v->_hliney; + if (v->_hliney > 199) return; + } + } + +#if TRACE_FRAMEBUFFER + video_dumpFrameBuffers(v, "fillPolygonChild"); +#endif +#if TRACE_BG_BUFFER + + video_dumpBackGroundBuffer(v); +#endif + } +} + +/* + What is read from the bytecode is not a pure screnspace polygon but a polygonspace polygon. + +*/ +void video_readAndDrawPolygonHierarchy(struct Video* v, uint16_t zoom, const struct Point *pgc) { + + struct Point pt = *pgc; + pt.x -= scriptPtr_fetchByte(&v->_pData) * zoom / 64; + pt.y -= scriptPtr_fetchByte(&v->_pData) * zoom / 64; + + int16_t childs = scriptPtr_fetchByte(&v->_pData); + debug(DBG_VIDEO, "video_readAndDrawPolygonHierarchy childs=%d", childs); + + for ( ; childs >= 0; --childs) { + + uint16_t off = scriptPtr_fetchWord(&v->_pData); + + struct Point po; + po = pt; + po.x += scriptPtr_fetchByte(&v->_pData) * zoom / 64; + po.y += scriptPtr_fetchByte(&v->_pData) * zoom / 64; + + uint16_t color = 0xFF; + uint16_t _bp = off; + off &= 0x7FFF; + + if (_bp & 0x8000) { + color = *v->_pData.pc & 0x7F; + v->_pData.pc += 2; + } + + uint8_t *bak = v->_pData.pc; + v->_pData.pc = v->_dataBuf + off * 2; + + + video_readAndDrawPolygon(v, color, zoom, &po); + + + v->_pData.pc = bak; + } + + +} + +int32_t video_calcStep(struct Video* v, const struct Point *p1, const struct Point *p2, uint16_t *dy) { + (void) v; + *dy = p2->y - p1->y; + return (p2->x - p1->x) * v->_interpTable[*dy] * 4; +} + +void video_drawString(struct Video* v, uint8_t color, uint16_t x, uint16_t y, uint16_t stringId) { + + const struct StrEntry *se = video_stringsTableEng; + + //Search for the location where the string is located. + while (se->id != END_OF_STRING_DICTIONARY && se->id != stringId) + ++se; + + debug(DBG_VIDEO, "video_drawString(%d, %d, %d, '%s')", color, x, y, se->str); + + //Not found + if (se->id == END_OF_STRING_DICTIONARY) + return; + + + //Used if the string contains a return carriage. + uint16_t xOrigin = x; + int len = rb->strlen(se->str); + for (int i = 0; i < len; ++i) { + + if (se->str[i] == '\n') { + y += 8; + x = xOrigin; + continue; + } + + video_drawChar(v, se->str[i], x, y, color, v->_curPagePtr1); + x++; + + } +} + +void video_drawChar(struct Video* v, uint8_t character, uint16_t x, uint16_t y, uint8_t color, uint8_t *buf) { + (void) v; + if (x <= 39 && y <= 192) { + + /* each character is 8x8 */ + const uint8_t *ft = video_font + (character - ' ') * 8; + + /* x is multiplied by 4 and not 8 because there are two pixels per byte */ + uint8_t *p = buf + x * 4 + y * 160; + + for (int j = 0; j < 8; ++j) { + uint8_t ch = *(ft + j); + for (int i = 0; i < 4; ++i) { + uint8_t b = *(p + i); + uint8_t cmask = 0xFF; + uint8_t colb = 0; + if (ch & 0x80) { + colb |= color << 4; + cmask &= 0x0F; + } + ch <<= 1; + if (ch & 0x80) { + colb |= color; + cmask &= 0xF0; + } + ch <<= 1; + *(p + i) = (b & cmask) | colb; + } + /* skip to the next line (320 pixels = 160 bytes) */ + p += 160; + } + } +} + +void video_drawPoint(struct Video* v, uint8_t color, int16_t x, int16_t y) { + debug(DBG_VIDEO, "drawPoint(%d, %d, %d)", color, x, y); + if (x >= 0 && x <= 319 && y >= 0 && y <= 199) { + uint16_t off = y * 160 + x / 2; + + uint8_t cmasko, cmaskn; + if (x & 1) { + cmaskn = 0x0F; + cmasko = 0xF0; + } else { + cmaskn = 0xF0; + cmasko = 0x0F; + } + + uint8_t colb = (color << 4) | color; + if (color == 0x10) { + cmaskn &= 0x88; + cmasko = ~cmaskn; + colb = 0x88; + } else if (color == 0x11) { + colb = *(v->_pagePtrs[0] + off); + } + uint8_t b = *(v->_curPagePtr1 + off); + *(v->_curPagePtr1 + off) = (b & cmasko) | (colb & cmaskn); + } +} + +/* Blend a line in the current framebuffer (v->_curPagePtr1) + */ +void video_drawLineBlend(struct Video* v, int16_t x1, int16_t x2, uint8_t color) { + /* silence warnings without XWORLD_DEBUG */ + (void) color; + debug(DBG_VIDEO, "drawLineBlend(%d, %d, %d)", x1, x2, color); + int16_t xmax = MAX(x1, x2); + int16_t xmin = MIN(x1, x2); + uint8_t *p = v->_curPagePtr1 + v->_hliney * 160 + xmin / 2; + + uint16_t w = xmax / 2 - xmin / 2 + 1; + uint8_t cmaske = 0; + uint8_t cmasks = 0; + if (xmin & 1) { + --w; + cmasks = 0xF7; + } + if (!(xmax & 1)) { + --w; + cmaske = 0x7F; + } + + if (cmasks != 0) { + *p = (*p & cmasks) | 0x08; + ++p; + } + while (w--) { + *p = (*p & 0x77) | 0x88; + ++p; + } + if (cmaske != 0) { + *p = (*p & cmaske) | 0x80; + ++p; + } + + +} + +void video_drawLineN(struct Video* v, int16_t x1, int16_t x2, uint8_t color) { + debug(DBG_VIDEO, "drawLineN(%d, %d, %d)", x1, x2, color); + int16_t xmax = MAX(x1, x2); + int16_t xmin = MIN(x1, x2); + uint8_t *p = v->_curPagePtr1 + v->_hliney * 160 + xmin / 2; + + uint16_t w = xmax / 2 - xmin / 2 + 1; + uint8_t cmaske = 0; + uint8_t cmasks = 0; + if (xmin & 1) { + --w; + cmasks = 0xF0; + } + if (!(xmax & 1)) { + --w; + cmaske = 0x0F; + } + + uint8_t colb = ((color & 0xF) << 4) | (color & 0xF); + if (cmasks != 0) { + *p = (*p & cmasks) | (colb & 0x0F); + ++p; + } + while (w--) { + *p++ = colb; + } + if (cmaske != 0) { + *p = (*p & cmaske) | (colb & 0xF0); + ++p; + } + + +} + +void video_drawLineP(struct Video* v, int16_t x1, int16_t x2, uint8_t color) { + /* silence warnings without XWORLD_DEBUG */ + (void) color; + debug(DBG_VIDEO, "drawLineP(%d, %d, %d)", x1, x2, color); + int16_t xmax = MAX(x1, x2); + int16_t xmin = MIN(x1, x2); + uint16_t off = v->_hliney * 160 + xmin / 2; + uint8_t *p = v->_curPagePtr1 + off; + uint8_t *q = v->_pagePtrs[0] + off; + + uint8_t w = xmax / 2 - xmin / 2 + 1; + uint8_t cmaske = 0; + uint8_t cmasks = 0; + if (xmin & 1) { + --w; + cmasks = 0xF0; + } + if (!(xmax & 1)) { + --w; + cmaske = 0x0F; + } + + if (cmasks != 0) { + *p = (*p & cmasks) | (*q & 0x0F); + ++p; + ++q; + } + while (w--) { + *p++ = *q++; + } + if (cmaske != 0) { + *p = (*p & cmaske) | (*q & 0xF0); + ++p; + ++q; + } + +} + +uint8_t *video_getPagePtr(struct Video* v, uint8_t page) { + uint8_t *p; + if (page <= 3) { + p = v->_pagePtrs[page]; + } else { + switch (page) { + case 0xFF: + p = v->_curPagePtr3; + break; + case 0xFE: + p = v->_curPagePtr2; + break; + default: + p = v->_pagePtrs[0]; // XXX check + warning("video_getPagePtr() p != [0,1,2,3,0xFF,0xFE] == 0x%X", page); + break; + } + } + return p; +} + + + +void video_changePagePtr1(struct Video* v, uint8_t page) { + debug(DBG_VIDEO, "video_changePagePtr1(%d)", page); + v->_curPagePtr1 = video_getPagePtr(v, page); +} + + + +void video_fillPage(struct Video* v, uint8_t pageId, uint8_t color) { + debug(DBG_VIDEO, "video_fillPage(%d, %d)", pageId, color); + uint8_t *p = video_getPagePtr(v, pageId); + + // Since a palette indice is coded on 4 bits, we need to duplicate the + // clearing color to the upper part of the byte. + uint8_t c = (color << 4) | color; + + rb->memset(p, c, VID_PAGE_SIZE); + +#if TRACE_FRAMEBUFFER + video_dumpFrameBuffers(v, "-fillPage"); +#endif +#if TRACE_BG_BUFFER + + video_dumpBackGroundBuffer(v); +#endif +} + + + + + +#if TRACE_FRAMEBUFFER +#define SCREENSHOT_BPP 3 +int traceFrameBufferCounter = 0; +uint8_t allFrameBuffers[640 * 400 * SCREENSHOT_BPP]; + +#endif + + + + + + +/* This opcode is used once the background of a scene has been drawn in one of the framebuffer: + it is copied in the current framebuffer at the start of a new frame in order to improve performances. */ +void video_copyPage(struct Video* v, uint8_t srcPageId, uint8_t dstPageId, int16_t vscroll) { + + debug(DBG_VIDEO, "video_copyPage(%d, %d)", srcPageId, dstPageId); + + if (srcPageId == dstPageId) + return; + + uint8_t *p; + uint8_t *q; + + if (srcPageId >= 0xFE || !((srcPageId &= 0xBF) & 0x80)) { + p = video_getPagePtr(v, srcPageId); + q = video_getPagePtr(v, dstPageId); + memcpy(q, p, VID_PAGE_SIZE); + + } else { + p = video_getPagePtr(v, srcPageId & 3); + q = video_getPagePtr(v, dstPageId); + if (vscroll >= -199 && vscroll <= 199) { + uint16_t h = 200; + if (vscroll < 0) { + h += vscroll; + p += -vscroll * 160; + } else { + h -= vscroll; + q += vscroll * 160; + } + memcpy(q, p, h * 160); + } + } + + +#if TRACE_FRAMEBUFFER + char name[256]; + rb->memset(name, 0, sizeof(name)); + sprintf(name, "copyPage_0x%X_to_0x%X", (p - v->_pagePtrs[0]) / VID_PAGE_SIZE, (q - v->_pagePtrs[0]) / VID_PAGE_SIZE); + dumpFrameBuffers(name); +#endif +} + + + + +void video_copyPagePtr(struct Video* v, const uint8_t *src) { + debug(DBG_VIDEO, "video_copyPagePtr()"); + uint8_t *dst = v->_pagePtrs[0]; + int h = 200; + while (h--) { + int w = 40; + while (w--) { + uint8_t p[] = { + *(src + 8000 * 3), + *(src + 8000 * 2), + *(src + 8000 * 1), + *(src + 8000 * 0) + }; + for(int j = 0; j < 4; ++j) { + uint8_t acc = 0; + for (int i = 0; i < 8; ++i) { + acc <<= 1; + acc |= (p[i & 3] & 0x80) ? 1 : 0; + p[i & 3] <<= 1; + } + *dst++ = acc; + } + ++src; + } + } + + +} + +/* + uint8_t *video_allocPage() { + uint8_t *buf = (uint8_t *)malloc(VID_PAGE_SIZE); + rb->memset(buf, 0, VID_PAGE_SIZE); + return buf; + } +*/ + + + +#if TRACE_FRAMEBUFFER +int dumpPaletteCursor = 0; +#endif + +/* + Note: The palettes set used to be allocated on the stack but I moved it to + the heap so I could dump the four framebuffer and follow how + frames are generated. +*/ +uint8_t pal[NUM_COLORS * 3]; //3 = BYTES_PER_PIXEL +void video_changePal(struct Video* v, uint8_t palNum) { + debug(DBG_VIDEO, "video_changePal(v=0x%08x, palNum=%d", v, palNum); + if (palNum >= 32) + return; + + uint8_t *p = v->res->segPalettes + palNum * 32; //colors are coded on 2bytes (565) for 16 colors = 32 + debug(DBG_VIDEO, "segPalettes: 0x%08x", v->res->segPalettes); + // Moved to the heap, legacy code used to allocate the palette + // on the stack. + //uint8_t pal[NUM_COLORS * 3]; //3 = BYTES_PER_PIXEL + + for (int i = 0; i < NUM_COLORS; ++i) + { + debug(DBG_VIDEO, "i: %d", i); + debug(DBG_VIDEO, "p: 0x%08x", p); + uint8_t c1 = *(p + 0); + uint8_t c2 = *(p + 1); + p += 2; + pal[i * 3 + 0] = ((c1 & 0x0F) << 2) | ((c1 & 0x0F) >> 2); // r + pal[i * 3 + 1] = ((c2 & 0xF0) >> 2) | ((c2 & 0xF0) >> 6); // g + pal[i * 3 + 2] = ((c2 & 0x0F) >> 2) | ((c2 & 0x0F) << 2); // b + } + + sys_setPalette(v->sys, 0, NUM_COLORS, pal); + v->currentPaletteId = palNum; + + +#if TRACE_PALETTE + printf("\nuint8_t dumpPalette[48] = {\n"); + for (int i = 0; i < NUM_COLORS; ++i) + { + printf("0x%X,0x%X,0x%X,", pal[i * 3 + 0], pal[i * 3 + 1], pal[i * 3 + 2]); + } + printf("\n};\n"); +#endif + + +#if TRACE_FRAMEBUFFER + dumpPaletteCursor++; +#endif +} + +void video_updateDisplay(struct Video* v, uint8_t pageId) { + + debug(DBG_VIDEO, "video_updateDisplay(%d)", pageId); + + if (pageId != 0xFE) { + if (pageId == 0xFF) { + /* swap ptrs 2 and 3 */ + uint8_t* temp = v->_curPagePtr3; + v->_curPagePtr3 = v->_curPagePtr2; + v->_curPagePtr2 = temp; + } else { + v->_curPagePtr2 = video_getPagePtr(v, pageId); + } + } + + //Check if we need to change the palette + if (v->paletteIdRequested != NO_PALETTE_CHANGE_REQUESTED) { + video_changePal(v, v->paletteIdRequested); + v->paletteIdRequested = NO_PALETTE_CHANGE_REQUESTED; + } + + //Q: Why 160 ? + //A: Because one byte gives two palette indices so + // we only need to move 320/2 per line. + sys_copyRect(v->sys, 0, 0, 320, 200, v->_curPagePtr2, 160); + +#if TRACE_FRAMEBUFFER + dumpFrameBuffer(v->_curPagePtr2, allFrameBuffers, 320, 200); +#endif +} + +void video_saveOrLoad(struct Video* v, struct Serializer *ser) { + uint8_t mask = 0; + if (ser->_mode == SM_SAVE) { + for (int i = 0; i < 4; ++i) { + if (v->_pagePtrs[i] == v->_curPagePtr1) + mask |= i << 4; + if (v->_pagePtrs[i] == v->_curPagePtr2) + mask |= i << 2; + if (v->_pagePtrs[i] == v->_curPagePtr3) + mask |= i << 0; + } + } + struct Entry entries[] = { + SE_INT(&v->currentPaletteId, SES_INT8, VER(1)), + SE_INT(&v->paletteIdRequested, SES_INT8, VER(1)), + SE_INT(&mask, SES_INT8, VER(1)), + SE_ARRAY(v->_pagePtrs[0], VID_PAGE_SIZE, SES_INT8, VER(1)), + SE_ARRAY(v->_pagePtrs[1], VID_PAGE_SIZE, SES_INT8, VER(1)), + SE_ARRAY(v->_pagePtrs[2], VID_PAGE_SIZE, SES_INT8, VER(1)), + SE_ARRAY(v->_pagePtrs[3], VID_PAGE_SIZE, SES_INT8, VER(1)), + SE_END() + }; + ser_saveOrLoadEntries(ser, entries); + + if (ser->_mode == SM_LOAD) { + v->_curPagePtr1 = v->_pagePtrs[(mask >> 4) & 0x3]; + v->_curPagePtr2 = v->_pagePtrs[(mask >> 2) & 0x3]; + v->_curPagePtr3 = v->_pagePtrs[(mask >> 0) & 0x3]; + video_changePal(v, v->currentPaletteId); + } +} + + + +#if TRACE_FRAMEBUFFER + + +uint8_t allPalettesDump[][48] = { + + + { + 0x4, 0x4, 0x4, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x2E, 0x22, 0x0, 0x3F, 0x0, 0x0, 0x33, 0x26, 0x0, 0x37, 0x2A, 0x0, 0x3B, 0x33, 0x0, 0x3F, 0x3B, 0x0, 0x3F, 0x3F, 0x1D, 0x3F, 0x3F, 0x2A, + }, + + { + 0x4, 0x4, 0x4, 0xC, 0xC, 0x11, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x2E, 0x22, 0x0, 0x15, 0x11, 0x11, 0x33, 0x26, 0x0, 0x37, 0x2A, 0x0, 0x3B, 0x33, 0x0, 0x3F, 0x3B, 0x0, 0x3F, 0x3F, 0x1D, 0x3F, 0x3F, 0x2A, + } + + , { + 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x11, 0x11, 0x15, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A, + } + + , { + 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x11, 0x11, 0x15, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A, + } + + , { + 0x0, 0x0, 0x0, 0x1D, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x15, 0x11, 0x19, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A, + } + + , { + 0x0, 0x4, 0x8, 0x15, 0x1D, 0x1D, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0xC, 0x19, 0x22, 0x11, 0x1D, 0x26, 0x8, 0x8, 0x8, 0x0, 0x0, 0x0, 0x2E, 0x2E, 0x2E, 0xC, 0xC, 0xC, 0x15, 0xC, 0x15, 0xC, 0x15, 0x15, 0x11, 0x19, 0x19, 0x1D, 0x26, 0x26, + } + + , { + 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x33, 0x26, 0x0, 0x3B, 0x33, 0x11, + } + + , { + 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x26, 0x15, 0x0, 0x26, 0x1D, 0x0, + } + + , { + 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x26, 0x1D, 0x0, + } + + , { + 0x0, 0x0, 0x0, 0x8, 0x4, 0xC, 0x15, 0xC, 0x11, 0x1D, 0x11, 0x0, 0xC, 0x8, 0x11, 0x2E, 0x1D, 0x0, 0x37, 0x26, 0x8, 0x3F, 0x2E, 0x0, 0x0, 0x0, 0x0, 0x11, 0xC, 0x15, 0x26, 0x15, 0x0, 0x15, 0x11, 0x19, 0x1D, 0x15, 0x1D, 0x26, 0x19, 0x19, 0x0, 0x0, 0x0, 0x3F, 0x3F, 0x3F, + } + + , { + 0x0, 0x0, 0x0, 0x8, 0x4, 0xC, 0x37, 0x1D, 0x1D, 0x3B, 0x2A, 0x22, 0x11, 0xC, 0x15, 0x2A, 0x0, 0x0, 0x33, 0x11, 0x0, 0x3F, 0x33, 0x1D, 0x3B, 0x19, 0x0, 0x11, 0x11, 0x19, 0x19, 0x15, 0x1D, 0x22, 0x19, 0x22, 0x2A, 0x1D, 0x26, 0x33, 0x22, 0x26, 0x37, 0x26, 0x22, 0x1D, 0x37, 0x3F, + } + + , { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D, 0x4, 0x8, 0xC, 0x2A, 0x1D, 0xC, 0x3F, 0x3B, 0x26, 0x3B, 0x2A, 0x11, 0x2A, 0x0, 0x0, 0x0, 0x11, 0x15, 0x2A, 0x1D, 0x26, 0xC, 0x8, 0xC, 0x8, 0x15, 0x1D, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x22, 0x0, 0x0, 0x3F, 0x33, 0x1D, + } + + , { + 0x0, 0x0, 0x0, 0x0, 0x15, 0x0, 0x4, 0x8, 0xC, 0x0, 0xC, 0x11, 0x8, 0x11, 0x19, 0x0, 0x19, 0x22, 0x2A, 0x0, 0x0, 0x19, 0x15, 0x22, 0x2E, 0x26, 0x2E, 0xC, 0x8, 0xC, 0x0, 0x2E, 0x0, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x1D, 0x0, 0x0, 0x3F, 0x3F, 0x19, + } + + , { + 0x0, 0x0, 0x0, 0x8, 0xC, 0x11, 0x11, 0x11, 0x15, 0x19, 0x15, 0x22, 0x26, 0x19, 0x2A, 0x2E, 0x26, 0x2E, 0x4, 0x4, 0xC, 0x0, 0xC, 0x15, 0x2A, 0x1D, 0x26, 0x0, 0x19, 0x0, 0x0, 0x2A, 0x0, 0x37, 0x26, 0x22, 0x0, 0x15, 0x1D, 0x37, 0x2E, 0x1D, 0x3F, 0x3F, 0x2E, 0x37, 0x37, 0x26, + } + + , { + 0x0, 0x0, 0x0, 0x0, 0x15, 0x0, 0x4, 0x8, 0xC, 0x0, 0xC, 0x11, 0x8, 0x11, 0x19, 0x0, 0x19, 0x22, 0x2A, 0x0, 0x0, 0x19, 0x15, 0x22, 0x2E, 0x26, 0x2E, 0xC, 0x8, 0xC, 0x0, 0x2E, 0x0, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x1D, 0x0, 0x0, 0x3F, 0x3F, 0x19, + } + + , { + 0x0, 0x0, 0x0, 0x0, 0xC, 0x0, 0x8, 0x0, 0x4, 0xC, 0x8, 0xC, 0x19, 0x11, 0x11, 0x3F, 0x3F, 0x19, 0x0, 0x1D, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x4, 0x8, 0x11, 0x19, 0x11, 0x19, 0x0, 0x0, 0x2E, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x37, 0x1D, 0x15, 0x0, 0x11, 0x11, 0x0, 0x3F, 0x2A, + } + + , { + 0x0, 0x0, 0x0, 0x0, 0xC, 0x0, 0x0, 0x11, 0x0, 0x0, 0x19, 0x0, 0xC, 0x26, 0x0, 0x15, 0x2E, 0x0, 0x4, 0x8, 0x0, 0x26, 0x11, 0x0, 0x2E, 0x3F, 0x0, 0x0, 0x15, 0x0, 0xC, 0x1D, 0x0, 0x15, 0x2E, 0x0, 0x15, 0x37, 0x0, 0x1D, 0x3F, 0x0, 0xC, 0x1D, 0xC, 0x\ + 1D, 0x26, 0x15, + } +}; + + + +#include "png.h" +int GL_FCS_SaveAsSpecifiedPNG(char* path, uint8_t* pixels, int depth = 8, int format = PNG_COLOR_TYPE_RGB) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_byte ** row_pointers = NULL; + int status = -1; + int bytePerPixel = 0; + int y; + + int fd = open (path, "wb"); + if (fd < 0) { + goto fopen_failed; + } + + png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + goto png_create_write_struct_failed; + } + + info_ptr = png_create_info_struct (png_ptr); + if (info_ptr == NULL) { + goto png_create_info_struct_failed; + } + + if (setjmp (png_jmpbuf (png_ptr))) { + goto png_failure; + } + + /* Set image attributes. */ + + png_set_IHDR (png_ptr, + info_ptr, + 640, + 400, + depth, + format, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + if (format == PNG_COLOR_TYPE_GRAY ) + bytePerPixel = depth / 8 * 1; + else + bytePerPixel = depth / 8 * 3; + + row_pointers = (png_byte **)png_malloc (png_ptr, 400 * sizeof (png_byte *)); + //for (y = vid.height-1; y >=0; --y) + for (y = 0; y < 400; y++) + { + row_pointers[y] = (png_byte*)&pixels[640 * (400 - y) * bytePerPixel]; + } + + png_init_io (png_ptr, fp); + png_set_rows (png_ptr, info_ptr, row_pointers); + png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + //png_read_image (png_ptr, info_ptr);// + + status = 0; + + png_free (png_ptr, row_pointers); + + +png_failure: +png_create_info_struct_failed: + png_destroy_write_struct (&png_ptr, &info_ptr); + +png_create_write_struct_failed: + fclose (fp); +fopen_failed: + return status; +} + +void writeLine(uint8_t *dst, uint8_t *src, int size) +{ + uint8_t* dumpPalette; + + if (!dumpPaletteCursor) + dumpPalette = allPalettesDump[dumpPaletteCursor]; + else + dumpPalette = pal; + + for( uint8_t twoPixels = 0 ; twoPixels < size ; twoPixels++) + { + int pixelIndex0 = (*src & 0xF0) >> 4; + pixelIndex0 &= 0x10 - 1; + + int pixelIndex1 = (*src & 0xF); + pixelIndex1 &= 0x10 - 1; + + //We need to write those two pixels + dst[0] = dumpPalette[pixelIndex0 * 3] << 2 | dumpPalette[pixelIndex0 * 3]; + dst[1] = dumpPalette[pixelIndex0 * 3 + 1] << 2 | dumpPalette[pixelIndex0 * 3 + 1]; + dst[2] = dumpPalette[pixelIndex0 * 3 + 2] << 2 | dumpPalette[pixelIndex0 * 3 + 2]; + //dst[3] = 0xFF; + dst += SCREENSHOT_BPP; + + dst[0] = dumpPalette[pixelIndex1 * 3] << 2 | dumpPalette[pixelIndex1 * 3]; + dst[1] = dumpPalette[pixelIndex1 * 3 + 1] << 2 | dumpPalette[pixelIndex1 * 3 + 1]; + dst[2] = dumpPalette[pixelIndex1 * 3 + 2] << 2 | dumpPalette[pixelIndex1 * 3 + 2]; + //dst[3] = 0xFF; + dst += SCREENSHOT_BPP; + + src++; + } +} + +void video_dumpFrameBuffer(uint8_t *src, uint8_t *dst, int x, int y) +{ + + for (int line = 199 ; line >= 0 ; line--) + { + writeLine(dst + x * SCREENSHOT_BPP + y * 640 * SCREENSHOT_BPP , src + line * 160, 160); + dst += 640 * SCREENSHOT_BPP; + } +} + +void video_dumpFrameBuffers(char* comment) +{ + + if (!traceFrameBufferCounter) + { + rb->memset(allFrameBuffers, 0, sizeof(allFrameBuffers)); + } + + + dumpFrameBuffer(v->_pagePtrs[1], allFrameBuffers, 0, 0); + dumpFrameBuffer(v->_pagePtrs[0], allFrameBuffers, 0, 200); + dumpFrameBuffer(v->_pagePtrs[2], allFrameBuffers, 320, 0); + //dumpFrameBuffer(v->_pagePtrs[3],allFrameBuffers,320,200); + + + //if (v->_curPagePtr1 == v->_pagePtrs[3]) + // + + /* + uint8_t* offScreen = sys->getOffScreenFramebuffer(); + for(int i=0 ; i < 200 ; i++) + writeLine(allFrameBuffers+320*3+640*i*3 + 200*640*3, offScreen+320*i/2 , 160); + */ + + + int frameId = traceFrameBufferCounter++; + //Write bitmap to disk. + + + + // Filling TGA header information + /* + char path[256]; + sprintf(path,"test%d.tga",traceFrameBufferCounter); + + #define IMAGE_WIDTH 640 + #define IMAGE_HEIGHT 400 + + uint8_t tga_header[18]; + rb->memset(tga_header, 0, 18); + tga_header[2] = 2; + tga_header[12] = (IMAGE_WIDTH & 0x00FF); + tga_header[13] = (IMAGE_WIDTH & 0xFF00) / 256; + tga_header[14] = (IMAGE_HEIGHT & 0x00FF) ; + tga_header[15] =(IMAGE_HEIGHT & 0xFF00) / 256; + tga_header[16] = 32 ; + + + + // Open the file, write both header and payload, close, done. + char path[256]; + sprintf(path,"test%d.tga",traceFrameBufferCounter); + FILE* pScreenshot = fopen(path, "wb"); + fwrite(&tga_header, 18, sizeof(uint8_t), pScreenshot); + fwrite(allFrameBuffers, IMAGE_WIDTH * IMAGE_HEIGHT,SCREENSHOT_BPP * sizeof(uint8_t),pScreenshot); + fclose(pScreenshot); + */ + + + char path[256]; + //sprintf(path,"%4d%s.png",traceFrameBufferCounter,comment); + sprintf(path, "%4d.png", traceFrameBufferCounter); + + GL_FCS_SaveAsSpecifiedPNG(path, allFrameBuffers); +} +#endif + +#if TRACE_BG_BUFFER + + + +uint8_t bgPalette[48] = { + 0x8, 0x8, 0xC, 0xC, 0xC, 0x15, 0xC, 0x11, 0x1D, 0x15, 0x2A, 0x3F, 0x1D, 0x19, 0x19, 0x37, 0x2E, 0x2A, 0x26, 0x1D, 0x1D, 0x37, 0x26, 0x22, 0x22, 0xC, 0x0, 0x26, 0x33, 0x3F, 0x11, 0x11, 0x15, 0x11, 0x15, 0x1D, 0x15, 0x19, 0x26, 0x15, 0x1D, 0x37, 0x0, 0x26, 0x3F, 0x2E, 0x15, 0x0, +}; +void bgWriteLine(uint8_t *dst, uint8_t *src, int size) +{ + uint8_t* dumpPalette; + +// if (!dumpPaletteCursor) + // dumpPalette = allPalettesDump[dumpPaletteCursor]; +// else + dumpPalette = bgPalette; + + for( uint8_t twoPixels = 0 ; twoPixels < size ; twoPixels++) + { + int pixelIndex0 = (*src & 0xF0) >> 4; + pixelIndex0 &= 0x10 - 1; + + int pixelIndex1 = (*src & 0xF); + pixelIndex1 &= 0x10 - 1; + + //We need to write those two pixels + dst[0] = dumpPalette[pixelIndex0 * 3] << 2 | dumpPalette[pixelIndex0 * 3]; + dst[1] = dumpPalette[pixelIndex0 * 3 + 1] << 2 | dumpPalette[pixelIndex0 * 3 + 1]; + dst[2] = dumpPalette[pixelIndex0 * 3 + 2] << 2 | dumpPalette[pixelIndex0 * 3 + 2]; + //dst[3] = 0xFF; + dst += 3; + + dst[0] = dumpPalette[pixelIndex1 * 3] << 2 | dumpPalette[pixelIndex1 * 3]; + dst[1] = dumpPalette[pixelIndex1 * 3 + 1] << 2 | dumpPalette[pixelIndex1 * 3 + 1]; + dst[2] = dumpPalette[pixelIndex1 * 3 + 2] << 2 | dumpPalette[pixelIndex1 * 3 + 2]; + //dst[3] = 0xFF; + dst += 3; + + src++; + } +} + +void bgDumpFrameBuffer(uint8_t *src, uint8_t *dst, int x, int y) +{ + + for (int line = 199 ; line >= 0 ; line--) + { + bgWriteLine(dst + x * 3 + y * 320 * 3 , src + line * 160, 160); + dst += 320 * 3; + } +} + +#include "png.h" +int bgSaveAsSpecifiedPNG(char* path, uint8_t* pixels, int depth = 8, int format = PNG_COLOR_TYPE_RGB) +{ +#if 0 + FILE * fp; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_byte ** row_pointers = NULL; + int status = -1; + int bytePerPixel = 0; + int y; + + fp = fopen (path, "wb"); + if (! fp) { + goto fopen_failed; + } + + png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + goto png_create_write_struct_failed; + } + + info_ptr = png_create_info_struct (png_ptr); + if (info_ptr == NULL) { + goto png_create_info_struct_failed; + } + + if (setjmp (png_jmpbuf (png_ptr))) { + goto png_failure; + } + + /* Set image attributes. */ + + png_set_IHDR (png_ptr, + info_ptr, + 320, + 200, + depth, + format, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + if (format == PNG_COLOR_TYPE_GRAY ) + bytePerPixel = depth / 8 * 1; + else + bytePerPixel = depth / 8 * 3; + + row_pointers = (png_byte **)png_malloc (png_ptr, 200 * sizeof (png_byte *)); + //for (y = vid.height-1; y >=0; --y) + for (y = 0; y < 200; y++) + { + row_pointers[y] = (png_byte*)&pixels[320 * (200 - y) * bytePerPixel]; + } + + png_init_io (png_ptr, fp); + png_set_rows (png_ptr, info_ptr, row_pointers); + png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + //png_read_image (png_ptr, info_ptr);// + + status = 0; + + png_free (png_ptr, row_pointers); + + +png_failure: +png_create_info_struct_failed: + png_destroy_write_struct (&png_ptr, &info_ptr); + +png_create_write_struct_failed: + fclose (fp); +fopen_failed: + return status; +#endif +} + +int bgFrameBufferCounter = 0; + +void video_dumpBackGroundBuffer() +{ + if (v->_curPagePtr1 != v->_pagePtrs[0]) + return; + + uint8_t bgBuffer[320 * 200 * 3]; + bgDumpFrameBuffer(v->_curPagePtr1, bgBuffer, 0, 0); + + + char path[256]; + //sprintf(path,"%4d%s.png",traceFrameBufferCounter,comment); + sprintf(path, "bg%4d.png", bgFrameBufferCounter++); + + bgSaveAsSpecifiedPNG(path, bgBuffer); +} + +#endif -- cgit v1.2.3