Compare commits

...

5 Commits

Author SHA1 Message Date
f8f7c7a9b6 Fix egg upscale issues 2026-05-28 19:32:04 +02:00
24ebd8a5af Add a way to switch backgrounds 2026-05-28 19:12:27 +02:00
82a7d76b92 Add upscaling on the egg screen 2026-05-28 01:32:10 +02:00
2ae5bc8bd4 Move sprite scale to the defs 2026-05-28 01:31:21 +02:00
dc44a695be Create upscale sprite function 2026-05-28 01:31:03 +02:00
13 changed files with 379 additions and 136 deletions

View File

@ -20,18 +20,14 @@ lib_deps =
fbiego/ESP32Time@^2.0.6
electroniccats/MPU6050@^1.4.3
; --- Hardware & Memory Topology ---
board_build.arduino.psram = enabled
board_upload.flash_size = 16MB
board_build.partitions = default_16MB.csv
; FIX 2: Explicitly matches the 'mode:DIO' your ROM is reporting
board_build.flash_mode = dio
board_build.arduino.memory_type = dio_opi
build_flags =
-DBOARD_HAS_PSRAM
; FIX 1: Forces all Serial.print logs to stay on the hardware UART port
-DARDUINO_USB_CDC_ON_BOOT=0
-DDEV_UNIT
-DDEBUG
@ -44,20 +40,17 @@ platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
; Flash and PSRAM settings for 4MB Flash / 2MB PSRAM (Quad SPI)
board_upload.flash_size = 4MB
board_build.arduino.memory_type = qio_qspi
board_build.flash_mode = qio
board_build.psram_type = qio
; Required flag to enable PSRAM in code
build_flags =
-DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=1
-DDEV_UNIT
; Ensure partition table fits 4MB
board_build.partitions = default.csv
lib_deps = TFT_eSPI, fbiego/ESP32Time@^2.0.6, electroniccats/MPU6050@^1.4.3
upload_port = COM5

View File

