Files
SyncHome/trunk/Arduino/libraries/Gamebuino_Classic/examples/4.Utilities/Loader/Loader.ino
2023-03-17 11:59:21 +00:00

311 lines
9.9 KiB
C++

const int chipSelect = 10;
#include <SdFat.h> // requires sdfat, get it at https://github.com/greiman/SdFat
#include <EEPROM.h>
#include <avr/pgmspace.h>
#include <SPI.h>
#include <Gamebuino.h>
Gamebuino gb;
SdFat sd;
SdFile file;
extern const byte logo[] PROGMEM;
const byte logo[] PROGMEM = {64,36,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xF8,0xFF,0xFF,0xFC,0x0,0x0,0x0,0x3,0xF9,0xFF,0xFF,0xFE,0x0,0x0,0x0,0x7,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x7,0xFF,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0xF,0xFF,0x3,0x30,0xC7,0x1F,0x0,0x0,0x1C,0x6,0x1,0x20,0x82,0xF,0x0,0x0,0x1C,0x6,0x49,0xE7,0x92,0x4F,0x0,0x0,0x1F,0xFE,0x49,0x27,0x9E,0x4F,0x0,0x0,0x1C,0x6,0x49,0x27,0x9E,0x4F,0x0,0x0,0x1C,0x6,0x79,0x20,0x9E,0xF,0x0,0x0,0x1F,0xFE,0x79,0x30,0x9F,0x1F,0x0,0x0,0x1C,0x6,0x79,0xFF,0xFF,0xFF,0x0,0x0,0x1C,0x7,0xFF,0x0,0x41,0xBF,0x0,0x0,0x1F,0xFF,0xFC,0x0,0x46,0x6F,0x0,0x0,0x18,0x7,0xF8,0x7F,0xFD,0x87,0x0,0x0,0x18,0x7,0xFC,0xF,0xFE,0x3,0x0,0x0,0x1F,0xFF,0xFE,0x3,0xFE,0x3,0x0,0x0,0x1C,0x7,0xFF,0xC1,0x80,0x7,0x0,0x0,0x1C,0x7,0xF0,0x1,0x80,0x1F,0x0,0x0,0x1F,0xFF,0xE0,0x3,0x0,0x7F,0x0,0x0,0x18,0x7,0xE0,0xF,0x7,0xFF,0x0,0x0,0x18,0x7,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x1C,0x7,0xFF,0x7F,0xFB,0xFF,0x0,0x0,0x1C,0x7,0xFF,0x77,0x33,0x73,0x0,0x0,0x1F,0xFF,0xFF,0x6A,0xAA,0xAF,0x0,0x0,0x1C,0x7,0xFF,0x6A,0xAA,0x6F,0x0,0x0,0x1C,0x7,0xFF,0xB7,0x33,0x2F,0x0,0x0,0xF,0xFF,0xFF,0xFF,0xFF,0xFE,0x0,0x0,0x7,0xFF,0xFF,0xFF,0xFF,0xFC,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
const byte floppy8x8[] PROGMEM = {8, 8, 0xCA, 0xCB, 0xC3, 0xFF, 0x81, 0x81, 0x81, 0xFF,};
char nextGameName[9] = "\0\0\0\0\0\0\0\0";
byte initres;
byte res;
int numberOfFiles;
int numberOfPages;
int selectedFile;
int selectedPage = 0;
int prevSelectedPage = 0;
#define PAGE_W 4
#define PAGE_H 2
#define PAGELENGTH (PAGE_W*PAGE_H)
#define TOKENSIZE 5
char token[TOKENSIZE + 1] = "gbinf";
#define NAMELENGTH 18
#define ICON_W 19
#define ICON_BYTEW ((ICON_W + 7) / 8)
#define ICON_H 18
#define ICON_LENGTH (ICON_BYTEW * ICON_H)
#define SLIDE_W 84
#define SLIDE_BYTEW ((SLIDE_W + 7) / 8)
#define SLIDE_H 32
#define SLIDE_LENGTH (SLIDE_BYTEW * SLIDE_H)
#define VERSIONOFFSET (TOKENSIZE + 1)
#define NAMEOFFSET (VERSIONOFFSET + 1)
#define ICONOFFSET (NAMEOFFSET + NAMELENGTH + 1)
#define SLIDENUMBEROFFSET ICONOFFSET + ICON_LENGTH
#define SLIDEOFFSET (SLIDENUMBEROFFSET + 1)
#define HEADERSIZE ((TOKENSIZE+1) + 1 + (NAMELENGTH+1) + ICON_LENGTH + 1)
char thisPageFiles[PAGELENGTH][9];
uint16_t thisPageClusters[PAGELENGTH];
byte cursorPos = 0;
byte oldCursorPos;
byte filesOnPage;
char completeName[13] = "xxxxxxxx.xxx";
char halfName[5] = "XXXX";
char fileExt[4] = "\0\0\0";
#define BUFFER_SIZE 128
char buffer[BUFFER_SIZE + 4];
void setup() {
//Serial.begin(115200);
gb.begin();
gb.battery.thresholds[0] = 0; //disable battery monitoring
gb.startMenuTimer = 12;
gb.titleScreen(logo);
gb.display.clear();
gb.display.persistence = true;
gb.battery.show = false;
gb.display.drawBitmap(0, 12, logo);
printTopHeader(F("\35 Reading SD card...\n\n"));
gb.display.update();
//SPI.setClockDivider(SPI_CLOCK_DIV128); //lower the SPI speed for better compatibility
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
printTopHeader(F("Insert SD & restart"));
gb.display.update();
while (1);
}
// using nextGameName to save 9 bytes of RAM
const char* address = SETTINGS_PAGE + OFFSET_CURRENTGAME;
nextGameName[0] = pgm_read_byte(address);
nextGameName[1] = pgm_read_byte(address + 1);
nextGameName[2] = pgm_read_byte(address + 2);
nextGameName[3] = pgm_read_byte(address + 3);
nextGameName[4] = pgm_read_byte(address + 4);
nextGameName[5] = pgm_read_byte(address + 5);
nextGameName[6] = pgm_read_byte(address + 6);
nextGameName[7] = pgm_read_byte(address + 7);
for (byte i = 0; i < 8; i++) {
if (nextGameName[i] == ' ')
nextGameName[i] = '\0';
}
if (nextGameName[0]) {
saveeeprom();
saveName();
}
//count the number of files
sd.chdir('/');
while (file.openNext(sd.vwd(), O_READ)) {
if (doDispFile()) {
numberOfFiles++;
}
file.close();
}
numberOfPages = ((numberOfFiles - 1) / PAGELENGTH) + 1;
gb.display.textWrap = false;
updateList();
}
void loop() {
while (1)
if (gb.update()) {
// maybe check for left/right boundries?
if (gb.buttons.pressed(BTN_A)) {
gb.sound.playTick();
displayGameScreen();
updateList();
}
if (gb.buttons.repeat(BTN_RIGHT, 3)) {
cursorPos++;
if (cursorPos >= filesOnPage) {
cursorPos = 0;
selectedPage++;
if (selectedPage >= numberOfPages) {
selectedPage = 0;
}
} else {
updateCursor();
}
}
if (gb.buttons.repeat(BTN_DOWN, 3)) {
cursorPos += PAGE_W;
if (cursorPos >= filesOnPage || gb.buttons.repeat(BTN_B, 1)) {
cursorPos %= PAGE_W;
selectedPage++;
if (selectedPage >= numberOfPages) {
selectedPage = 0;
}
} else {
updateCursor();
}
}
if (gb.buttons.repeat(BTN_LEFT, 3)) {
if (cursorPos == 0 ) { // so that we don't have to compare with negative numbers
cursorPos = PAGELENGTH - 1; // updating the list will adjust this if on last page
if (selectedPage == 0) {
selectedPage = numberOfPages; // we will get decreased one after this if-condition anyways
}
selectedPage--;
} else {
cursorPos--;
updateCursor();
}
}
if (gb.buttons.repeat(BTN_UP, 3)) {
if (cursorPos < PAGE_W || gb.buttons.repeat(BTN_B, 1)) { // so that we don't have to compare with negative numbers
cursorPos += (PAGE_W * (PAGE_H - 1)); // updating the list will adjust this if on last page
if (selectedPage == 0) {
selectedPage = numberOfPages; // we will get decreased one after this if-condition anyways
}
selectedPage--;
} else {
cursorPos -= PAGE_W;
updateCursor();
}
}
if (selectedPage != prevSelectedPage) {
updateList();
prevSelectedPage = selectedPage;
}
// draw the blinking selection box
gb.display.setColor(BLACK);
if ((gb.frameCount % 8) >= 4) {
gb.display.setColor(WHITE);
}
drawCursorBox(cursorPos);
}
}
void saveName() {
//saves the game's name to the flash memory the write_flash_page function from the bootloader
for (byte i = 0; i < 128; i++) {
buffer[i] = pgm_read_byte(SETTINGS_PAGE + i);
}
for (byte i = 0; i < 9; i++) {
buffer[i + OFFSET_CURRENTGAME] = nextGameName[i];
}
write_flash_page (SETTINGS_PAGE, (unsigned char *)buffer);
}
void loadSelectedFile() {
// no need to check if the HEX file exists, as we created the thisPageFiles array with searching for hex files
strcpy(nextGameName, thisPageFiles[cursorPos]);
saveName();
loadeeprom();
//show the first slide from the game's inf file while loading
if(!drawSlide(0)){
//if there is no slide to draw
gb.display.cursorY = 20;
printCentered(nextGameName);
}
printTopHeader(F(" \35 Loading game... "));
printBottomHeader(F(" ! DON'T TURN OFF ! "));
gb.display.update();
load_game(nextGameName);
}
// top header
void drawTopHeader() {
gb.display.setColor(BLACK);
gb.display.fillRect(0, 0, LCDWIDTH, 7);
gb.display.cursorX = 2;
gb.display.cursorY = 1;
gb.display.setColor(WHITE);
gb.display.fillRect(0, 7, LCDWIDTH, 1);
}
void printTopHeader(const __FlashStringHelper* text) {
drawTopHeader();
printCentered(text);
}
void printTopHeader(char* text) {
drawTopHeader();
printCentered(text);
}
// bottom header
void drawBottomheader() {
gb.display.setColor(BLACK);
gb.display.fillRect(0, LCDHEIGHT - 7, LCDWIDTH, 7);
gb.display.cursorX = 2;
gb.display.cursorY = 42;
gb.display.setColor(WHITE);
gb.display.fillRect(0, LCDHEIGHT-8, LCDWIDTH, 1);
}
void printBottomHeader(const __FlashStringHelper* text) {
drawBottomheader();
printCentered(text);
}
void printBottomHeader(char* text) {
drawBottomheader();
printCentered(text);
}
void printCentered(const __FlashStringHelper* text){
gb.display.cursorX = (LCDWIDTH / 2) - (strlen_PF((unsigned long) text) * gb.display.fontSize * gb.display.fontWidth / 2);
gb.display.print(text);
}
void printCentered(char* text){
gb.display.cursorX = (LCDWIDTH / 2) - (strlen(text) * gb.display.fontSize * gb.display.fontWidth / 2);
gb.display.print(text);
}
boolean drawBitmapFromSD(byte x, byte y, byte w, byte h, char* fileName, unsigned int bitmapOffset) {
if (file.open(fileName, O_READ)) {
byte byteWidth = (w + 7) / 8;
byte bufferHeight = BUFFER_SIZE / byteWidth;
file.seekSet(bitmapOffset);
for (byte j = 0; j < h; j++) {
if (j % bufferHeight == 0) { //end of the buffer reached
byte lengthToRead = byteWidth * min(bufferHeight, h - j); //either read until the buffer is full or the end of the picture is reached
if (file.read(buffer, lengthToRead) < lengthToRead) { //read the next part of the picture
file.close(); //can't read the file
return false;
}
}
for (byte i = 0; i < w; i++) {
if (buffer[((j % bufferHeight) * byteWidth + i / 8)] & (B10000000 >> (i % 8))) {
gb.display.drawPixel(x + i, y + j);
}
}
}
} else {
return false; //can't open file
}
file.close();
return true;
}
boolean drawSlide(unsigned int slideNumber) {
gb.display.setColor(WHITE);
gb.display.fillRect(0,8,LCDWIDTH,LCDHEIGHT-16);
gb.display.setColor(BLACK);
strcpy(completeName, thisPageFiles[cursorPos]);
strcat(completeName, ".INF");
return drawBitmapFromSD(0, 8, SLIDE_W, SLIDE_H, completeName, SLIDEOFFSET + (SLIDE_LENGTH * slideNumber));
}