@ -3,6 +3,8 @@
#include <stdint.h>
extern int currentBackground;
struct BackgroundData {
uint8_t backgroundWidth;
uint8_t backgroundHeight;

View File

@ -164,6 +164,8 @@
#define CHARA_COUNT_IN_DEVICE 5
#define SPRITE_SCALE 6
extern int screenKey;
extern int menuKey;
extern int submenuKey;

View File

@ -61,6 +61,9 @@ uint32_t dayUnixTime = 0;
Egg_t* eggSelection = NULL;
uint8_t eggNumber = 0;
// Background stuff
int currentBackground = 0;
// Tasks
TaskHandle_t secondLoop = NULL;
@ -92,7 +95,7 @@ void setup() {
storage_readFile("/menu.bin", &menuElementsData);
storage_readFile("/ui.bin", &uiElementsData);
storage_initBackground("/bg2.bin", bg);
storage_initBackground(currentBackground, bg);
pinMode(K1_PIN, BUTTON_MODE);
pinMode(K2_PIN, BUTTON_MODE);

View File

@ -0,0 +1,77 @@
#include "menu.h"
#include "defs/sprite_data.h"
#include "buttons/buttons.h"
#include "draw/draw.h"
#include "storage/storage.h"
#include "display/display.h"
#include "defs/screen_defs.h"
void menu_changeBackgroundScreen(
TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* uiSpriteData
) {
int8_t selectedBackground = currentBackground;
fs::File bgFolder = SPIFFS.open("/bg");
fs::File background = bgFolder.openNextFile();
uint8_t backgrounds = 0;
uint64_t currentTime = esp_timer_get_time();
int8_t selectedPreviousBackground = 0;
while (background) {
if (!background.isDirectory()) {
backgrounds++;
}
background = background.openNextFile();
}
for (;;) {
uint8_t buttonsPressed = buttons_getPressedButtons();
currentTime = esp_timer_get_time();
switch (buttonsPressed) {
case K1_PRESSED:
selectedBackground++;
if (selectedBackground > backgrounds) {
selectedBackground = 0;
}
storage_initBackground(selectedBackground, bg);
lastUpdateTime = currentTime;
break;
case K2_PRESSED:
selectedBackground--;
if (selectedBackground < 0) {
selectedBackground = backgrounds - 1;
}
storage_initBackground(selectedBackground, bg);
lastUpdateTime = currentTime;
break;
case K3_PRESSED:
currentBackground = selectedBackground;
lastUpdateTime = currentTime;
return;
case K4_PRESSED:
storage_initBackground(currentBackground, bg);
lastUpdateTime = currentTime;
return;
}
if (selectedPreviousBackground != selectedBackground) {
draw_drawBackground(bg, 90, 90, 3);
draw_drawSprite(sprite, 174, 96, uiSpriteData, ARROW_ICON);
tft_drawBuffer();
}
if (currentTime - lastUpdateTime > INACTIVITY_THRESHOLD_TIME_US) {
storage_initBackground(currentBackground, bg);
return;
}
}
}

View File

@ -53,11 +53,13 @@ void menu_drawAngryScreen(
struct SpriteData* spriteData, struct SpriteData* smallUiElements
);
void menu_drawFridgeScreen(TFT_eSprite &bg, TFT_eSprite& sprite, struct SpriteData* smallUiElements, struct SpriteData* bigUiElements);
void training_screenTraining2(
TFT_eSprite &bg, TFT_eSprite &sprite,
struct SpriteData* mainCharaData, struct SpriteData* attackSprites
);
void menu_changeBackgroundScreen(
TFT_eSprite &bg, TFT_eSprite &sprite, struct SpriteData* uiSpriteData
);
void menu_sleepScreen_sleepAction();
void menu_sleepScreen_recalculateSleep();

124
src/storage/read_file.cpp Normal file
View File

@ -0,0 +1,124 @@
#include "storage.h"
#include "memory/memory.h"
#include "defs/defs.h"
#include "utils/utils.h"
const char* TAG_SR = "[STORAGE]";
void storage_readFile(const char* path, struct SpriteData* spriteData) {
File file = SPIFFS.open(path, "r");
if (!file) {
printf("%s Failed to open file for reading\n", TAG_SR);
return;
}
uint8_t width, height, spriteNumber;
file.read(&width, 1);
file.read(&height, 1);
file.read(&spriteNumber, 1);
if (spriteData->spriteData != NULL) {
memory_free(spriteData);
}
const uint8_t scaledW = width * SPRITE_SCALE;
const uint8_t scaledH = height * SPRITE_SCALE;
uint16_t** scaled =
(uint16_t**) ps_malloc(spriteNumber * sizeof(uint16_t*));
if (!scaled) {
printf("%s PSRAM alloc failed for pointer table\n", TAG_SR);
file.close();
return;
}
for (uint8_t i = 0; i < spriteNumber; i++) {
scaled[i] =
(uint16_t*) ps_malloc(scaledW * scaledH * sizeof(uint16_t));
if (!scaled[i]) {
printf("%s PSRAM alloc failed for sprite %d\n", TAG_SR, i);
for (uint8_t j = 0; j < i; j++) {
free(scaled[j]);
}
free(scaled);
file.close();
return;
}
}
uint16_t* spriteBuf =
(uint16_t*) malloc(width * height * sizeof(uint16_t));
if (!spriteBuf) {
printf("%s scratch alloc failed\n", TAG_SR);
for (uint8_t i = 0; i < spriteNumber; i++) {
free(scaled[i]);
}
free(scaled);
file.close();
return;
}
printf(
"%s Read header: width=%d, height=%d, numSprites=%d -> scaled to %dx%d\n",
TAG_SR,
width,
height,
spriteNumber,
scaledW,
scaledH
);
for (int sprN = 0; sprN < spriteNumber; sprN++) {
// Read original sprite into temporary buffer
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
uint8_t hi, lo;
file.read(&hi, 1);
file.read(&lo, 1);
spriteBuf[y * width + x] =
(lo << 8) | hi;
}
}
// Upscale sprite
utils_upscaleSprite(
spriteBuf,
width,
height,
scaled[sprN]
);
}
free(spriteBuf);
file.close();
spriteData->spriteWidth = scaledW;
spriteData->spriteHeight = scaledH;
spriteData->spriteNumber = spriteNumber;
spriteData->spriteData = scaled;
printf(
"%s Loaded & upscaled %s (%d sprites, each %dx%d px)\n",
TAG_SR,
path,
spriteNumber,
scaledW,
scaledH
);
}

View File

@ -8,8 +8,6 @@
const char* TAG_S = "[STORAGE]";
#define SPRITE_SCALE 6
void storage_init() {
if (!SPIFFS.begin(true)) {
printf("%s Failed to mount file system\n", TAG_S);
@ -19,95 +17,11 @@ void storage_init() {
}
void storage_readFile(const char* path, struct SpriteData* spriteData) {
File file = SPIFFS.open(path, "r");
if (!file) {
printf("%s Failed to open file for reading\n", TAG_S);
return;
}
void storage_initBackground(const int id, TFT_eSprite& bg) {
char path[15];
uint8_t width, height, spriteNumber;
snprintf(path, 15, "/bg/%i.bin", id);
file.read(&width, 1);
file.read(&height, 1);
file.read(&spriteNumber, 1);
if (spriteData->spriteData != NULL) {
memory_free(spriteData);
}
const uint8_t scaledW = width * SPRITE_SCALE;
const uint8_t scaledH = height * SPRITE_SCALE;
uint16_t** scaled = (uint16_t**) ps_malloc(spriteNumber * sizeof(uint16_t*));
if (!scaled) {
printf("%s PSRAM alloc failed for pointer table\n", TAG_S);
file.close();
return;
}
for (uint8_t i = 0; i < spriteNumber; i++) {
scaled[i] = (uint16_t*) ps_malloc(scaledW * scaledH * sizeof(uint16_t));
if (!scaled[i]) {
printf("%s PSRAM alloc failed for sprite %d\n", TAG_S, i);
for (uint8_t j = 0; j < i; j++) free(scaled[j]);
free(scaled);
file.close();
return;
}
}
uint16_t* rowBuf = (uint16_t*) malloc(width * sizeof(uint16_t));
if (!rowBuf) {
printf("%s scratch alloc failed\n", TAG_S);
for (uint8_t i = 0; i < spriteNumber; i++) free(scaled[i]);
free(scaled);
file.close();
return;
}
printf(
"%s Read header: width=%d, height=%d, numSprites=%d -> scaled to %dx%d\n",
TAG_S, width, height, spriteNumber, scaledW, scaledH
);
for (int sprN = 0; sprN < spriteNumber; sprN++) {
uint16_t* dst = scaled[sprN];
for (int srcY = 0; srcY < height; srcY++) {
for (int srcX = 0; srcX < width; srcX++) {
uint8_t hi, lo;
file.read(&hi, 1);
file.read(&lo, 1);
rowBuf[srcX] = (lo << 8) | hi;
}
for (int dy = 0; dy < SPRITE_SCALE; dy++) {
uint16_t* dstRow = dst + (srcY * SPRITE_SCALE + dy) * scaledW;
for (int srcX = 0; srcX < width; srcX++) {
uint16_t color = rowBuf[srcX];
uint16_t* dstPixel = dstRow + srcX * SPRITE_SCALE;
for (int dx = 0; dx < SPRITE_SCALE; dx++) {
dstPixel[dx] = color;
}
}
}
}
}
free(rowBuf);
file.close();
spriteData->spriteWidth = scaledW;
spriteData->spriteHeight = scaledH;
spriteData->spriteNumber = spriteNumber;
spriteData->spriteData = scaled;
printf("%s Loaded & upscaled %s (%d sprites, each %dx%d px)\n",
TAG_S, path, spriteNumber, scaledW, scaledH);
}
void storage_initBackground(const char* path, TFT_eSprite& bg) {
File file = SPIFFS.open(path, "r");
if (!file) {
printf("%s Failed to open file for reading\n", TAG_S);

View File

@ -9,7 +9,7 @@
void storage_init();
void storage_readFile(const char* path, struct SpriteData* spriteData);
void storage_initBackground(const char* path, TFT_eSprite &bg);
void storage_initBackground(const int id, TFT_eSprite &bg);
void storage_saveState();
void storage_loadState();

View File

@ -0,0 +1,41 @@
#include "utils.h"
#include "defs/defs.h"
/**
* Upscale a sprite using nearest-neighbor scaling.
*
* @param src Pointer to source sprite pixels
* @param srcWidth Original sprite width
* @param srcHeight Original sprite height
* @param dst Destination buffer for upscaled sprite
*/
void utils_upscaleSprite(
const uint16_t* src,
uint8_t srcWidth,
uint8_t srcHeight,
uint16_t* dst
) {
const uint8_t scaledW = srcWidth * SPRITE_SCALE;
for (int srcY = 0; srcY < srcHeight; srcY++) {
for (int dy = 0; dy < SPRITE_SCALE; dy++) {
uint16_t* dstRow =
dst + ((srcY * SPRITE_SCALE + dy) * scaledW);
for (int srcX = 0; srcX < srcWidth; srcX++) {
uint16_t color = src[srcY * srcWidth + srcX];
uint16_t* dstPixel =
dstRow + (srcX * SPRITE_SCALE);
for (int dx = 0; dx < SPRITE_SCALE; dx++) {
dstPixel[dx] = color;
}
}
}
}
}

14
src/utils/utils.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef UTILS_H
#define UTILS_H
#include <Arduino.h>
void utils_upscaleSprite(
const uint16_t* src,
uint8_t srcWidth,
uint8_t srcHeight,
uint16_t* dst
);
#endif

View File

@ -1,23 +1,24 @@
#include "lines.h"
#include "memory/memory.h"
#include "defs/defs.h"
#include "utils/utils.h"
#include <FS.h>
#include <SPIFFS.h>
const char lineHeader[5] = "NPET";
const char lineHeader[5] = "NPET";
const uint8_t headerSize = 4;
void lines_getAvailableLines() {
if (eggSelection != NULL) {
return;
}
fs::File root = SPIFFS.open("/lines");
fs::File lineFile = root.openNextFile();
uint8_t allocCount = 0;
char header[5];
char header[5];
while (lineFile) {
printf("[LINES] Opening file %s\n", lineFile.name());
@ -40,39 +41,69 @@ void lines_getAvailableLines() {
lineFile = root.openNextFile("r");
while (lineFile) {
uint16_t bytesRead = lineFile.readBytes(header, headerSize);
uint16_t bytesRead = 0;
bytesRead += lineFile.readBytes(header, headerSize);
bytesRead += lineFile.read(&availableLines[allocCount].id, 1);
bytesRead += lineFile.readBytes(availableLines[allocCount].name, 16);
bytesRead += lineFile.read(&availableLines[allocCount].eggSprite.spriteWidth, 1);
bytesRead += lineFile.read(&availableLines[allocCount].eggSprite.spriteHeight, 1);
bytesRead += lineFile.read(&availableLines[allocCount].eggSprite.spriteNumber, 1); // Se coloca el cursor al principio de los datos de sprite
uint8_t originalWidth;
uint8_t originalHeight;
uint8_t spriteCount;
availableLines[allocCount].eggSprite.spriteNumber = 1; // Inutil, pero es que necesito hacer la lectura
bytesRead += lineFile.read(&originalWidth, 1);
bytesRead += lineFile.read(&originalHeight, 1);
bytesRead += lineFile.read(&spriteCount, 1);
availableLines[allocCount].eggSprite.spriteData = memory_allocate(
availableLines[allocCount].eggSprite.spriteNumber,
availableLines[allocCount].eggSprite.spriteWidth,
availableLines[allocCount].eggSprite.spriteHeight
);
const uint8_t scaledWidth = originalWidth * SPRITE_SCALE;
const uint8_t scaledHeight = originalHeight * SPRITE_SCALE;
availableLines[allocCount].eggSprite.spriteWidth = scaledWidth;
availableLines[allocCount].eggSprite.spriteHeight = scaledHeight;
availableLines[allocCount].eggSprite.spriteNumber = 1;
availableLines[allocCount].eggSprite.spriteData = memory_allocate(1, scaledWidth, scaledHeight);
uint16_t* spriteBuffer =
(uint16_t*) malloc(
originalWidth *
originalHeight *
sizeof(uint16_t)
);
if (!spriteBuffer) {
printf("[LINES] Failed to allocate sprite buffer\n");
lineFile.close();
lineFile = root.openNextFile();
continue;
}
uint8_t highByte;
uint8_t lowByte;
for (int i = 0; i < availableLines[allocCount].eggSprite.spriteWidth * availableLines[allocCount].eggSprite.spriteHeight; i++) {
bytesRead += lineFile.read(&highByte, 1);
for (int i = 0; i < originalWidth * originalHeight; i++) {
bytesRead += lineFile.read(&lowByte, 1);
bytesRead += lineFile.read(&highByte, 1);
uint16_t pixel = (highByte << 8) | lowByte;
availableLines[allocCount].eggSprite.spriteData[0][i] = pixel;
spriteBuffer[i] = (highByte << 8) | lowByte;
}
utils_upscaleSprite(
spriteBuffer,
originalWidth,
originalHeight,
availableLines[allocCount].eggSprite.spriteData[0]
);
free(spriteBuffer);
strcpy(availableLines[allocCount].fileName, lineFile.name());
lineFile.close();
allocCount++;
lineFile = root.openNextFile();
}
@ -80,4 +111,4 @@ void lines_getAvailableLines() {
eggNumber = allocCount;
eggSelection = availableLines;
}
}

View File

@ -1,6 +1,7 @@
#include "lines.h"
#include "memory/memory.h"
#include "defs/defs.h"
#include "utils/utils.h"
#include <FS.h>
#include <SPIFFS.h>
@ -40,37 +41,76 @@ void lines_getSingleLine(const char* fileName) {
currentLine[currentCharacter] = selectedLine;
currentEgg = selectedEgg;
}
void lines_getSingleEggSprites(fs::File &lineFile, Egg_t* selectedEgg) {
// Importante tener el nombre de archivo del huevo en todo momento
strcpy(selectedEgg->fileName, lineFile.name());
// Ahora se lee los datos
lineFile.read(&(selectedEgg->eggSprite.spriteWidth), 1);
lineFile.read(&(selectedEgg->eggSprite.spriteHeight), 1);
// Leer dimensiones originales
uint8_t originalWidth;
uint8_t originalHeight;
lineFile.read(&originalWidth, 1);
lineFile.read(&originalHeight, 1);
lineFile.read(&(selectedEgg->eggSprite.spriteNumber), 1);
const uint8_t scaledWidth = originalWidth * SPRITE_SCALE;
const uint8_t scaledHeight = originalHeight * SPRITE_SCALE;
// Guardar dimensiones escaladas
selectedEgg->eggSprite.spriteWidth = scaledWidth;
selectedEgg->eggSprite.spriteHeight = scaledHeight;
// Reservar memoria para sprites escalados
selectedEgg->eggSprite.spriteData = memory_allocate(
selectedEgg->eggSprite.spriteNumber,
selectedEgg->eggSprite.spriteWidth,
selectedEgg->eggSprite.spriteHeight
selectedEgg->eggSprite.spriteNumber,
scaledWidth,
scaledHeight
);
uint16_t size = selectedEgg->eggSprite.spriteWidth * selectedEgg->eggSprite.spriteHeight;
const uint16_t originalSize =
originalWidth * originalHeight;
// Buffer temporal en SRAM
uint16_t* spriteBuffer =
(uint16_t*) malloc(
originalSize * sizeof(uint16_t)
);
if (!spriteBuffer) {
printf("[LINES] Failed to allocate sprite buffer\n");
return;
}
uint8_t highByte;
uint8_t lowByte;
for (int spr = 0; spr < selectedEgg->eggSprite.spriteNumber; spr++) {
for (int i= 0; i < size; i++) {
lineFile.read(&highByte, 1);
for (
int spr = 0;
spr < selectedEgg->eggSprite.spriteNumber;
spr++
) {
// Leer sprite original
for (int i = 0; i < originalSize; i++) {
lineFile.read(&lowByte, 1);
lineFile.read(&highByte, 1);
uint16_t pixel = (highByte << 8) | lowByte;
selectedEgg->eggSprite.spriteData[spr][i] = pixel;
spriteBuffer[i] =
(highByte << 8) | lowByte;
}
}
// Escalar sprite
utils_upscaleSprite(
spriteBuffer,
originalWidth,
originalHeight,
selectedEgg->eggSprite.spriteData[spr]
);
}
free(spriteBuffer);
}
// Son las 22:35, que estoy haciendo?