tidy-fix automàtic (sense naming)

This commit is contained in:
2026-05-14 18:28:23 +02:00
parent 358e91ea30
commit b7a551c158
81 changed files with 1549 additions and 831 deletions
+9 -3
View File
@@ -62,10 +62,14 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa
return;
}
if (!music_enabled_) return;
if (!music_enabled_) {
return;
}
auto* resource = AudioResource::getMusic(name);
if (resource == nullptr) return;
if (resource == nullptr) {
return;
}
if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) {
JA_CrossfadeMusic(resource, crossfade_ms, loop);
@@ -83,7 +87,9 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa
// Reproduce la música por puntero (con crossfade opcional)
void Audio::playMusic(JA_Music_t* music, const int loop, const int crossfade_ms) {
if (!music_enabled_ || music == nullptr) return;
if (!music_enabled_ || music == nullptr) {
return;
}
if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) {
JA_CrossfadeMusic(music, crossfade_ms, loop);
+1 -1
View File
@@ -62,7 +62,7 @@ class Audio {
// --- Helpers de conversió per a la capa de presentació ---
// UI (menús, notificacions) manega enters 0..100; internament viu float 0..1.
static constexpr auto toPercent(float volume) -> int {
return static_cast<int>(volume * 100.0F + 0.5F);
return static_cast<int>((volume * 100.0F) + 0.5F);
}
static constexpr auto fromPercent(int percent) -> float {
return static_cast<float>(percent) / 100.0F;
+2 -2
View File
@@ -4,11 +4,11 @@
namespace AudioResource {
JA_Music_t* getMusic(const std::string& name) {
auto getMusic(const std::string& name) -> JA_Music_t* {
return Resource::Cache::get()->getMusic(name);
}
JA_Sound_t* getSound(const std::string& name) {
auto getSound(const std::string& name) -> JA_Sound_t* {
return Resource::Cache::get()->getSound(name);
}
+2 -2
View File
@@ -12,6 +12,6 @@ struct JA_Music_t;
struct JA_Sound_t;
namespace AudioResource {
JA_Music_t* getMusic(const std::string& name);
JA_Sound_t* getSound(const std::string& name);
auto getMusic(const std::string& name) -> JA_Music_t*;
auto getSound(const std::string& name) -> JA_Sound_t*;
} // namespace AudioResource
+221 -120
View File
@@ -2,10 +2,10 @@
// --- Includes ---
#include <SDL3/SDL.h>
#include <stdint.h> // Para uint32_t, uint8_t
#include <stdio.h> // Para NULL, fseek, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
#include <stdlib.h> // Para free, malloc
#include <cstdint> // Para uint32_t, uint8_t
#include <cstdio> // Para NULL, fseek, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
#include <cstdlib> // Para free, malloc
#include <iostream> // Para std::cout
#include <memory> // Para std::unique_ptr
#include <string> // Para std::string
@@ -19,7 +19,9 @@
// overhead gràcies a EBO, igual que un unique_ptr amb default_delete.
struct SDLFreeDeleter {
void operator()(Uint8* p) const noexcept {
if (p) SDL_free(p);
if (p != nullptr) {
SDL_free(p);
}
}
};
@@ -40,8 +42,10 @@ enum JA_Music_state {
};
// --- Struct Definitions ---
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
#define JA_MAX_GROUPS 2
enum {
JA_MAX_SIMULTANEOUS_CHANNELS = 20,
JA_MAX_GROUPS = 2
};
struct JA_Sound_t {
SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000};
@@ -84,7 +88,7 @@ inline JA_Music_t* current_music{nullptr};
inline JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS];
inline SDL_AudioSpec JA_audioSpec{SDL_AUDIO_S16, 2, 48000};
inline float JA_musicVolume{1.0f};
inline float JA_musicVolume{1.0F};
inline float JA_soundVolume[JA_MAX_GROUPS];
inline bool JA_musicEnabled{true};
inline bool JA_soundEnabled{true};
@@ -95,7 +99,7 @@ struct JA_FadeState {
bool active{false};
Uint64 start_time{0};
int duration_ms{0};
float initial_volume{0.0f};
float initial_volume{0.0F};
};
struct JA_OutgoingMusic {
@@ -108,8 +112,8 @@ inline JA_FadeState incoming_fade;
// --- Forward Declarations ---
inline void JA_StopMusic();
inline void JA_StopChannel(const int channel);
inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop = 0, const int group = 0);
inline void JA_StopChannel(int channel);
inline auto JA_PlaySoundOnChannel(JA_Sound_t* sound, int channel, int loop = 0, int group = 0) -> int;
inline void JA_CrossfadeMusic(JA_Music_t* music, int crossfade_ms, int loop = -1);
// --- Music streaming internals ---
@@ -120,12 +124,14 @@ static constexpr int JA_MUSIC_BYTES_PER_SAMPLE = 2;
static constexpr int JA_MUSIC_CHUNK_SHORTS = 8192;
// Umbral d'audio per davant del cursor de reproducció. Mantenim ≥ 0.5 s a
// l'SDL_AudioStream per absorbir jitter de frame i evitar underruns.
static constexpr float JA_MUSIC_LOW_WATER_SECONDS = 0.5f;
static constexpr float JA_MUSIC_LOW_WATER_SECONDS = 0.5F;
// Decodifica un chunk del vorbis i el volca a l'stream. Retorna samples
// decodificats per canal (0 = EOF de l'stream vorbis).
inline int JA_FeedMusicChunk(JA_Music_t* music) {
if (!music || !music->vorbis || !music->stream) return 0;
inline auto JA_FeedMusicChunk(JA_Music_t* music) -> int {
if ((music == nullptr) || (music->vorbis == nullptr) || (music->stream == nullptr)) {
return 0;
}
short chunk[JA_MUSIC_CHUNK_SHORTS];
const int num_channels = music->spec.channels;
@@ -134,7 +140,9 @@ inline int JA_FeedMusicChunk(JA_Music_t* music) {
num_channels,
chunk,
JA_MUSIC_CHUNK_SHORTS);
if (samples_per_channel <= 0) return 0;
if (samples_per_channel <= 0) {
return 0;
}
const int bytes = samples_per_channel * num_channels * JA_MUSIC_BYTES_PER_SAMPLE;
SDL_PutAudioStreamData(music->stream, chunk, bytes);
@@ -144,19 +152,25 @@ inline int JA_FeedMusicChunk(JA_Music_t* music) {
// Reompli l'stream fins que tinga ≥ JA_MUSIC_LOW_WATER_SECONDS bufferats.
// En arribar a EOF del vorbis, aplica el loop (times) o deixa drenar.
inline void JA_PumpMusic(JA_Music_t* music) {
if (!music || !music->vorbis || !music->stream) return;
if ((music == nullptr) || (music->vorbis == nullptr) || (music->stream == nullptr)) {
return;
}
const int bytes_per_second = music->spec.freq * music->spec.channels * JA_MUSIC_BYTES_PER_SAMPLE;
const int low_water_bytes = static_cast<int>(JA_MUSIC_LOW_WATER_SECONDS * static_cast<float>(bytes_per_second));
while (SDL_GetAudioStreamAvailable(music->stream) < low_water_bytes) {
const int decoded = JA_FeedMusicChunk(music);
if (decoded > 0) continue;
if (decoded > 0) {
continue;
}
// EOF: si queden loops, rebobinar; si no, tallar i deixar drenar.
if (music->times != 0) {
stb_vorbis_seek_start(music->vorbis);
if (music->times > 0) music->times--;
if (music->times > 0) {
music->times--;
}
} else {
break;
}
@@ -168,14 +182,18 @@ inline void JA_PumpMusic(JA_Music_t* music) {
// streaming: l'stream robat no es pot re-alimentar perquè perd la referència
// al seu vorbis decoder. No aplica loop — si el vorbis s'esgota abans, parem.
inline void JA_PreFillOutgoing(JA_Music_t* music, int duration_ms) {
if (!music || !music->vorbis || !music->stream) return;
if ((music == nullptr) || (music->vorbis == nullptr) || (music->stream == nullptr)) {
return;
}
const int bytes_per_second = music->spec.freq * music->spec.channels * JA_MUSIC_BYTES_PER_SAMPLE;
const int needed_bytes = static_cast<int>((static_cast<int64_t>(duration_ms) * bytes_per_second) / 1000);
while (SDL_GetAudioStreamAvailable(music->stream) < needed_bytes) {
const int decoded = JA_FeedMusicChunk(music);
if (decoded <= 0) break; // EOF: deixem drenar el que hi haja
if (decoded <= 0) {
break; // EOF: deixem drenar el que hi haja
}
}
}
@@ -183,7 +201,7 @@ inline void JA_PreFillOutgoing(JA_Music_t* music, int duration_ms) {
inline void JA_Update() {
// --- Outgoing music fade-out (crossfade o fade-out a silencio) ---
if (outgoing_music.stream && outgoing_music.fade.active) {
if ((outgoing_music.stream != nullptr) && outgoing_music.fade.active) {
Uint64 now = SDL_GetTicks();
Uint64 elapsed = now - outgoing_music.fade.start_time;
if (elapsed >= (Uint64)outgoing_music.fade.duration_ms) {
@@ -192,12 +210,12 @@ inline void JA_Update() {
outgoing_music.fade.active = false;
} else {
float percent = (float)elapsed / (float)outgoing_music.fade.duration_ms;
SDL_SetAudioStreamGain(outgoing_music.stream, outgoing_music.fade.initial_volume * (1.0f - percent));
SDL_SetAudioStreamGain(outgoing_music.stream, outgoing_music.fade.initial_volume * (1.0F - percent));
}
}
// --- Current music ---
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING) {
if (JA_musicEnabled && (current_music != nullptr) && current_music->state == JA_MUSIC_PLAYING) {
// Fade-in (parte de un crossfade)
if (incoming_fade.active) {
Uint64 now = SDL_GetTicks();
@@ -221,42 +239,59 @@ inline void JA_Update() {
// --- Sound channels ---
if (JA_soundEnabled) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i)
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) {
if (channels[i].state == JA_CHANNEL_PLAYING) {
if (channels[i].times != 0) {
if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2)) {
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer.get(), channels[i].sound->length);
if (channels[i].times > 0) channels[i].times--;
if (channels[i].times > 0) {
channels[i].times--;
}
}
} else {
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i);
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) {
JA_StopChannel(i);
}
}
}
}
}
}
inline void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels) {
JA_audioSpec = {format, num_channels, freq};
if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
JA_audioSpec = {.format = format, .channels = num_channels, .freq = freq};
if (sdlAudioDevice != 0u) {
SDL_CloseAudioDevice(sdlAudioDevice);
}
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
if (sdlAudioDevice == 0) std::cout << "Failed to initialize SDL audio!" << '\n';
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) channels[i].state = JA_CHANNEL_FREE;
for (int i = 0; i < JA_MAX_GROUPS; ++i) JA_soundVolume[i] = 0.5f;
if (sdlAudioDevice == 0) {
std::cout << "Failed to initialize SDL audio!" << '\n';
}
for (auto& channel : channels) {
channel.state = JA_CHANNEL_FREE;
}
for (float& i : JA_soundVolume) {
i = 0.5F;
}
}
inline void JA_Quit() {
if (outgoing_music.stream) {
if (outgoing_music.stream != nullptr) {
SDL_DestroyAudioStream(outgoing_music.stream);
outgoing_music.stream = nullptr;
}
if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
if (sdlAudioDevice != 0u) {
SDL_CloseAudioDevice(sdlAudioDevice);
}
sdlAudioDevice = 0;
}
// --- Music Functions ---
inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) {
if (!buffer || length == 0) return nullptr;
inline auto JA_LoadMusic(const Uint8* buffer, Uint32 length) -> JA_Music_t* {
if ((buffer == nullptr) || length == 0) {
return nullptr;
}
// Allocem el JA_Music_t primer per aprofitar el seu `std::vector<Uint8>`
// com a propietari del OGG comprimit. stb_vorbis guarda un punter
@@ -270,7 +305,7 @@ inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) {
static_cast<int>(length),
&vorbis_error,
nullptr);
if (!music->vorbis) {
if (music->vorbis == nullptr) {
std::cout << "JA_LoadMusic: stb_vorbis_open_memory failed (error " << vorbis_error << ")" << '\n';
delete music;
return nullptr;
@@ -287,21 +322,25 @@ inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) {
// Overload amb filename — els callers l'usen per poder comparar la música
// en curs amb JA_GetMusicFilename() i no rearrancar-la si ja és la mateixa.
inline JA_Music_t* JA_LoadMusic(Uint8* buffer, Uint32 length, const char* filename) {
inline auto JA_LoadMusic(Uint8* buffer, Uint32 length, const char* filename) -> JA_Music_t* {
JA_Music_t* music = JA_LoadMusic(static_cast<const Uint8*>(buffer), length);
if (music && filename) music->filename = filename;
if ((music != nullptr) && (filename != nullptr)) {
music->filename = filename;
}
return music;
}
inline JA_Music_t* JA_LoadMusic(const char* filename) {
inline auto JA_LoadMusic(const char* filename) -> JA_Music_t* {
// Carreguem primer el arxiu en memòria i després el descomprimim.
FILE* f = fopen(filename, "rb");
if (!f) return nullptr;
if (f == nullptr) {
return nullptr;
}
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
auto* buffer = static_cast<Uint8*>(malloc(fsize + 1));
if (!buffer) {
if (buffer == nullptr) {
fclose(f);
return nullptr;
}
@@ -313,7 +352,7 @@ inline JA_Music_t* JA_LoadMusic(const char* filename) {
fclose(f);
JA_Music_t* music = JA_LoadMusic(static_cast<const Uint8*>(buffer), static_cast<Uint32>(fsize));
if (music) {
if (music != nullptr) {
music->filename = filename;
}
@@ -323,7 +362,9 @@ inline JA_Music_t* JA_LoadMusic(const char* filename) {
}
inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) {
if (!JA_musicEnabled || !music || !music->vorbis) return;
if (!JA_musicEnabled || (music == nullptr) || (music->vorbis == nullptr)) {
return;
}
JA_StopMusic();
@@ -336,7 +377,7 @@ inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) {
stb_vorbis_seek_start(current_music->vorbis);
current_music->stream = SDL_CreateAudioStream(&current_music->spec, &JA_audioSpec);
if (!current_music->stream) {
if (current_music->stream == nullptr) {
std::cout << "Failed to create audio stream!" << '\n';
current_music->state = JA_MUSIC_STOPPED;
return;
@@ -351,23 +392,35 @@ inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) {
}
}
inline const char* JA_GetMusicFilename(const JA_Music_t* music = nullptr) {
if (!music) music = current_music;
if (!music || music->filename.empty()) return nullptr;
inline auto JA_GetMusicFilename(const JA_Music_t* music = nullptr) -> const char* {
if (music == nullptr) {
music = current_music;
}
if ((music == nullptr) || music->filename.empty()) {
return nullptr;
}
return music->filename.c_str();
}
inline void JA_PauseMusic() {
if (!JA_musicEnabled) return;
if (!current_music || current_music->state != JA_MUSIC_PLAYING) return;
if (!JA_musicEnabled) {
return;
}
if ((current_music == nullptr) || current_music->state != JA_MUSIC_PLAYING) {
return;
}
current_music->state = JA_MUSIC_PAUSED;
SDL_UnbindAudioStream(current_music->stream);
}
inline void JA_ResumeMusic() {
if (!JA_musicEnabled) return;
if (!current_music || current_music->state != JA_MUSIC_PAUSED) return;
if (!JA_musicEnabled) {
return;
}
if ((current_music == nullptr) || current_music->state != JA_MUSIC_PAUSED) {
return;
}
current_music->state = JA_MUSIC_PLAYING;
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
@@ -375,33 +428,39 @@ inline void JA_ResumeMusic() {
inline void JA_StopMusic() {
// Limpiar outgoing crossfade si existe
if (outgoing_music.stream) {
if (outgoing_music.stream != nullptr) {
SDL_DestroyAudioStream(outgoing_music.stream);
outgoing_music.stream = nullptr;
outgoing_music.fade.active = false;
}
incoming_fade.active = false;
if (!current_music || current_music->state == JA_MUSIC_INVALID || current_music->state == JA_MUSIC_STOPPED) return;
if ((current_music == nullptr) || current_music->state == JA_MUSIC_INVALID || current_music->state == JA_MUSIC_STOPPED) {
return;
}
current_music->state = JA_MUSIC_STOPPED;
if (current_music->stream) {
if (current_music->stream != nullptr) {
SDL_DestroyAudioStream(current_music->stream);
current_music->stream = nullptr;
}
// Deixem el handle de vorbis viu — es tanca en JA_DeleteMusic.
// Rebobinem perquè un futur JA_PlayMusic comence des del principi.
if (current_music->vorbis) {
if (current_music->vorbis != nullptr) {
stb_vorbis_seek_start(current_music->vorbis);
}
}
inline void JA_FadeOutMusic(const int milliseconds) {
if (!JA_musicEnabled) return;
if (!current_music || current_music->state != JA_MUSIC_PLAYING) return;
if (!JA_musicEnabled) {
return;
}
if ((current_music == nullptr) || current_music->state != JA_MUSIC_PLAYING) {
return;
}
// Destruir outgoing anterior si existe
if (outgoing_music.stream) {
if (outgoing_music.stream != nullptr) {
SDL_DestroyAudioStream(outgoing_music.stream);
outgoing_music.stream = nullptr;
}
@@ -412,20 +471,24 @@ inline void JA_FadeOutMusic(const int milliseconds) {
// Robar el stream del current_music al outgoing
outgoing_music.stream = current_music->stream;
outgoing_music.fade = {true, SDL_GetTicks(), milliseconds, JA_musicVolume};
outgoing_music.fade = {.active = true, .start_time = SDL_GetTicks(), .duration_ms = milliseconds, .initial_volume = JA_musicVolume};
// Dejar current_music sin stream (ya lo tiene outgoing)
current_music->stream = nullptr;
current_music->state = JA_MUSIC_STOPPED;
if (current_music->vorbis) stb_vorbis_seek_start(current_music->vorbis);
if (current_music->vorbis != nullptr) {
stb_vorbis_seek_start(current_music->vorbis);
}
incoming_fade.active = false;
}
inline void JA_CrossfadeMusic(JA_Music_t* music, const int crossfade_ms, const int loop) {
if (!JA_musicEnabled || !music || !music->vorbis) return;
if (!JA_musicEnabled || (music == nullptr) || (music->vorbis == nullptr)) {
return;
}
// Destruir outgoing anterior si existe (crossfade durante crossfade)
if (outgoing_music.stream) {
if (outgoing_music.stream != nullptr) {
SDL_DestroyAudioStream(outgoing_music.stream);
outgoing_music.stream = nullptr;
outgoing_music.fade.active = false;
@@ -434,13 +497,15 @@ inline void JA_CrossfadeMusic(JA_Music_t* music, const int crossfade_ms, const i
// Robar el stream de la musica actual al outgoing para el fade-out.
// Pre-omplim amb `crossfade_ms` de so perquè no es quede en silenci
// abans d'acabar el fade (l'stream robat ja no pot alimentar-se).
if (current_music && current_music->state == JA_MUSIC_PLAYING && current_music->stream) {
if ((current_music != nullptr) && current_music->state == JA_MUSIC_PLAYING && (current_music->stream != nullptr)) {
JA_PreFillOutgoing(current_music, crossfade_ms);
outgoing_music.stream = current_music->stream;
outgoing_music.fade = {true, SDL_GetTicks(), crossfade_ms, JA_musicVolume};
outgoing_music.fade = {.active = true, .start_time = SDL_GetTicks(), .duration_ms = crossfade_ms, .initial_volume = JA_musicVolume};
current_music->stream = nullptr;
current_music->state = JA_MUSIC_STOPPED;
if (current_music->vorbis) stb_vorbis_seek_start(current_music->vorbis);
if (current_music->vorbis != nullptr) {
stb_vorbis_seek_start(current_music->vorbis);
}
}
// Iniciar la nueva pista con gain=0 (el fade-in la sube gradualmente)
@@ -450,42 +515,52 @@ inline void JA_CrossfadeMusic(JA_Music_t* music, const int crossfade_ms, const i
stb_vorbis_seek_start(current_music->vorbis);
current_music->stream = SDL_CreateAudioStream(&current_music->spec, &JA_audioSpec);
if (!current_music->stream) {
if (current_music->stream == nullptr) {
std::cout << "Failed to create audio stream for crossfade!" << '\n';
current_music->state = JA_MUSIC_STOPPED;
return;
}
SDL_SetAudioStreamGain(current_music->stream, 0.0f);
SDL_SetAudioStreamGain(current_music->stream, 0.0F);
JA_PumpMusic(current_music); // pre-carrega abans de bindejar
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
// Configurar fade-in
incoming_fade = {true, SDL_GetTicks(), crossfade_ms, 0.0f};
incoming_fade = {.active = true, .start_time = SDL_GetTicks(), .duration_ms = crossfade_ms, .initial_volume = 0.0F};
}
inline JA_Music_state JA_GetMusicState() {
if (!JA_musicEnabled) return JA_MUSIC_DISABLED;
if (!current_music) return JA_MUSIC_INVALID;
inline auto JA_GetMusicState() -> JA_Music_state {
if (!JA_musicEnabled) {
return JA_MUSIC_DISABLED;
}
if (current_music == nullptr) {
return JA_MUSIC_INVALID;
}
return current_music->state;
}
inline void JA_DeleteMusic(JA_Music_t* music) {
if (!music) return;
if (music == nullptr) {
return;
}
if (current_music == music) {
JA_StopMusic();
current_music = nullptr;
}
if (music->stream) SDL_DestroyAudioStream(music->stream);
if (music->vorbis) stb_vorbis_close(music->vorbis);
if (music->stream != nullptr) {
SDL_DestroyAudioStream(music->stream);
}
if (music->vorbis != nullptr) {
stb_vorbis_close(music->vorbis);
}
// ogg_data (std::vector) i filename (std::string) s'alliberen sols
// al destructor de JA_Music_t.
delete music;
}
inline float JA_SetMusicVolume(float volume) {
JA_musicVolume = SDL_clamp(volume, 0.0f, 1.0f);
if (current_music && current_music->stream) {
inline auto JA_SetMusicVolume(float volume) -> float {
JA_musicVolume = SDL_clamp(volume, 0.0F, 1.0F);
if ((current_music != nullptr) && (current_music->stream != nullptr)) {
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
}
return JA_musicVolume;
@@ -495,18 +570,20 @@ inline void JA_SetMusicPosition(float /*value*/) {
// No implementat amb el backend de streaming.
}
inline float JA_GetMusicPosition() {
return 0.0f;
inline auto JA_GetMusicPosition() -> float {
return 0.0F;
}
inline void JA_EnableMusic(const bool value) {
if (!value && current_music && (current_music->state == JA_MUSIC_PLAYING)) JA_StopMusic();
if (!value && (current_music != nullptr) && (current_music->state == JA_MUSIC_PLAYING)) {
JA_StopMusic();
}
JA_musicEnabled = value;
}
// --- Sound Functions ---
inline JA_Sound_t* JA_LoadSound(uint8_t* buffer, uint32_t size) {
inline auto JA_LoadSound(uint8_t* buffer, uint32_t size) -> JA_Sound_t* {
auto sound = std::make_unique<JA_Sound_t>();
Uint8* raw = nullptr;
if (!SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &raw, &sound->length)) {
@@ -517,7 +594,7 @@ inline JA_Sound_t* JA_LoadSound(uint8_t* buffer, uint32_t size) {
return sound.release();
}
inline JA_Sound_t* JA_LoadSound(const char* filename) {
inline auto JA_LoadSound(const char* filename) -> JA_Sound_t* {
auto sound = std::make_unique<JA_Sound_t>();
Uint8* raw = nullptr;
if (!SDL_LoadWAV(filename, &sound->spec, &raw, &sound->length)) {
@@ -528,8 +605,10 @@ inline JA_Sound_t* JA_LoadSound(const char* filename) {
return sound.release();
}
inline int JA_PlaySound(JA_Sound_t* sound, const int loop = 0, const int group = 0) {
if (!JA_soundEnabled || !sound) return -1;
inline auto JA_PlaySound(JA_Sound_t* sound, const int loop = 0, const int group = 0) -> int {
if (!JA_soundEnabled || (sound == nullptr)) {
return -1;
}
int channel = 0;
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; }
@@ -541,9 +620,13 @@ inline int JA_PlaySound(JA_Sound_t* sound, const int loop = 0, const int group =
return JA_PlaySoundOnChannel(sound, channel, loop, group);
}
inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop, const int group) {
if (!JA_soundEnabled || !sound) return -1;
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return -1;
inline auto JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop, const int group) -> int {
if (!JA_soundEnabled || (sound == nullptr)) {
return -1;
}
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) {
return -1;
}
JA_StopChannel(channel);
@@ -554,7 +637,7 @@ inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int
channels[channel].state = JA_CHANNEL_PLAYING;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
if (!channels[channel].stream) {
if (channels[channel].stream == nullptr) {
std::cout << "Failed to create audio stream for sound!" << '\n';
channels[channel].state = JA_CHANNEL_FREE;
return -1;
@@ -568,23 +651,30 @@ inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int
}
inline void JA_DeleteSound(JA_Sound_t* sound) {
if (!sound) return;
if (sound == nullptr) {
return;
}
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].sound == sound) JA_StopChannel(i);
if (channels[i].sound == sound) {
JA_StopChannel(i);
}
}
// buffer es destrueix automàticament via RAII (SDLFreeDeleter).
delete sound;
}
inline void JA_PauseChannel(const int channel) {
if (!JA_soundEnabled) return;
if (!JA_soundEnabled) {
return;
}
if (channel == -1) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
if (channels[i].state == JA_CHANNEL_PLAYING) {
channels[i].state = JA_CHANNEL_PAUSED;
SDL_UnbindAudioStream(channels[i].stream);
for (auto& channel : channels) {
if (channel.state == JA_CHANNEL_PLAYING) {
channel.state = JA_CHANNEL_PAUSED;
SDL_UnbindAudioStream(channel.stream);
}
}
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
if (channels[channel].state == JA_CHANNEL_PLAYING) {
channels[channel].state = JA_CHANNEL_PAUSED;
@@ -594,14 +684,17 @@ inline void JA_PauseChannel(const int channel) {
}
inline void JA_ResumeChannel(const int channel) {
if (!JA_soundEnabled) return;
if (!JA_soundEnabled) {
return;
}
if (channel == -1) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
if (channels[i].state == JA_CHANNEL_PAUSED) {
channels[i].state = JA_CHANNEL_PLAYING;
SDL_BindAudioStream(sdlAudioDevice, channels[i].stream);
for (auto& channel : channels) {
if (channel.state == JA_CHANNEL_PAUSED) {
channel.state = JA_CHANNEL_PLAYING;
SDL_BindAudioStream(sdlAudioDevice, channel.stream);
}
}
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
if (channels[channel].state == JA_CHANNEL_PAUSED) {
channels[channel].state = JA_CHANNEL_PLAYING;
@@ -612,18 +705,22 @@ inline void JA_ResumeChannel(const int channel) {
inline void JA_StopChannel(const int channel) {
if (channel == -1) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].state != JA_CHANNEL_FREE) {
if (channels[i].stream) SDL_DestroyAudioStream(channels[i].stream);
channels[i].stream = nullptr;
channels[i].state = JA_CHANNEL_FREE;
channels[i].pos = 0;
channels[i].sound = nullptr;
for (auto& channel : channels) {
if (channel.state != JA_CHANNEL_FREE) {
if (channel.stream != nullptr) {
SDL_DestroyAudioStream(channel.stream);
}
channel.stream = nullptr;
channel.state = JA_CHANNEL_FREE;
channel.pos = 0;
channel.sound = nullptr;
}
}
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
if (channels[channel].state != JA_CHANNEL_FREE) {
if (channels[channel].stream) SDL_DestroyAudioStream(channels[channel].stream);
if (channels[channel].stream != nullptr) {
SDL_DestroyAudioStream(channels[channel].stream);
}
channels[channel].stream = nullptr;
channels[channel].state = JA_CHANNEL_FREE;
channels[channel].pos = 0;
@@ -632,19 +729,23 @@ inline void JA_StopChannel(const int channel) {
}
}
inline JA_Channel_state JA_GetChannelState(const int channel) {
if (!JA_soundEnabled) return JA_SOUND_DISABLED;
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return JA_CHANNEL_INVALID;
inline auto JA_GetChannelState(const int channel) -> JA_Channel_state {
if (!JA_soundEnabled) {
return JA_SOUND_DISABLED;
}
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) {
return JA_CHANNEL_INVALID;
}
return channels[channel].state;
}
inline float JA_SetSoundVolume(float volume, const int group = -1) {
const float v = SDL_clamp(volume, 0.0f, 1.0f);
inline auto JA_SetSoundVolume(float volume, const int group = -1) -> float {
const float v = SDL_clamp(volume, 0.0F, 1.0F);
if (group == -1) {
for (int i = 0; i < JA_MAX_GROUPS; ++i) {
JA_soundVolume[i] = v;
for (float& i : JA_soundVolume) {
i = v;
}
} else if (group >= 0 && group < JA_MAX_GROUPS) {
JA_soundVolume[group] = v;
@@ -653,11 +754,11 @@ inline float JA_SetSoundVolume(float volume, const int group = -1) {
}
// Aplicar volum als canals actius.
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if ((channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED)) {
if (group == -1 || channels[i].group == group) {
if (channels[i].stream) {
SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume[channels[i].group]);
for (auto& channel : channels) {
if ((channel.state == JA_CHANNEL_PLAYING) || (channel.state == JA_CHANNEL_PAUSED)) {
if (group == -1 || channel.group == group) {
if (channel.stream != nullptr) {
SDL_SetAudioStreamGain(channel.stream, JA_soundVolume[channel.group]);
}
}
}
@@ -672,7 +773,7 @@ inline void JA_EnableSound(const bool value) {
JA_soundEnabled = value;
}
inline float JA_SetVolume(float volume) {
inline auto JA_SetVolume(float volume) -> float {
float v = JA_SetMusicVolume(volume);
JA_SetSoundVolume(v, -1);
return v;
+44 -20
View File
@@ -52,8 +52,8 @@ namespace Gamepad {
// Recorta el nom visible del mando: trim des del primer '(' o '['
// (per a evitar coses com "Retroid Controller (vendor: 1001) ..."),
// elimina espais finals i talla a 25 caràcters.
static std::string prettyName(const char* raw) {
std::string name = (raw && *raw) ? raw : "Gamepad";
static auto prettyName(const char* raw) -> std::string {
std::string name = ((raw != nullptr) && (*raw != 0)) ? raw : "Gamepad";
const auto pos = name.find_first_of("([");
if (pos != std::string::npos) {
name.erase(pos);
@@ -64,7 +64,9 @@ namespace Gamepad {
if (name.size() > 25) {
name.resize(25);
}
if (name.empty()) name = "Gamepad";
if (name.empty()) {
name = "Gamepad";
}
return name;
}
@@ -98,12 +100,14 @@ namespace Gamepad {
static void openFirstGamepad() {
int count = 0;
SDL_JoystickID* ids = SDL_GetJoysticks(&count);
if (ids) {
if (ids != nullptr) {
for (int i = 0; i < count; ++i) {
installWebStandardMapping(ids[i]);
if (!SDL_IsGamepad(ids[i])) continue;
if (!SDL_IsGamepad(ids[i])) {
continue;
}
pad_ = SDL_OpenGamepad(ids[i]);
if (pad_) {
if (pad_ != nullptr) {
pad_id_ = ids[i];
SDL_Log("Gamepad connectat: %s", SDL_GetGamepadName(pad_));
break;
@@ -128,7 +132,7 @@ namespace Gamepad {
}
void destroy() {
if (pad_) {
if (pad_ != nullptr) {
SDL_CloseGamepad(pad_);
pad_ = nullptr;
pad_id_ = 0;
@@ -145,12 +149,14 @@ namespace Gamepad {
// GAMEPAD_ADDED) perquè SDL no reconeix el GUID. Escoltem els dos i
// injectem el mapping estàndard abans d'obrir el mando.
if (event.type == SDL_EVENT_GAMEPAD_ADDED || event.type == SDL_EVENT_JOYSTICK_ADDED) {
if (!pad_) {
if (pad_ == nullptr) {
SDL_JoystickID jid = event.jdevice.which;
installWebStandardMapping(jid);
if (!SDL_IsGamepad(jid)) return;
if (!SDL_IsGamepad(jid)) {
return;
}
pad_ = SDL_OpenGamepad(jid);
if (pad_) {
if (pad_ != nullptr) {
pad_id_ = jid;
std::string name = prettyName(SDL_GetGamepadName(pad_));
SDL_Log("Gamepad connectat: %s", name.c_str());
@@ -158,7 +164,7 @@ namespace Gamepad {
}
}
} else if (event.type == SDL_EVENT_GAMEPAD_REMOVED || event.type == SDL_EVENT_JOYSTICK_REMOVED) {
if (pad_ && event.jdevice.which == pad_id_) {
if ((pad_ != nullptr) && event.jdevice.which == pad_id_) {
std::string saved_name = prettyName(SDL_GetGamepadName(pad_));
SDL_Log("Gamepad desconnectat: %s", saved_name.c_str());
SDL_CloseGamepad(pad_);
@@ -190,7 +196,9 @@ namespace Gamepad {
}
void update() {
if (!pad_) return;
if (pad_ == nullptr) {
return;
}
// D-pad
bool dup = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_DPAD_UP);
@@ -220,19 +228,35 @@ namespace Gamepad {
bool back = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_BACK);
// Select (Back) → obre/tanca menú de servei (flanc)
if (back && !prev_back_) pushKey(KeyConfig::scancode("menu_toggle"));
if (back && !prev_back_) {
pushKey(KeyConfig::scancode("menu_toggle"));
}
// Start → pausa (flanc)
if (start && !prev_start_) pushKey(KeyConfig::scancode("pause_toggle"));
if (start && !prev_start_) {
pushKey(KeyConfig::scancode("pause_toggle"));
}
if (Menu::isOpen()) {
// Navegació del menú per flanc
if (up && !prev_up_) pushKey(SDL_SCANCODE_UP);
if (dn && !prev_down_) pushKey(SDL_SCANCODE_DOWN);
if (lt && !prev_left_) pushKey(SDL_SCANCODE_LEFT);
if (rt && !prev_right_) pushKey(SDL_SCANCODE_RIGHT);
if (up && !prev_up_) {
pushKey(SDL_SCANCODE_UP);
}
if (dn && !prev_down_) {
pushKey(SDL_SCANCODE_DOWN);
}
if (lt && !prev_left_) {
pushKey(SDL_SCANCODE_LEFT);
}
if (rt && !prev_right_) {
pushKey(SDL_SCANCODE_RIGHT);
}
// EAST accepta, SOUTH cancela / endarrere
if (east && !prev_east_) pushKey(SDL_SCANCODE_RETURN);
if (south && !prev_south_) pushKey(SDL_SCANCODE_BACKSPACE);
if (east && !prev_east_) {
pushKey(SDL_SCANCODE_RETURN);
}
if (south && !prev_south_) {
pushKey(SDL_SCANCODE_BACKSPACE);
}
// Assegura que el joc no rep tecles de moviment mentre el menú està obert
JI_SetVirtualKey(SDL_SCANCODE_UP, JI_VSRC_GAMEPAD, false);
+30 -10
View File
@@ -34,7 +34,9 @@ namespace GlobalInputs {
snprintf(msg, sizeof(msg), Locale::get("notifications.zoom_fmt"), Screen::get()->getZoom());
Overlay::showNotification(msg);
}
if (dec_zoom) consumed = true;
if (dec_zoom) {
consumed = true;
}
dec_zoom_prev = dec_zoom;
// F2 — Augmentar zoom
@@ -45,7 +47,9 @@ namespace GlobalInputs {
snprintf(msg, sizeof(msg), Locale::get("notifications.zoom_fmt"), Screen::get()->getZoom());
Overlay::showNotification(msg);
}
if (inc_zoom) consumed = true;
if (inc_zoom) {
consumed = true;
}
inc_zoom_prev = inc_zoom;
// F3 — Toggle pantalla completa
@@ -54,7 +58,9 @@ namespace GlobalInputs {
Screen::get()->toggleFullscreen();
Overlay::showNotification(Screen::get()->isFullscreen() ? Locale::get("notifications.fullscreen") : Locale::get("notifications.windowed"));
}
if (fullscreen) consumed = true;
if (fullscreen) {
consumed = true;
}
fullscreen_prev = fullscreen;
// F4 — Toggle shaders
@@ -63,7 +69,9 @@ namespace GlobalInputs {
Screen::get()->toggleShaders();
Overlay::showNotification(Options::video.shader_enabled ? Locale::get("notifications.shader_on") : Locale::get("notifications.shader_off"));
}
if (shader) consumed = true;
if (shader) {
consumed = true;
}
shader_prev = shader;
// F5 — Toggle aspect ratio 4:3
@@ -72,7 +80,9 @@ namespace GlobalInputs {
Screen::get()->toggleAspectRatio();
Overlay::showNotification(Options::video.aspect_ratio_4_3 ? Locale::get("notifications.aspect_43") : Locale::get("notifications.aspect_square"));
}
if (aspect) consumed = true;
if (aspect) {
consumed = true;
}
aspect_prev = aspect;
// F6 — Toggle supersampling
@@ -82,7 +92,9 @@ namespace GlobalInputs {
Overlay::showNotification(Options::video.supersampling ? Locale::get("notifications.ss_on") : Locale::get("notifications.ss_off"));
}
}
if (ss) consumed = true;
if (ss) {
consumed = true;
}
ss_prev = ss;
// F7 — Canviar tipus de shader (PostFX ↔ CrtPi)
@@ -94,7 +106,9 @@ namespace GlobalInputs {
Overlay::showNotification(msg);
}
}
if (next_shader) consumed = true;
if (next_shader) {
consumed = true;
}
next_shader_prev = next_shader;
// F8 — Pròxim preset del shader actiu
@@ -106,7 +120,9 @@ namespace GlobalInputs {
Overlay::showNotification(msg);
}
}
if (next_preset) consumed = true;
if (next_preset) {
consumed = true;
}
next_preset_prev = next_preset;
// F9 — Cicla filtre de textura (NEAREST ↔ LINEAR), sempre aplicat
@@ -117,7 +133,9 @@ namespace GlobalInputs {
? Locale::get("notifications.filter_linear")
: Locale::get("notifications.filter_nearest"));
}
if (texture_filter) consumed = true;
if (texture_filter) {
consumed = true;
}
texture_filter_prev = texture_filter;
// F10 — Toggle render info (FPS, driver, shader)
@@ -125,7 +143,9 @@ namespace GlobalInputs {
if (render_info && !render_info_prev) {
Overlay::toggleRenderInfo();
}
if (render_info) consumed = true;
if (render_info) {
consumed = true;
}
render_info_prev = render_info;
return consumed;
+33 -11
View File
@@ -16,7 +16,9 @@ namespace KeyConfig {
auto findIndex(const std::string& id) -> size_t {
auto it = index_.find(id);
if (it == index_.end()) return SIZE_MAX;
if (it == index_.end()) {
return SIZE_MAX;
}
return it->second;
}
@@ -30,7 +32,9 @@ namespace KeyConfig {
std::string content(buf.begin(), buf.end());
try {
auto yaml = fkyaml::node::deserialize(content);
if (!yaml.contains("keys")) return;
if (!yaml.contains("keys")) {
return;
}
for (const auto& node : yaml["keys"]) {
KeyEntry entry;
@@ -59,7 +63,9 @@ namespace KeyConfig {
void applyOverrides(const std::string& disk_path) {
std::ifstream file(disk_path);
if (!file.good()) return;
if (!file.good()) {
return;
}
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
@@ -67,7 +73,9 @@ namespace KeyConfig {
try {
auto yaml = fkyaml::node::deserialize(content);
if (!yaml.contains("overrides")) return;
if (!yaml.contains("overrides")) {
return;
}
int applied = 0;
for (const auto& kv : yaml["overrides"].as_map()) {
@@ -118,28 +126,38 @@ namespace KeyConfig {
auto scancode(const std::string& id) -> SDL_Scancode {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return SDL_SCANCODE_UNKNOWN;
if (idx == SIZE_MAX) {
return SDL_SCANCODE_UNKNOWN;
}
return entries_[idx].scancode;
}
auto scancodePtr(const std::string& id) -> SDL_Scancode* {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return nullptr;
if (idx == SIZE_MAX) {
return nullptr;
}
return &entries_[idx].scancode;
}
void setScancode(const std::string& id, SDL_Scancode sc) {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return;
if (idx == SIZE_MAX) {
return;
}
entries_[idx].scancode = sc;
const char* name = SDL_GetScancodeName(sc);
entries_[idx].code = (name != nullptr) ? name : "";
}
auto isGuiKey(SDL_Scancode sc) -> bool {
if (sc == SDL_SCANCODE_UNKNOWN) return false;
if (sc == SDL_SCANCODE_UNKNOWN) {
return false;
}
for (const auto& e : entries_) {
if (e.scancode == sc) return true;
if (e.scancode == sc) {
return true;
}
}
return false;
}
@@ -149,12 +167,16 @@ namespace KeyConfig {
}
auto saveOverrides() -> bool {
if (overrides_path_.empty()) return false;
if (overrides_path_.empty()) {
return false;
}
// Recull només les entrades remapeades.
std::vector<const KeyEntry*> changed;
for (const auto& e : entries_) {
if (e.scancode != e.default_scancode) changed.push_back(&e);
if (e.scancode != e.default_scancode) {
changed.push_back(&e);
}
}
std::ofstream file(overrides_path_);
+3 -1
View File
@@ -17,7 +17,9 @@ namespace KeyRemap {
void update() {
const bool* ks = SDL_GetKeyboardState(nullptr);
if (!ks) return;
if (ks == nullptr) {
return;
}
mirror(Options::keys_game.up, SDL_SCANCODE_UP, ks);
mirror(Options::keys_game.down, SDL_SCANCODE_DOWN, ks);
mirror(Options::keys_game.left, SDL_SCANCODE_LEFT, ks);
+43 -24
View File
@@ -42,18 +42,18 @@ void JD8_ClearScreen(Uint8 color) {
memset(screen, color, 64000);
}
JD8_Surface JD8_NewSurface() {
auto JD8_NewSurface() -> JD8_Surface {
return new Uint8[64000]{};
}
// Helper intern: deriva el basename d'una ruta per a buscar al Cache.
static std::string jd8_basename(const char* file) {
static auto jd8_basename(const char* file) -> std::string {
std::string s = file;
auto pos = s.find_last_of("/\\");
return pos == std::string::npos ? s : s.substr(pos + 1);
}
JD8_Surface JD8_LoadSurface(const char* file) {
auto JD8_LoadSurface(const char* file) -> JD8_Surface {
// Prova primer el Resource::Cache. Si l'asset és precarregat, copiem
// els 64KB des del cache (microsegons) i ens estalviem la decodificació
// GIF. Mantenim el contracte de la funció: el caller rep un buffer
@@ -70,7 +70,8 @@ JD8_Surface JD8_LoadSurface(const char* file) {
}
auto buffer = ResourceHelper::loadFile(file);
unsigned short w, h;
unsigned short w;
unsigned short h;
Uint8* pixels = LoadGif(buffer.data(), &w, &h);
if (pixels == nullptr) {
printf("Unable to load bitmap: %s\n", SDL_GetError());
@@ -82,11 +83,11 @@ JD8_Surface JD8_LoadSurface(const char* file) {
return image;
}
JD8_Palette JD8_LoadPalette(const char* file) {
auto JD8_LoadPalette(const char* file) -> JD8_Palette {
// Sempre retorna un buffer de 256 colors reservat amb `new Color[256]`
// — el caller és responsable d'alliberar-lo amb `delete[]` (o lliurar-ne
// l'ownership a `JD8_SetScreenPalette`).
JD8_Palette palette = new Color[256];
auto palette = new Color[256];
if (Resource::Cache::get() != nullptr) {
try {
@@ -106,7 +107,9 @@ JD8_Palette JD8_LoadPalette(const char* file) {
}
void JD8_SetScreenPalette(JD8_Palette palette) {
if (main_palette == palette) return;
if (main_palette == palette) {
return;
}
delete[] main_palette;
main_palette = palette;
}
@@ -126,9 +129,15 @@ void JD8_FillRect(int x, int y, int w, int h, Uint8 color) {
h += y;
y = 0;
}
if (x + w > 320) w = 320 - x;
if (y + h > 200) h = 200 - y;
if (w <= 0 || h <= 0) return;
if (x + w > 320) {
w = 320 - x;
}
if (y + h > 200) {
h = 200 - y;
}
if (w <= 0 || h <= 0) {
return;
}
for (int row = y; row < y + h; ++row) {
memset(&screen[x + (row * 320)], color, w);
}
@@ -158,47 +167,55 @@ void JD8_BlitToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int sw
}
}
void JD8_BlitCK(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey) {
void JD8_BlitCK(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey) {
int src_pointer = sx + (sy * 320);
int dst_pointer = x + (y * 320);
for (int j = 0; j < sh; j++) {
for (int i = 0; i < sw; i++) {
if (surface[src_pointer + i] != colorkey) screen[dst_pointer + i] = surface[src_pointer + i];
if (surface[src_pointer + i] != colorkey) {
screen[dst_pointer + i] = surface[src_pointer + i];
}
}
src_pointer += 320;
dst_pointer += 320;
}
}
void JD8_BlitCKCut(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey) {
void JD8_BlitCKCut(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey) {
int src_pointer = sx + (sy * 320);
int dst_pointer = x + (y * 320);
for (int j = 0; j < sh; j++) {
for (int i = 0; i < sw; i++) {
if (surface[src_pointer + i] != colorkey && (x + i >= 0) && (y + j >= 0) && (x + i < 320) && (y + j < 200)) screen[dst_pointer + i] = surface[src_pointer + i];
if (surface[src_pointer + i] != colorkey && (x + i >= 0) && (y + j >= 0) && (x + i < 320) && (y + j < 200)) {
screen[dst_pointer + i] = surface[src_pointer + i];
}
}
src_pointer += 320;
dst_pointer += 320;
}
}
void JD8_BlitCKScroll(int y, JD8_Surface surface, int sx, int sy, int sh, Uint8 colorkey) {
void JD8_BlitCKScroll(int y, const JD8_Surface surface, int sx, int sy, int sh, Uint8 colorkey) {
int dst_pointer = y * 320;
for (int j = sy; j < sy + sh; j++) {
for (int i = 0; i < 320; i++) {
int x = (i + sx) % 320;
if (surface[x + j * 320] != colorkey) screen[dst_pointer] = surface[x + j * 320];
if (surface[x + (j * 320)] != colorkey) {
screen[dst_pointer] = surface[x + (j * 320)];
}
dst_pointer++;
}
}
}
void JD8_BlitCKToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest, Uint8 colorkey) {
void JD8_BlitCKToSurface(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest, Uint8 colorkey) {
int src_pointer = sx + (sy * 320);
int dst_pointer = x + (y * 320);
for (int j = 0; j < sh; j++) {
for (int i = 0; i < sw; i++) {
if (surface[src_pointer + i] != colorkey) dest[dst_pointer + i] = surface[src_pointer + i];
if (surface[src_pointer + i] != colorkey) {
dest[dst_pointer + i] = surface[src_pointer + i];
}
}
src_pointer += 320;
dst_pointer += 320;
@@ -218,15 +235,15 @@ void JD8_Flip() {
}
}
Uint32* JD8_GetFramebuffer() {
auto JD8_GetFramebuffer() -> Uint32* {
return pixel_data;
}
void JD8_FreeSurface(JD8_Surface surface) {
void JD8_FreeSurface(const JD8_Surface surface) {
delete[] surface;
}
Uint8 JD8_GetPixel(JD8_Surface surface, int x, int y) {
auto JD8_GetPixel(JD8_Surface surface, int x, int y) -> Uint8 {
return surface[x + (y * 320)];
}
@@ -292,12 +309,14 @@ void JD8_FadeStartToPal(JD8_Palette pal) {
fade_step = 0;
}
bool JD8_FadeIsActive() {
auto JD8_FadeIsActive() -> bool {
return fade_type != FadeType::None;
}
bool JD8_FadeTickStep() {
if (fade_type == FadeType::None) return true;
auto JD8_FadeTickStep() -> bool {
if (fade_type == FadeType::None) {
return true;
}
apply_fade_step();
fade_step++;
+12 -12
View File
@@ -16,11 +16,11 @@ void JD8_Quit();
void JD8_ClearScreen(Uint8 color);
JD8_Surface JD8_NewSurface();
auto JD8_NewSurface() -> JD8_Surface;
JD8_Surface JD8_LoadSurface(const char* file);
auto JD8_LoadSurface(const char* file) -> JD8_Surface;
JD8_Palette JD8_LoadPalette(const char* file);
auto JD8_LoadPalette(const char* file) -> JD8_Palette;
void JD8_SetScreenPalette(JD8_Palette palette);
@@ -36,13 +36,13 @@ void JD8_Blit(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh)
void JD8_BlitToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest);
void JD8_BlitCK(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey);
void JD8_BlitCK(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey);
void JD8_BlitCKCut(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey);
void JD8_BlitCKCut(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey);
void JD8_BlitCKScroll(int y, JD8_Surface surface, int sx, int sy, int sh, Uint8 colorkey);
void JD8_BlitCKScroll(int y, const JD8_Surface surface, int sx, int sy, int sh, Uint8 colorkey);
void JD8_BlitCKToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest, Uint8 colorkey);
void JD8_BlitCKToSurface(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest, Uint8 colorkey);
// Converteix la pantalla indexada a ARGB. El Director crida aquesta
// funció al final de cada tick i després llegeix el framebuffer via
@@ -51,11 +51,11 @@ void JD8_Flip();
// Accés al framebuffer ARGB de 320x200 actualitzat per l'última crida a
// JD8_Flip(). Propietat de jdraw8 — el caller no ha de lliberar-lo.
Uint32* JD8_GetFramebuffer();
auto JD8_GetFramebuffer() -> Uint32*;
void JD8_FreeSurface(JD8_Surface surface);
void JD8_FreeSurface(const JD8_Surface surface);
Uint8 JD8_GetPixel(JD8_Surface surface, int x, int y);
auto JD8_GetPixel(JD8_Surface surface, int x, int y) -> Uint8;
void JD8_PutPixel(JD8_Surface surface, int x, int y, Uint8 pixel);
@@ -70,8 +70,8 @@ void JD8_SetPaletteColor(Uint8 index, Uint8 r, Uint8 g, Uint8 b);
// L'embolcall `scenes::PaletteFade` ho fa més idiomàtic per a escenes.
void JD8_FadeStartOut();
void JD8_FadeStartToPal(JD8_Palette pal);
bool JD8_FadeTickStep();
bool JD8_FadeIsActive();
auto JD8_FadeTickStep() -> bool;
auto JD8_FadeIsActive() -> bool;
// JD_Font JD_LoadFont( char *file, int width, int height);
+25 -11
View File
@@ -27,12 +27,16 @@ namespace {
config.clear();
const std::string config_file = config_folder + "/config.txt";
std::ifstream fi(config_file);
if (!fi.is_open()) return;
if (!fi.is_open()) {
return;
}
std::string line;
while (std::getline(fi, line)) {
const auto eq = line.find('=');
if (eq == std::string::npos) continue;
if (eq == std::string::npos) {
continue;
}
config.push_back({line.substr(0, eq), line.substr(eq + 1)});
}
}
@@ -40,7 +44,9 @@ namespace {
void save_config_values() {
const std::string config_file = config_folder + "/config.txt";
std::ofstream fo(config_file);
if (!fo.is_open()) return;
if (!fo.is_open()) {
return;
}
for (const auto& pair : config) {
fo << pair.key << '=' << pair.value << '\n';
}
@@ -52,7 +58,7 @@ void file_setresourcefolder(const char* str) {
resource_folder = str;
}
const char* file_getresourcefolder() {
auto file_getresourcefolder() -> const char* {
return resource_folder.c_str();
}
@@ -76,9 +82,13 @@ void file_setconfigfolder(const char* foldername) {
// arranque dins del navegador. La config no persistirà entre recàrregues
// (MEMFS és volàtil); caldria IDBFS si volguéssem persistència a web.
struct passwd* pw = getpwuid(getuid());
const char* homedir = (pw && pw->pw_dir && pw->pw_dir[0]) ? pw->pw_dir : nullptr;
if (!homedir || !homedir[0]) homedir = getenv("HOME");
if (!homedir || !homedir[0]) homedir = "/tmp";
const char* homedir = ((pw != nullptr) && (pw->pw_dir != nullptr) && (pw->pw_dir[0] != 0)) ? pw->pw_dir : nullptr;
if ((homedir == nullptr) || (homedir[0] == 0)) {
homedir = getenv("HOME");
}
if ((homedir == nullptr) || (homedir[0] == 0)) {
homedir = "/tmp";
}
config_folder = std::string(homedir) + "/.config/" + foldername;
#endif
@@ -92,14 +102,16 @@ void file_setconfigfolder(const char* foldername) {
// volàtil al navegador de totes formes: ignorem l'error i continuem.
}
const char* file_getconfigfolder() {
auto file_getconfigfolder() -> const char* {
thread_local std::string folder;
folder = config_folder + "/";
return folder.c_str();
}
const char* file_getconfigvalue(const char* key) {
if (config.empty()) load_config_values();
auto file_getconfigvalue(const char* key) -> const char* {
if (config.empty()) {
load_config_values();
}
for (const auto& pair : config) {
if (pair.key == key) {
thread_local std::string value_cache;
@@ -111,7 +123,9 @@ const char* file_getconfigvalue(const char* key) {
}
void file_setconfigvalue(const char* key, const char* value) {
if (config.empty()) load_config_values();
if (config.empty()) {
load_config_values();
}
for (auto& pair : config) {
if (pair.key == key) {
pair.value = value;
+3 -3
View File
@@ -1,10 +1,10 @@
#pragma once
void file_setconfigfolder(const char* foldername);
const char* file_getconfigfolder();
auto file_getconfigfolder() -> const char*;
void file_setresourcefolder(const char* str);
const char* file_getresourcefolder();
auto file_getresourcefolder() -> const char*;
const char* file_getconfigvalue(const char* key);
auto file_getconfigvalue(const char* key) -> const char*;
void file_setconfigvalue(const char* key, const char* value);
+4 -4
View File
@@ -24,7 +24,7 @@ void JG_QuitSignal() {
quitting = true;
}
bool JG_Quitting() {
auto JG_Quitting() -> bool {
return quitting;
}
@@ -32,7 +32,7 @@ void JG_SetUpdateTicks(Uint32 milliseconds) {
update_ticks = milliseconds;
}
bool JG_ShouldUpdate() {
auto JG_ShouldUpdate() -> bool {
const Uint32 now = SDL_GetTicks();
if (now - update_time > update_ticks) {
update_time = now;
@@ -45,11 +45,11 @@ bool JG_ShouldUpdate() {
return false;
}
Uint32 JG_GetCycleCounter() {
auto JG_GetCycleCounter() -> Uint32 {
return cycle_counter;
}
Uint32 JG_GetDeltaMs() {
auto JG_GetDeltaMs() -> Uint32 {
const Uint32 now = SDL_GetTicks();
const Uint32 delta = now - last_delta_time;
last_delta_time = now;
+4 -4
View File
@@ -7,14 +7,14 @@ void JG_Finalize();
void JG_QuitSignal();
bool JG_Quitting();
auto JG_Quitting() -> bool;
void JG_SetUpdateTicks(Uint32 milliseconds);
bool JG_ShouldUpdate();
auto JG_ShouldUpdate() -> bool;
Uint32 JG_GetCycleCounter();
auto JG_GetCycleCounter() -> Uint32;
// Temps transcorregut (en ms) des de l'última crida a JG_GetDeltaMs.
// Helper per a la migració progressiva a time-based (Fase 4+).
Uint32 JG_GetDeltaMs();
auto JG_GetDeltaMs() -> Uint32;
+45 -22
View File
@@ -1,5 +1,6 @@
#include "core/jail/jinput.hpp"
#include <algorithm>
#include <cstring>
#include "core/system/director.hpp"
@@ -19,7 +20,7 @@ namespace {
// Temps restant en mil·lisegons durant el qual JI_KeyPressed/JI_AnyKey
// retornen false. Utilitzat per a evitar que pulsacions fortuïtes
// saltin cinemàtiques al començament.
float wait_ms = 0.0f;
float wait_ms = 0.0F;
// Per a calcular el delta entre crides a JI_Update sense que els callers
// hagen de passar-lo explícitament. Es reinicia a la primera crida.
@@ -29,7 +30,7 @@ namespace {
Uint8 virtual_keystates[JI_VSRC_COUNT][SDL_SCANCODE_COUNT] = {{0}};
Uint8 scancode_to_ascii(Uint8 scancode) {
auto scancode_to_ascii(Uint8 scancode) -> Uint8 {
if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
return static_cast<Uint8>('a' + (scancode - SDL_SCANCODE_A));
}
@@ -47,8 +48,12 @@ void JI_SetInputBlocked(bool blocked) {
}
void JI_SetVirtualKey(int scancode, int source, bool pressed) {
if (scancode < 0 || scancode >= SDL_SCANCODE_COUNT) return;
if (source < 0 || source >= JI_VSRC_COUNT) return;
if (scancode < 0 || scancode >= SDL_SCANCODE_COUNT) {
return;
}
if (source < 0 || source >= JI_VSRC_COUNT) {
return;
}
virtual_keystates[source][scancode] = pressed ? 1 : 0;
}
@@ -64,49 +69,67 @@ void JI_Update() {
// El director ha processat tots els events. Ací només refresquem
// el snapshot del teclat i consumim el flag de tecla polsada.
if (keystates == nullptr) {
keystates = SDL_GetKeyboardState(NULL);
keystates = SDL_GetKeyboardState(nullptr);
}
const Uint64 now = SDL_GetTicks();
if (last_update_tick == 0) last_update_tick = now;
const float delta_ms = static_cast<float>(now - last_update_tick);
if (last_update_tick == 0) {
last_update_tick = now;
}
const auto delta_ms = static_cast<float>(now - last_update_tick);
last_update_tick = now;
if (wait_ms > 0.0f) {
if (wait_ms > 0.0F) {
wait_ms -= delta_ms;
if (wait_ms < 0.0f) wait_ms = 0.0f;
wait_ms = std::max(wait_ms, 0.0f);
}
// Consumim el flag de "alguna tecla no-GUI polsada" del director
key_pressed = Director::get()->consumeKeyPressed();
}
bool JI_KeyPressed(int key) {
if (wait_ms > 0.0f || keystates == nullptr) return false;
auto JI_KeyPressed(int key) -> bool {
if (wait_ms > 0.0F || keystates == nullptr) {
return false;
}
// Input bloquejat (p.ex. menú flotant obert)
if (input_blocked) return false;
if (input_blocked) {
return false;
}
// ESC bloquejada pel Director (primera pulsació mostra notificació)
if (key == SDL_SCANCODE_ESCAPE && Director::get()->isEscBlocked()) return false;
if (key < 0 || key >= SDL_SCANCODE_COUNT) return false;
if (keystates[key] != 0) return true;
for (int src = 0; src < JI_VSRC_COUNT; src++) {
if (virtual_keystates[src][key] != 0) return true;
if (key == SDL_SCANCODE_ESCAPE && Director::get()->isEscBlocked()) {
return false;
}
if (key < 0 || key >= SDL_SCANCODE_COUNT) {
return false;
}
if (static_cast<int>(keystates[key]) != 0) {
return true;
}
for (auto& virtual_keystate : virtual_keystates) {
if (virtual_keystate[key] != 0) {
return true;
}
}
return false;
}
bool JI_CheatActivated(const char* cheat_code) {
auto JI_CheatActivated(const char* cheat_code) -> bool {
const size_t len = std::strlen(cheat_code);
if (len > sizeof(cheat)) return false;
if (len > sizeof(cheat)) {
return false;
}
// Compara contra els últims `len` caràcters del buffer. El buffer té
// mida fixa 5 i acumula sempre el darrer tecle a la posició 4.
const size_t offset = sizeof(cheat) - len;
for (size_t i = 0; i < len; i++) {
if (cheat[offset + i] != static_cast<Uint8>(cheat_code[i])) return false;
if (cheat[offset + i] != static_cast<Uint8>(cheat_code[i])) {
return false;
}
}
return true;
}
bool JI_AnyKey() {
return wait_ms > 0.0f ? false : key_pressed;
auto JI_AnyKey() -> bool {
return wait_ms > 0.0F ? false : key_pressed;
}
+4 -4
View File
@@ -12,14 +12,14 @@ void JI_SetInputBlocked(bool blocked);
enum JI_VirtualSource {
JI_VSRC_GAMEPAD = 0,
JI_VSRC_REMAP = 1,
JI_VSRC_COUNT
JI_VSRC_COUNT = 2
};
void JI_SetVirtualKey(int scancode, int source, bool pressed);
void JI_Update();
bool JI_KeyPressed(int key);
auto JI_KeyPressed(int key) -> bool;
bool JI_CheatActivated(const char* cheat_code);
auto JI_CheatActivated(const char* cheat_code) -> bool;
bool JI_AnyKey();
auto JI_AnyKey() -> bool;
+5 -3
View File
@@ -15,7 +15,7 @@ namespace Locale {
static void traverse(const fkyaml::node& node, const std::string& prefix) {
if (node.is_mapping()) {
for (auto it = node.begin(); it != node.end(); ++it) {
std::string key = it.key().get_value<std::string>();
auto key = it.key().get_value<std::string>();
std::string full = prefix.empty() ? key : prefix + "." + key;
traverse(it.value(), full);
}
@@ -26,7 +26,7 @@ namespace Locale {
}
}
bool load(const char* filename) {
auto load(const char* filename) -> bool {
auto buffer = ResourceHelper::loadFile(filename);
if (buffer.empty()) {
std::cerr << "Locale: unable to load " << filename << '\n';
@@ -48,7 +48,9 @@ namespace Locale {
auto get(const char* key) -> const char* {
auto it = strings_.find(key);
if (it != strings_.end()) return it->second.c_str();
if (it != strings_.end()) {
return it->second.c_str();
}
return key; // fallback: retorna la clau mateixa
}
+1 -1
View File
@@ -4,7 +4,7 @@
// Les claus són nested amb notació punt ("menu.items.zoom").
// Si una clau no existeix, Locale::get torna la clau mateixa (útil per debug).
namespace Locale {
bool load(const char* filename);
auto load(const char* filename) -> bool;
// Retorna la cadena associada a la clau. El punter és estable durant tota la
// sessió (no canvia), per tant es pot guardar en const char*.
+114 -72
View File
@@ -1,5 +1,6 @@
#include "core/rendering/menu.hpp"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <functional>
@@ -72,16 +73,20 @@ namespace Menu {
std::string subtitle; // opcional — si no buit, es dibuixa sota el títol
};
static bool isVisible(const Item& it) { return !it.visible || it.visible(); }
static auto isVisible(const Item& it) -> bool { return !it.visible || it.visible(); }
// Troba el pròxim ítem visible en direcció `dir` (±1) a partir de `from`.
// Si cap és visible retorna `from`.
static int nextVisibleCursor(const Page& p, int from, int dir) {
static auto nextVisibleCursor(const Page& p, int from, int dir) -> int {
const int n = static_cast<int>(p.items.size());
if (n <= 0) return from;
if (n <= 0) {
return from;
}
for (int i = 1; i <= n; ++i) {
int idx = ((from + dir * i) % n + n) % n;
if (isVisible(p.items[idx])) return idx;
if (isVisible(p.items[idx])) {
return idx;
}
}
return from;
}
@@ -97,7 +102,7 @@ namespace Menu {
// --- Transició entre pàgines ---
static constexpr float TRANSITION_SPEED = 5.5F; // ~180 ms
static Page transition_outgoing_{"", {}, 0};
static Page transition_outgoing_{.title = "", .items = {}, .cursor = 0};
static bool transition_active_{false};
static float transition_progress_{1.0F};
static int transition_dir_{+1}; // +1 endavant, -1 enrere
@@ -120,19 +125,19 @@ namespace Menu {
// --- Helpers ---
static std::string yesNo(bool b) { return b ? Locale::get("menu.values.yes") : Locale::get("menu.values.no"); }
static std::string onOff(bool b) { return b ? Locale::get("menu.values.on") : Locale::get("menu.values.off"); }
static auto yesNo(bool b) -> std::string { return b ? Locale::get("menu.values.yes") : Locale::get("menu.values.no"); }
static auto onOff(bool b) -> std::string { return b ? Locale::get("menu.values.on") : Locale::get("menu.values.off"); }
// --- Builders de pàgines ---
static Page buildVideo();
static Page buildAudio();
static Page buildControls();
static Page buildGame();
static Page buildSystem();
static auto buildVideo() -> Page;
static auto buildAudio() -> Page;
static auto buildControls() -> Page;
static auto buildGame() -> Page;
static auto buildSystem() -> Page;
static Page buildRoot() {
Page p{Locale::get("menu.titles.root"), {}, 0};
static auto buildRoot() -> Page {
Page p{.title = Locale::get("menu.titles.root"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.video"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildVideo()); }, nullptr});
p.items.push_back({Locale::get("menu.items.audio"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildAudio()); }, nullptr});
p.items.push_back({Locale::get("menu.items.controls"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildControls()); }, nullptr});
@@ -141,8 +146,8 @@ namespace Menu {
return p;
}
static Page buildVideo() {
Page p{Locale::get("menu.titles.video"), {}, 0};
static auto buildVideo() -> Page {
Page p{.title = Locale::get("menu.titles.video"), .items = {}, .cursor = 0};
// Zoom i fullscreen: sense sentit a WASM (el navegador posseix el canvas)
#ifndef __EMSCRIPTEN__
@@ -150,8 +155,9 @@ namespace Menu {
char buf[16];
std::snprintf(buf, sizeof(buf), "%dX", Screen::get()->getZoom());
return std::string(buf); }, [](int dir) {
if (dir < 0) Screen::get()->decZoom();
else if (dir > 0) Screen::get()->incZoom(); }, nullptr, nullptr});
if (dir < 0) { Screen::get()->decZoom();
} else if (dir > 0) { Screen::get()->incZoom();
} }, nullptr, nullptr});
p.items.push_back({Locale::get("menu.items.screen"), ItemKind::Toggle, [] { return std::string(Screen::get()->isFullscreen() ? Locale::get("menu.values.fullscreen") : Locale::get("menu.values.windowed")); }, [](int) { Screen::get()->toggleFullscreen(); }, nullptr, nullptr, nullptr});
#endif
@@ -185,15 +191,18 @@ namespace Menu {
p.items.push_back({Locale::get("menu.items.shader"), ItemKind::Toggle, [] { return onOff(Options::video.shader_enabled); }, [](int) { Screen::get()->toggleShaders(); }, nullptr, nullptr, nullptr});
p.items.push_back({Locale::get("menu.items.shader_type"), ItemKind::Cycle, [] { return std::string(Screen::get()->getActiveShaderName()); }, [](int dir) {
if (dir < 0) Screen::get()->prevShaderType();
else Screen::get()->nextShaderType(); }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
if (dir < 0) { Screen::get()->prevShaderType();
} else { Screen::get()->nextShaderType();
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
p.items.push_back({Locale::get("menu.items.preset"), ItemKind::Cycle, [] { return std::string(Screen::get()->getCurrentPresetName()); }, [](int dir) {
if (dir < 0) Screen::get()->prevPreset();
else Screen::get()->nextPreset(); }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
if (dir < 0) { Screen::get()->prevPreset();
} else { Screen::get()->nextPreset();
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
p.items.push_back({Locale::get("menu.items.supersampling"), ItemKind::Toggle, [] { return onOff(Options::video.supersampling); }, [](int) { Screen::get()->toggleSupersampling(); }, nullptr, nullptr, [] {
if (!Options::video.shader_enabled) return false;
if (!Options::video.shader_enabled) { return false;
}
const char* name = Screen::get()->getActiveShaderName();
return name && std::string(name) == "POSTFX"; }});
#endif
@@ -213,10 +222,10 @@ namespace Menu {
}
// Converteix volum 0..1 a percentatge i ho formata com "50%"
static std::string volPct(float v) {
int pct = static_cast<int>(v * 100.0F + 0.5F);
if (pct < 0) pct = 0;
if (pct > 100) pct = 100;
static auto volPct(float v) -> std::string {
int pct = static_cast<int>((v * 100.0F) + 0.5F);
pct = std::max(pct, 0);
pct = std::min(pct, 100);
char buf[8];
std::snprintf(buf, sizeof(buf), "%d%%", pct);
return std::string(buf);
@@ -225,13 +234,13 @@ namespace Menu {
// Canvi +/- d'un volum en steps de 0.05 (5%) amb clamping
static void stepVolume(float& v, int dir) {
v += (dir >= 0 ? 0.05F : -0.05F);
if (v < 0.0F) v = 0.0F;
if (v > 1.0F) v = 1.0F;
v = std::max(v, 0.0F);
v = std::min(v, 1.0F);
Options::applyAudio();
}
static Page buildControls() {
Page p{Locale::get("menu.titles.controls"), {}, 0};
static auto buildControls() -> Page {
Page p{.title = Locale::get("menu.titles.controls"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.move_up"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.up});
p.items.push_back({Locale::get("menu.items.move_down"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.down});
p.items.push_back({Locale::get("menu.items.move_left"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.left});
@@ -240,8 +249,8 @@ namespace Menu {
return p;
}
static Page buildAudio() {
Page p{Locale::get("menu.titles.audio"), {}, 0};
static auto buildAudio() -> Page {
Page p{.title = Locale::get("menu.titles.audio"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.master_enable"), ItemKind::Toggle, [] { return onOff(Options::audio.enabled); }, [](int) {
Options::audio.enabled = !Options::audio.enabled;
@@ -264,8 +273,8 @@ namespace Menu {
return p;
}
static Page buildGame() {
Page p{Locale::get("menu.titles.game"), {}, 0};
static auto buildGame() -> Page {
Page p{.title = Locale::get("menu.titles.game"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.use_new_logo"), ItemKind::Toggle, [] { return yesNo(Options::game.use_new_logo); }, [](int) { Options::game.use_new_logo = !Options::game.use_new_logo; }, nullptr});
@@ -276,19 +285,23 @@ namespace Menu {
return p;
}
static Page buildSystem() {
Page p{Locale::get("menu.titles.system"), {}, 0};
static auto buildSystem() -> Page {
Page p{.title = Locale::get("menu.titles.system"), .items = {}, .cursor = 0};
p.subtitle = std::string("v") + Texts::VERSION + " (" + Version::GIT_HASH + ")";
p.items.push_back({Locale::get("menu.items.restart"), ItemKind::Action, nullptr, nullptr, [] {
if (Director::get()) Director::get()->requestRestart();
if (Director::get()) {
Director::get()->requestRestart();
}
},
nullptr,
nullptr});
#ifndef __EMSCRIPTEN__
p.items.push_back({Locale::get("menu.items.exit_game"), ItemKind::Action, nullptr, nullptr, [] {
if (Director::get()) Director::get()->requestQuit();
if (Director::get()) {
Director::get()->requestQuit();
}
},
nullptr,
nullptr});
@@ -307,10 +320,14 @@ namespace Menu {
const Uint8 sb = (src_argb >> 16) & 0xFF;
const Uint8 inv = 255 - sa;
for (int row = y; row < y + h; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = x; col < x + w; col++) {
if (col < 0 || col >= SCREEN_W) continue;
Uint32* p = &buf[col + row * SCREEN_W];
if (col < 0 || col >= SCREEN_W) {
continue;
}
Uint32* p = &buf[col + (row * SCREEN_W)];
Uint32 dst = *p;
Uint8 dr = dst & 0xFF;
Uint8 dg = (dst >> 8) & 0xFF;
@@ -318,17 +335,21 @@ namespace Menu {
Uint8 r = (sr * sa + dr * inv) / 255;
Uint8 g = (sg * sa + dg * inv) / 255;
Uint8 b = (sb * sa + db * inv) / 255;
*p = 0xFF000000u | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
*p = 0xFF000000U | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
}
}
}
static void fillRect(Uint32* buf, int x, int y, int w, int h, Uint32 color) {
for (int row = y; row < y + h; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = x; col < x + w; col++) {
if (col < 0 || col >= SCREEN_W) continue;
buf[col + row * SCREEN_W] = color;
if (col < 0 || col >= SCREEN_W) {
continue;
}
buf[col + (row * SCREEN_W)] = color;
}
}
}
@@ -343,12 +364,14 @@ namespace Menu {
// Mida final de la caixa segons el nombre d'items *visibles*.
// body = (N-1) * ITEM_SPACING + charH — així BOTTOM_PAD és el buit real
// sota el text del darrer ítem, no un buit extra per sobre d'un "slot" buit.
static int boxHeight(const Page& page) {
static auto boxHeight(const Page& page) -> int {
int n = 0;
for (const auto& it : page.items) {
if (isVisible(it)) ++n;
if (isVisible(it)) {
++n;
}
}
int body = (n == 0) ? 8 : (n - 1) * ITEM_SPACING + 8;
int body = (n == 0) ? 8 : ((n - 1) * ITEM_SPACING) + 8;
int header = HEADER_H + (page.subtitle.empty() ? 0 : SUBTITLE_H);
return header + body + BOTTOM_PAD;
}
@@ -403,7 +426,9 @@ namespace Menu {
// render() faça decréixer open_anim_ fins a 0. En aquell moment es neteja
// l'estat. Si es crida estant ja tancat o tancant-se, no-op.
void close() {
if (stack_.empty() || closing_) return;
if (stack_.empty() || closing_) {
return;
}
closing_ = true;
capturing_ = nullptr;
transition_active_ = false;
@@ -416,7 +441,9 @@ namespace Menu {
}
void captureKey(SDL_Scancode sc) {
if (!capturing_) return;
if (capturing_ == nullptr) {
return;
}
if (sc == SDL_SCANCODE_ESCAPE) {
// Cancel·la
capturing_ = nullptr;
@@ -427,15 +454,18 @@ namespace Menu {
}
void handleKey(SDL_Scancode sc) {
if (!isOpen()) return;
if (!isOpen()) {
return;
}
Page& page = stack_.back();
if (page.items.empty()) {
// Pàgina buida — només backspace surt
if (sc == SDL_SCANCODE_BACKSPACE) {
if (stack_.size() > 1)
if (stack_.size() > 1) {
popPage();
else
} else {
close();
}
}
return;
}
@@ -467,7 +497,9 @@ namespace Menu {
case SDL_SCANCODE_KP_ENTER:
if (page.items[page.cursor].kind == ItemKind::Submenu ||
page.items[page.cursor].kind == ItemKind::Action) {
if (page.items[page.cursor].enter) page.items[page.cursor].enter();
if (page.items[page.cursor].enter) {
page.items[page.cursor].enter();
}
} else if (page.items[page.cursor].kind == ItemKind::KeyBind) {
capturing_ = page.items[page.cursor].scancode;
} else if (page.items[page.cursor].change) {
@@ -475,10 +507,11 @@ namespace Menu {
}
break;
case SDL_SCANCODE_BACKSPACE:
if (stack_.size() > 1)
if (stack_.size() > 1) {
popPage();
else
} else {
close();
}
break;
default:
break;
@@ -500,7 +533,7 @@ namespace Menu {
static void renderPageContent(Uint32* pixel_data, const Page& page, int box_x, int box_y, int x_offset, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) {
// Títol
int title_w = font_->width(page.title);
int title_x = box_x + (BOX_W - title_w) / 2 + x_offset;
int title_x = box_x + ((BOX_W - title_w) / 2) + x_offset;
font_->drawClipped(pixel_data, title_x, box_y + TITLE_PAD_Y, page.title, TITLE_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
// Línia sota el títol (també lliscada) — clippada manualment
@@ -519,26 +552,31 @@ namespace Menu {
int items_y = title_line_y + 4;
if (!page.subtitle.empty()) {
int sub_w = font_->width(page.subtitle.c_str());
int sub_x = box_x + (BOX_W - sub_w) / 2 + x_offset;
int sub_x = box_x + ((BOX_W - sub_w) / 2) + x_offset;
font_->drawClipped(pixel_data, sub_x, items_y, page.subtitle.c_str(), LABEL_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
items_y += SUBTITLE_H;
}
// Compta visibles — si cap, dibuixa placeholder (caixa totalment col·lapsada però oberta)
int visible_count = 0;
for (const auto& it : page.items)
if (isVisible(it)) ++visible_count;
for (const auto& it : page.items) {
if (isVisible(it)) {
++visible_count;
}
}
if (visible_count == 0) {
const char* empty_text = Locale::get("menu.values.empty");
int ew = font_->width(empty_text);
font_->drawClipped(pixel_data, box_x + (BOX_W - ew) / 2 + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
font_->drawClipped(pixel_data, box_x + ((BOX_W - ew) / 2) + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
return;
}
int y_slot = 0; // índex de fila visible (independent de l'índex real de l'ítem)
for (size_t i = 0; i < page.items.size(); i++) {
const Item& item = page.items[i];
if (!isVisible(item)) continue;
int y = items_y + y_slot * ITEM_SPACING;
if (!isVisible(item)) {
continue;
}
int y = items_y + (y_slot * ITEM_SPACING);
++y_slot;
bool selected = (static_cast<int>(i) == page.cursor);
Uint32 label_color = selected ? CURSOR_COLOR : LABEL_COLOR;
@@ -546,7 +584,7 @@ namespace Menu {
// Action: sense valor a la dreta — centrem el label amb el cursor just a l'esquerra.
if (item.kind == ItemKind::Action) {
int lw = font_->width(item.label);
int lx = box_x + (BOX_W - lw) / 2 + x_offset;
int lx = box_x + ((BOX_W - lw) / 2) + x_offset;
if (selected) {
font_->drawClipped(pixel_data, lx - font_->width("> "), y, ">", CURSOR_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
}
@@ -567,8 +605,10 @@ namespace Menu {
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - aw + x_offset, y, arrow, ac, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
} else if (item.kind == ItemKind::KeyBind) {
bool this_capturing = (capturing_ == item.scancode);
const char* text = this_capturing ? Locale::get("menu.values.press_key") : (item.scancode ? SDL_GetScancodeName(*item.scancode) : "");
if (!text || !*text) text = Locale::get("menu.values.unknown");
const char* text = this_capturing ? Locale::get("menu.values.press_key") : ((item.scancode != nullptr) ? SDL_GetScancodeName(*item.scancode) : "");
if ((text == nullptr) || (*text == 0)) {
text = Locale::get("menu.values.unknown");
}
int tw = font_->width(text);
Uint32 tc = this_capturing ? 0xFF00FFFF : (selected ? CURSOR_COLOR : VALUE_COLOR);
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - tw + x_offset, y, text, tc, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
@@ -582,7 +622,9 @@ namespace Menu {
}
void render(Uint32* pixel_data) {
if (!isVisible() || !font_ || !pixel_data) return;
if (!isVisible() || !font_ || (pixel_data == nullptr)) {
return;
}
// Delta time
Uint32 now = SDL_GetTicks();
@@ -600,7 +642,7 @@ namespace Menu {
}
} else if (open_anim_ < 1.0F) {
open_anim_ += OPEN_SPEED * dt;
if (open_anim_ > 1.0F) open_anim_ = 1.0F;
open_anim_ = std::min(open_anim_, 1.0F);
}
// Avança transició
@@ -626,7 +668,7 @@ namespace Menu {
animated_h_ = static_cast<float>(current_h);
} else {
float t = HEIGHT_RATE * dt;
if (t > 1.0F) t = 1.0F;
t = std::min(t, 1.0F);
animated_h_ += diff * t;
}
}
@@ -643,12 +685,12 @@ namespace Menu {
// Caixa creix verticalment durant l'obertura
int box_h = static_cast<int>(target_h * eased);
if (box_h < 2) box_h = 2;
box_h = std::max(box_h, 2);
int box_x = (SCREEN_W - BOX_W) / 2;
int box_y = (SCREEN_H - box_h) / 2;
// Fons semi-transparent (alpha escalat per l'animació d'obertura)
Uint8 alpha = static_cast<Uint8>(BG_ALPHA * eased);
auto alpha = static_cast<Uint8>(BG_ALPHA * eased);
blendRect(pixel_data, box_x, box_y, BOX_W, box_h, BG_COLOR, alpha);
// El contingut només apareix quan la caixa és prou gran
+25 -19
View File
@@ -102,16 +102,22 @@ namespace Overlay {
// Pinta un rectangle sòlid dins els límits de la pantalla
static void drawRect(Uint32* pixel_data, int rx, int ry, int rw, int rh, Uint32 color) {
for (int row = ry; row < ry + rh; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = rx; col < rx + rw; col++) {
if (col < 0 || col >= SCREEN_W) continue;
pixel_data[col + row * SCREEN_W] = color;
if (col < 0 || col >= SCREEN_W) {
continue;
}
pixel_data[col + (row * SCREEN_W)] = color;
}
}
}
void render(Uint32* pixel_data) {
if (!font_ || !pixel_data) return;
if (!font_ || (pixel_data == nullptr)) {
return;
}
// Calcula delta time
Uint32 now = SDL_GetTicks();
@@ -148,7 +154,9 @@ namespace Overlay {
break;
}
if (notif.status == Status::FINISHED) continue;
if (notif.status == Status::FINISHED) {
continue;
}
// Posició segons el tipus
int box_x = 0;
@@ -180,7 +188,7 @@ namespace Overlay {
int line_y = box_y + NOTIF_PADDING_V;
for (const auto& line : notif.lines) {
int line_w = font_->width(line.c_str());
int line_x = box_x + (notif.box_w - line_w) / 2; // centrat dins la caixa
int line_x = box_x + ((notif.box_w - line_w) / 2); // centrat dins la caixa
if (notif.style == NotifStyle::SHADOW) {
font_->draw(pixel_data, line_x + 1, line_y + 1, line.c_str(), notif.accent_color);
} else if (notif.style == NotifStyle::OUTLINE) {
@@ -203,7 +211,7 @@ namespace Overlay {
// Mateix lloc: entra fins a 1
if (info_anim_ < 1.0F) {
info_anim_ += INFO_SLIDE_SPEED * dt;
if (info_anim_ > 1.0F) info_anim_ = 1.0F;
info_anim_ = std::min(info_anim_, 1.0F);
}
} else {
// Canvi: si visible_pos està OFF, commuta directament
@@ -225,10 +233,10 @@ namespace Overlay {
float target = seg.visible ? 1.0F : 0.0F;
if (seg.anim < target) {
seg.anim += SEG_SPEED * dt;
if (seg.anim > target) seg.anim = target;
seg.anim = std::min(seg.anim, target);
} else if (seg.anim > target) {
seg.anim -= SEG_SPEED * dt;
if (seg.anim < target) seg.anim = target;
seg.anim = std::max(seg.anim, target);
}
}
@@ -283,9 +291,7 @@ namespace Overlay {
}
// Elimina les acabades
notifications_.erase(
std::remove_if(notifications_.begin(), notifications_.end(), [](const Notification& n) { return n.status == Status::FINISHED; }),
notifications_.end());
std::erase_if(notifications_, [](const Notification& n) { return n.status == Status::FINISHED; });
// Si la notificació d'ESC ha desaparegut, reseteja l'estat
if (esc_waiting_ && notifications_.empty()) {
@@ -293,7 +299,7 @@ namespace Overlay {
}
// Indicador de pausa persistent (cantó superior dret)
if (Director::get() && Director::get()->isPaused()) {
if ((Director::get() != nullptr) && Director::get()->isPaused()) {
const char* pause_text = Locale::get("notifications.pause");
int w = font_->width(pause_text);
int x = SCREEN_W - w - 4;
@@ -357,9 +363,7 @@ namespace Overlay {
}
// Neteja notificacions finalitzades
notifications_.erase(
std::remove_if(notifications_.begin(), notifications_.end(), [](const Notification& n) { return n.status == Status::FINISHED; }),
notifications_.end());
std::erase_if(notifications_, [](const Notification& n) { return n.status == Status::FINISHED; });
// Menú flotant per damunt de tot (isVisible inclou l'animació de tancament)
if (Menu::isVisible()) {
@@ -392,7 +396,7 @@ namespace Overlay {
int max_w = 0;
for (const auto& line : lines) {
int w = font_->width(line.c_str());
if (w > max_w) max_w = w;
max_w = std::max(w, max_w);
}
notif.box_w = max_w + NOTIF_PADDING_H * 2;
int line_h = font_->charHeight();
@@ -414,7 +418,7 @@ namespace Overlay {
void setRenderInfoSegments(const char* s0, const char* s1, const char* s2, const char* s3, unsigned int mono_mask) {
const char* segs[INFO_SEGMENT_COUNT] = {s0, s1, s2, s3};
for (int i = 0; i < INFO_SEGMENT_COUNT; i++) {
info_segments_[i].mono_digits = (mono_mask >> i) & 1u;
info_segments_[i].mono_digits = (((mono_mask >> i) & 1U) != 0u);
if (segs[i] != nullptr && *segs[i] != '\0') {
info_segments_[i].text = segs[i];
info_segments_[i].visible = true;
@@ -425,7 +429,9 @@ namespace Overlay {
}
void startCredits() {
if (credits_phase_ != CreditsPhase::IDLE) return;
if (credits_phase_ != CreditsPhase::IDLE) {
return;
}
credits_phase_ = CreditsPhase::DELAY;
credits_timer_ = 0.0F;
}
+104 -41
View File
@@ -1,5 +1,6 @@
#include "core/rendering/screen.hpp"
#include <algorithm>
#include <cstdio>
#include <iostream>
@@ -76,15 +77,15 @@ Screen::Screen() {
calculateMaxZoom();
if (zoom_ < 1) zoom_ = 1;
if (zoom_ > max_zoom_) zoom_ = max_zoom_;
zoom_ = std::max(zoom_, 1);
zoom_ = std::min(zoom_, max_zoom_);
// Clamp de la resolució interna a [1, max_zoom_]. Llegir del YAML i
// ajustar aquí és l'únic moment en què es fa — el menú re-clampa cada
// canvi. Si la pantalla és més petita que el valor desat (p.ex. canvi
// de monitor), baixem al màxim suportat.
if (Options::video.internal_resolution < 1) Options::video.internal_resolution = 1;
if (Options::video.internal_resolution > max_zoom_) Options::video.internal_resolution = max_zoom_;
Options::video.internal_resolution = std::max(Options::video.internal_resolution, 1);
Options::video.internal_resolution = std::min(Options::video.internal_resolution, max_zoom_);
int w = GAME_WIDTH * zoom_;
int h = Options::video.aspect_ratio_4_3 ? static_cast<int>(GAME_HEIGHT * 1.2F) * zoom_ : GAME_HEIGHT * zoom_;
@@ -124,15 +125,25 @@ Screen::~Screen() {
if (shader_backend_) {
#ifndef NO_SHADERS
auto* gpu = dynamic_cast<Rendering::SDL3GPUShader*>(shader_backend_.get());
if (gpu) gpu->destroy();
if (gpu != nullptr) {
gpu->destroy();
}
#endif
shader_backend_.reset();
}
if (internal_texture_sdl_) SDL_DestroyTexture(internal_texture_sdl_);
if (texture_) SDL_DestroyTexture(texture_);
if (renderer_) SDL_DestroyRenderer(renderer_);
if (window_) SDL_DestroyWindow(window_);
if (internal_texture_sdl_ != nullptr) {
SDL_DestroyTexture(internal_texture_sdl_);
}
if (texture_ != nullptr) {
SDL_DestroyTexture(texture_);
}
if (renderer_ != nullptr) {
SDL_DestroyRenderer(renderer_);
}
if (window_ != nullptr) {
SDL_DestroyWindow(window_);
}
}
void Screen::initShaders() {
@@ -143,7 +154,9 @@ void Screen::initShaders() {
// curtcircuiten cap al fallback SDL_Renderer.
return;
#else
if (!Options::video.gpu_acceleration) return;
if (!Options::video.gpu_acceleration) {
return;
}
shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
@@ -282,19 +295,25 @@ void Screen::toggleFullscreen() {
}
void Screen::incZoom() {
if (fullscreen_ || zoom_ >= max_zoom_) return;
if (fullscreen_ || zoom_ >= max_zoom_) {
return;
}
zoom_++;
adjustWindowSize();
}
void Screen::decZoom() {
if (fullscreen_ || zoom_ <= 1) return;
if (fullscreen_ || zoom_ <= 1) {
return;
}
zoom_--;
adjustWindowSize();
}
void Screen::setZoom(int zoom) {
if (zoom < 1 || zoom > max_zoom_ || fullscreen_) return;
if (zoom < 1 || zoom > max_zoom_ || fullscreen_) {
return;
}
zoom_ = zoom;
adjustWindowSize();
}
@@ -310,9 +329,15 @@ auto Screen::toggleSupersampling() -> bool {
// SS només té sentit amb shaders on i pipeline PostFX (el Lanczos downscale
// i el camí SS s'apliquen al pas de PostFX; CRTPI fa el seu propi
// submostreig intern i no usa aquesta via).
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (shader_backend_->getActiveShader() != Rendering::ShaderType::POSTFX) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() != Rendering::ShaderType::POSTFX) {
return false;
}
Options::video.supersampling = !Options::video.supersampling;
shader_backend_->setOversample(Options::video.supersampling ? 3 : 1);
return true;
@@ -366,9 +391,11 @@ void Screen::cycleTextureFilter(int dir) {
void Screen::changeInternalResolution(int dir) {
int next = Options::video.internal_resolution + (dir >= 0 ? 1 : -1);
if (next < 1) next = 1;
if (next > max_zoom_) next = max_zoom_;
if (next == Options::video.internal_resolution) return;
next = std::max(next, 1);
next = std::min(next, max_zoom_);
if (next == Options::video.internal_resolution) {
return;
}
Options::video.internal_resolution = next;
// Propaga al backend actiu. Al fallback path, la textura es recrea al
@@ -381,8 +408,12 @@ void Screen::changeInternalResolution(int dir) {
}
auto Screen::nextShaderType() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
shader_backend_->setActiveShader(Rendering::ShaderType::CRTPI);
@@ -397,16 +428,24 @@ auto Screen::nextShaderType() -> bool {
}
auto Screen::nextPreset() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::postfx_presets.empty()) return false;
if (Options::postfx_presets.empty()) {
return false;
}
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
Options::video.current_postfx_preset = Options::postfx_presets[Options::current_postfx_preset].name;
applyCurrentPostFXPreset();
} else {
if (Options::crtpi_presets.empty()) return false;
if (Options::crtpi_presets.empty()) {
return false;
}
Options::current_crtpi_preset = (Options::current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
Options::video.current_crtpi_preset = Options::crtpi_presets[Options::current_crtpi_preset].name;
applyCurrentCrtPiPreset();
@@ -420,17 +459,25 @@ auto Screen::prevShaderType() -> bool {
}
auto Screen::prevPreset() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::postfx_presets.empty()) return false;
if (Options::postfx_presets.empty()) {
return false;
}
int n = static_cast<int>(Options::postfx_presets.size());
Options::current_postfx_preset = (Options::current_postfx_preset - 1 + n) % n;
Options::video.current_postfx_preset = Options::postfx_presets[Options::current_postfx_preset].name;
applyCurrentPostFXPreset();
} else {
if (Options::crtpi_presets.empty()) return false;
if (Options::crtpi_presets.empty()) {
return false;
}
int n = static_cast<int>(Options::crtpi_presets.size());
Options::current_crtpi_preset = (Options::current_crtpi_preset - 1 + n) % n;
Options::video.current_crtpi_preset = Options::crtpi_presets[Options::current_crtpi_preset].name;
@@ -440,13 +487,17 @@ auto Screen::prevPreset() -> bool {
}
auto Screen::getCurrentPresetName() const -> const char* {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return "---";
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return "---";
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::current_postfx_preset < static_cast<int>(Options::postfx_presets.size()))
if (Options::current_postfx_preset < static_cast<int>(Options::postfx_presets.size())) {
return Options::postfx_presets[Options::current_postfx_preset].name.c_str();
}
} else {
if (Options::current_crtpi_preset < static_cast<int>(Options::crtpi_presets.size()))
if (Options::current_crtpi_preset < static_cast<int>(Options::crtpi_presets.size())) {
return Options::crtpi_presets[Options::current_crtpi_preset].name.c_str();
}
}
return "---";
}
@@ -458,7 +509,9 @@ void Screen::setActiveShader(Rendering::ShaderType type) {
}
void Screen::applyCurrentPostFXPreset() {
if (!shader_backend_ || Options::postfx_presets.empty()) return;
if (!shader_backend_ || Options::postfx_presets.empty()) {
return;
}
const auto& preset = Options::postfx_presets[Options::current_postfx_preset];
Rendering::PostFXParams p;
p.vignette = preset.vignette;
@@ -473,7 +526,9 @@ void Screen::applyCurrentPostFXPreset() {
}
void Screen::applyCurrentCrtPiPreset() {
if (!shader_backend_ || Options::crtpi_presets.empty()) return;
if (!shader_backend_ || Options::crtpi_presets.empty()) {
return;
}
const auto& preset = Options::crtpi_presets[Options::current_crtpi_preset];
Rendering::CrtPiParams p;
p.scanline_weight = preset.scanline_weight;
@@ -498,7 +553,9 @@ auto Screen::isHardwareAccelerated() const -> bool {
}
auto Screen::getActiveShaderName() const -> const char* {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return "SENSE GPU";
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return "SENSE GPU";
}
return shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX ? "POSTFX" : "CRT-PI";
}
@@ -534,7 +591,7 @@ void Screen::updateRenderInfo() {
fps_driver.c_str(),
shader_seg.empty() ? nullptr : shader_seg.c_str(),
ss_seg,
time_buf[0] ? time_buf : nullptr,
(time_buf[0] != 0) ? time_buf : nullptr,
0b1001);
}
@@ -544,7 +601,9 @@ void Screen::applyFallbackPresentation() {
SDL_ScaleMode scale = (Options::video.texture_filter == Options::TextureFilter::LINEAR)
? SDL_SCALEMODE_LINEAR
: SDL_SCALEMODE_NEAREST;
if (texture_) SDL_SetTextureScaleMode(texture_, scale);
if (texture_ != nullptr) {
SDL_SetTextureScaleMode(texture_, scale);
}
// Si 4:3 actiu, la finestra ja té aspect 4:3 (alçada × 1.2); STRETCH és
// l'única opció viable al path fallback (el GPU path fa l'upscale 4:3 abans
@@ -578,7 +637,9 @@ void Screen::applyFallbackPresentation() {
}
void Screen::ensureFallbackInternalTexture() {
if (renderer_ == nullptr) return;
if (renderer_ == nullptr) {
return;
}
const int mult = Options::video.internal_resolution;
if (mult <= 1) {
// No cal textura intermèdia — recicla si la teníem.
@@ -589,7 +650,9 @@ void Screen::ensureFallbackInternalTexture() {
}
return;
}
if (internal_texture_sdl_ != nullptr && internal_texture_mult_ == mult) return;
if (internal_texture_sdl_ != nullptr && internal_texture_mult_ == mult) {
return;
}
if (internal_texture_sdl_ != nullptr) {
SDL_DestroyTexture(internal_texture_sdl_);
@@ -620,11 +683,11 @@ void Screen::adjustWindowSize() {
void Screen::calculateMaxZoom() {
SDL_DisplayID display = SDL_GetPrimaryDisplay();
const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display);
if (mode) {
if (mode != nullptr) {
int max_w = mode->w / GAME_WIDTH;
int max_h = mode->h / GAME_HEIGHT;
max_zoom_ = (max_w < max_h) ? max_w : max_h;
if (max_zoom_ < 1) max_zoom_ = 1;
max_zoom_ = std::max(max_zoom_, 1);
}
}
@@ -906,8 +906,8 @@ namespace Rendering {
// ---- Calcular viewport (dimensions lògiques del canvas) ----
// Si 4:3 actiu, effective_height ja és 240 (la textura estirada)
const float logical_w = static_cast<float>(game_width_);
const float logical_h = static_cast<float>(effective_height);
const auto logical_w = static_cast<float>(game_width_);
const auto logical_h = static_cast<float>(effective_height);
float vx = 0.0F;
float vy = 0.0F;
@@ -1276,7 +1276,9 @@ namespace Rendering {
// Recrea la textura intermèdia amb les noves dimensions (320·N × 200·N).
void SDL3GPUShader::setInternalResolution(int multiplier) {
const int NEW = std::max(1, multiplier);
if (NEW == internal_res_) return;
if (NEW == internal_res_) {
return;
}
internal_res_ = NEW;
if (is_initialized_ && device_ != nullptr) {
SDL_WaitForGPUIdle(device_);
@@ -1286,7 +1288,9 @@ namespace Rendering {
void SDL3GPUShader::setStretch4_3(bool enabled) {
stretch_4_3_ = enabled;
if (!is_initialized_ || device_ == nullptr) return;
if (!is_initialized_ || device_ == nullptr) {
return;
}
// Recrear scaled_texture_ perquè tinga les dimensions correctes (amb o sense 4:3)
if (oversample_ > 1 && ss_factor_ > 0) {
@@ -1464,7 +1468,9 @@ namespace Rendering {
SDL_ReleaseGPUTexture(device_, internal_texture_);
internal_texture_ = nullptr;
}
if (internal_res_ <= 1 || device_ == nullptr) return true;
if (internal_res_ <= 1 || device_ == nullptr) {
return true;
}
const int W = game_width_ * internal_res_;
const int H = game_height_ * internal_res_;
+127 -57
View File
@@ -12,7 +12,7 @@
// Forward declarations de gif.h (inclòs des de jdraw8.cpp, no es pot incloure dos vegades)
struct rgb;
extern unsigned char* LoadGif(unsigned char* data, unsigned short* w, unsigned short* h);
extern auto LoadGif(unsigned char* data, unsigned short* w, unsigned short* h) -> unsigned char*;
Text::Text(const char* fnt_file, const char* gif_file) {
loadBitmap(gif_file);
@@ -23,7 +23,9 @@ Text::Text(const char* fnt_file, const char* gif_file) {
auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
auto byte = static_cast<uint8_t>(*ptr);
if (byte == 0) return 0;
if (byte == 0) {
return 0;
}
uint32_t cp = 0;
int extra = 0;
@@ -47,7 +49,9 @@ auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
ptr++;
for (int i = 0; i < extra; i++) {
auto cont = static_cast<uint8_t>(*ptr);
if ((cont & 0xC0) != 0x80) return 0xFFFD;
if ((cont & 0xC0) != 0x80) {
return 0xFFFD;
}
cp = (cp << 6) | (cont & 0x3F);
ptr++;
}
@@ -71,7 +75,9 @@ void Text::loadFont(const char* fnt_file) {
while (std::getline(stream, line)) {
// Ignora comentaris i línies buides
if (line.empty() || line[0] == '#') continue;
if (line.empty() || line[0] == '#') {
continue;
}
// Elimina comentaris inline
auto comment_pos = line.find('#');
@@ -133,9 +139,10 @@ void Text::loadBitmap(const char* gif_file) {
int w = raw[6] | (raw[7] << 8);
int h = raw[8] | (raw[9] << 8);
unsigned short gw = 0, gh = 0;
unsigned short gw = 0;
unsigned short gh = 0;
Uint8* pixels = LoadGif(raw, &gw, &gh);
if (!pixels) {
if (pixels == nullptr) {
std::cerr << "Text: unable to decode GIF: " << gif_file << '\n';
return;
}
@@ -151,14 +158,18 @@ void Text::loadBitmap(const char* gif_file) {
// --- Renderitzat ---
void Text::draw(Uint32* pixel_data, int x, int y, const char* text, Uint32 color) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -174,21 +185,27 @@ void Text::draw(Uint32* pixel_data, int x, int y, const char* text, Uint32 color
// Pinta glifo pixel a pixel
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = cursor_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
// Píxel no transparent (índex 0 és fons típicament)
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -204,17 +221,23 @@ void Text::drawCentered(Uint32* pixel_data, int y, const char* text, Uint32 colo
}
void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
// Descart ràpid si el glifo sencer cau fora verticalment
if (y + box_height_ <= clip_y_min || y >= clip_y_max) return;
if (y + box_height_ <= clip_y_min || y >= clip_y_max) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -235,21 +258,31 @@ void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint3
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < clip_y_min || dst_y >= clip_y_max) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
if (dst_y < clip_y_min || dst_y >= clip_y_max) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = cursor_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < clip_x_min || dst_x >= clip_x_max) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
if (dst_x < clip_x_min || dst_x >= clip_x_max) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -259,14 +292,18 @@ void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint3
}
void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int cell_w) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -279,23 +316,29 @@ void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 c
const auto& glyph = it->second;
// Centra el glif dins la cel·la
int glyph_x = cursor_x + (cell_w - glyph.w) / 2;
int glyph_x = cursor_x + ((cell_w - glyph.w) / 2);
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = glyph_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -305,21 +348,27 @@ void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 c
}
void Text::drawMonoDigits(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int digit_cell_w) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
it = glyphs_.find('?');
if (it == glyphs_.end()) {
if (!first) cursor_x += 1;
if (!first) {
cursor_x += 1;
}
cursor_x += box_width_;
first = false;
continue;
@@ -329,22 +378,30 @@ void Text::drawMonoDigits(Uint32* pixel_data, int x, int y, const char* text, Ui
const auto& glyph = it->second;
bool is_digit = (cp >= '0' && cp <= '9');
if (!first) cursor_x += 1; // kerning
if (!first) {
cursor_x += 1; // kerning
}
int glyph_x = is_digit ? cursor_x + (digit_cell_w - glyph.w) / 2 : cursor_x;
int glyph_x = is_digit ? cursor_x + ((digit_cell_w - glyph.w) / 2) : cursor_x;
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = glyph_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -358,32 +415,41 @@ auto Text::widthMonoDigits(const char* text, int digit_cell_w) const -> int {
const char* ptr = text;
int w = 0;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (!first) w += 1; // kerning
if (cp == 0) {
break;
}
if (!first) {
w += 1; // kerning
}
first = false;
bool is_digit = (cp >= '0' && cp <= '9');
if (is_digit) {
w += digit_cell_w;
} else {
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) it = glyphs_.find('?');
if (it != glyphs_.end())
if (it == glyphs_.end()) {
it = glyphs_.find('?');
}
if (it != glyphs_.end()) {
w += it->second.w;
else
} else {
w += box_width_;
}
}
}
return w;
}
auto Text::widthMono(const char* text, int cell_w) const -> int {
auto Text::widthMono(const char* text, int cell_w) -> int {
const char* ptr = text;
int count = 0;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
count++;
}
return count * cell_w;
@@ -394,16 +460,20 @@ auto Text::width(const char* text) const -> int {
int w = 0;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
it = glyphs_.find('?');
}
if (!first) w += 1; // kerning
if (!first) {
w += 1; // kerning
}
first = false;
if (it != glyphs_.end()) {
+1 -1
View File
@@ -28,7 +28,7 @@ class Text {
// Calcula ancho en píxeles d'un text
[[nodiscard]] auto width(const char* text) const -> int;
// Amplada mono: nombre de codepoints × cell_w
[[nodiscard]] auto widthMono(const char* text, int cell_w) const -> int;
[[nodiscard]] static auto widthMono(const char* text, int cell_w) -> int;
// Amplada mono-dígits: amplada natural, però substituint els dígits per digit_cell_w
[[nodiscard]] auto widthMonoDigits(const char* text, int digit_cell_w) const -> int;
[[nodiscard]] auto charHeight() const -> int { return box_height_; }
+23 -9
View File
@@ -15,8 +15,8 @@
// ni inline, així que no podem tornar-lo a incloure aquí. Ens fiem de les
// declaracions extern dels símbols que ens calen (linkatge C++ normal,
// igual que fa text.cpp).
extern unsigned char* LoadGif(unsigned char* data, unsigned short* w, unsigned short* h);
extern unsigned char* LoadPalette(unsigned char* data);
extern auto LoadGif(unsigned char* data, unsigned short* w, unsigned short* h) -> unsigned char*;
extern auto LoadPalette(unsigned char* data) -> unsigned char*;
namespace Resource {
@@ -90,7 +90,9 @@ namespace Resource {
}
auto Cache::getProgress() const -> float {
if (total_count_ == 0) return 1.0F;
if (total_count_ == 0) {
return 1.0F;
}
return static_cast<float>(loaded_count_) / static_cast<float>(total_count_);
}
@@ -102,7 +104,9 @@ namespace Resource {
}
auto Cache::loadStep(int budget_ms) -> bool {
if (stage_ == LoadStage::DONE) return true;
if (stage_ == LoadStage::DONE) {
return true;
}
const Uint64 start_ns = SDL_GetTicksNS();
const Uint64 budget_ns = static_cast<Uint64>(budget_ms) * 1'000'000ULL;
@@ -112,7 +116,9 @@ namespace Resource {
switch (stage_) {
case LoadStage::MUSICS: {
auto items = list->getListByType(List::Type::MUSIC);
if (stage_index_ == 0) musics_.clear();
if (stage_index_ == 0) {
musics_.clear();
}
if (stage_index_ >= items.size()) {
stage_ = LoadStage::SOUNDS;
stage_index_ = 0;
@@ -123,7 +129,9 @@ namespace Resource {
}
case LoadStage::SOUNDS: {
auto items = list->getListByType(List::Type::SOUND);
if (stage_index_ == 0) sounds_.clear();
if (stage_index_ == 0) {
sounds_.clear();
}
if (stage_index_ >= items.size()) {
stage_ = LoadStage::BITMAPS;
stage_index_ = 0;
@@ -134,7 +142,9 @@ namespace Resource {
}
case LoadStage::BITMAPS: {
auto items = list->getListByType(List::Type::BITMAP);
if (stage_index_ == 0) surfaces_.clear();
if (stage_index_ == 0) {
surfaces_.clear();
}
if (stage_index_ >= items.size()) {
stage_ = LoadStage::TEXT_FILES;
stage_index_ = 0;
@@ -148,7 +158,9 @@ namespace Resource {
auto font_items = list->getListByType(List::Type::FONT);
auto items = data_items;
items.insert(items.end(), font_items.begin(), font_items.end());
if (stage_index_ == 0) text_files_.clear();
if (stage_index_ == 0) {
text_files_.clear();
}
if (stage_index_ >= items.size()) {
stage_ = LoadStage::DONE;
stage_index_ = 0;
@@ -161,7 +173,9 @@ namespace Resource {
case LoadStage::DONE:
break;
}
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) break;
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) {
break;
}
}
return stage_ == LoadStage::DONE;
+6 -2
View File
@@ -16,13 +16,17 @@ namespace ResourceHelper {
auto readFromDisk(const std::string& relative_path) -> std::vector<uint8_t> {
const std::string full = std::string(file_getresourcefolder()) + relative_path;
std::ifstream file(full, std::ios::binary | std::ios::ate);
if (!file) return {};
if (!file) {
return {};
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> data(size);
if (!file.read(reinterpret_cast<char*>(data.data()), size)) return {};
if (!file.read(reinterpret_cast<char*>(data.data()), size)) {
return {};
}
return data;
}
} // namespace
+15 -5
View File
@@ -95,11 +95,21 @@ namespace Resource {
}
auto List::parseAssetType(const std::string& type_str) -> Type {
if (type_str == "DATA") return Type::DATA;
if (type_str == "BITMAP") return Type::BITMAP;
if (type_str == "MUSIC") return Type::MUSIC;
if (type_str == "SOUND") return Type::SOUND;
if (type_str == "FONT") return Type::FONT;
if (type_str == "DATA") {
return Type::DATA;
}
if (type_str == "BITMAP") {
return Type::BITMAP;
}
if (type_str == "MUSIC") {
return Type::MUSIC;
}
if (type_str == "SOUND") {
return Type::SOUND;
}
if (type_str == "FONT") {
return Type::FONT;
}
throw std::runtime_error("Unknown asset type: " + type_str);
}
+9 -3
View File
@@ -29,7 +29,9 @@ auto ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) -> uint32
}
void ResourcePack::encryptData(std::vector<uint8_t>& data, const std::string& key) {
if (key.empty()) return;
if (key.empty()) {
return;
}
for (size_t i = 0; i < data.size(); ++i) {
data[i] ^= static_cast<uint8_t>(key[i % key.length()]);
}
@@ -162,7 +164,9 @@ auto ResourcePack::addDirectory(const std::string& directory) -> bool {
}
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) {
if (!entry.is_regular_file()) continue;
if (!entry.is_regular_file()) {
continue;
}
std::string filepath = entry.path().string();
std::string filename = std::filesystem::relative(entry.path(), directory).string();
@@ -177,7 +181,9 @@ auto ResourcePack::addDirectory(const std::string& directory) -> bool {
auto ResourcePack::getResource(const std::string& filename) -> std::vector<uint8_t> {
auto it = resources_.find(filename);
if (it == resources_.end()) return {};
if (it == resources_.end()) {
return {};
}
const ResourceEntry& entry = it->second;
if (entry.offset + entry.size > data_.size()) {
+19 -15
View File
@@ -56,7 +56,7 @@ void Director::initGameContext() {
}
}
std::unique_ptr<scenes::Scene> Director::createNextScene() {
auto Director::createNextScene() const -> std::unique_ptr<scenes::Scene> {
// Mentre el Resource::Cache no haja acabat de precarregar, executem
// el BootLoaderScene — pinta una barra de progrés i avança la
// càrrega per pressupost de temps. Quan acaba, retorna i tornem ací
@@ -139,7 +139,7 @@ void Director::setup() {
has_frame_ = false;
}
bool Director::iterate() {
auto Director::iterate() -> bool {
if (quit_requested_) {
JG_QuitSignal();
current_scene_.reset(); // destrueix l'escena actual ordenadament
@@ -216,9 +216,13 @@ bool Director::iterate() {
// game_state_ i info::ctx. Si és impossible (game_state_ == -1,
// quit, o state no registrat), eixim del loop.
if (!current_scene_) {
if (game_state_ == -1 || JG_Quitting()) return false;
if (game_state_ == -1 || JG_Quitting()) {
return false;
}
current_scene_ = createNextScene();
if (!current_scene_) return false;
if (!current_scene_) {
return false;
}
current_scene_->onEnter();
last_tick_ms_ = SDL_GetTicks();
}
@@ -270,7 +274,9 @@ void Director::run() {
setup();
while (true) {
pollAllEvents();
if (!iterate()) break;
if (!iterate()) {
break;
}
}
teardown();
}
@@ -379,16 +385,14 @@ void Director::handleEvent(const SDL_Event& event) {
// Ja processat a KEY_DOWN, només deixem netejar el bloqueig
// quan l'overlay faça timeout
return;
} else {
// Comprova si és una tecla d'UI registrada (no passa al joc).
// KeyConfig::isGuiKey cobreix totes les tecles GUI a la vegada,
// incloent pause_toggle i menu_toggle (defensa en profunditat:
// aquestes ja s'haurien hagut de menjar al swallow d'amunt).
const auto sc = event.key.scancode;
if (!KeyConfig::isGuiKey(sc)) {
key_pressed_ = true;
JI_moveCheats(sc);
}
} // Comprova si és una tecla d'UI registrada (no passa al joc).
// KeyConfig::isGuiKey cobreix totes les tecles GUI a la vegada,
// incloent pause_toggle i menu_toggle (defensa en profunditat:
// aquestes ja s'haurien hagut de menjar al swallow d'amunt).
const auto sc = event.key.scancode;
if (!KeyConfig::isGuiKey(sc)) {
key_pressed_ = true;
JI_moveCheats(sc);
}
}
Mouse::handleEvent(event);
+6 -7
View File
@@ -28,13 +28,13 @@ class Director {
// per l'event loop de SDL3 en lloc d'un bucle propi — imprescindible
// per al port a emscripten, on el runtime posseïx el main loop.
void setup();
bool iterate(); // torna false quan el joc vol eixir
auto iterate() -> bool; // torna false quan el joc vol eixir
void teardown();
void handleEvent(const SDL_Event& event);
// Demana l'eixida (ex: segona pulsació d'ESC o SDL_QUIT)
void requestQuit();
auto isQuitRequested() const -> bool { return quit_requested_; }
[[nodiscard]] auto isQuitRequested() const -> bool { return quit_requested_; }
// Demana un reinici "suau": para música i sons, reseteja info::ctx i
// torna a l'intro (state 255). Es processa al començament del pròxim
@@ -45,14 +45,13 @@ class Director {
auto consumeKeyPressed() -> bool;
// Indica si ESC està bloquejada (el joc no l'ha de veure)
auto isEscBlocked() const -> bool { return esc_blocked_ || esc_swallow_until_release_; }
[[nodiscard]] auto isEscBlocked() const -> bool { return esc_blocked_ || esc_swallow_until_release_; }
// Pausa: mentre està activa, iterate() no avança l'escena — es
// continua presentant el darrer frame amb overlay fresc.
void togglePause();
auto isPaused() const -> bool { return paused_; }
[[nodiscard]] auto isPaused() const -> bool { return paused_; }
public:
~Director();
private:
@@ -64,10 +63,10 @@ class Director {
// Inicialitza info::ctx a partir de Options::game.* i comprova trick.ini.
// Es crida una sola vegada des d'iterate() a la primera invocació.
void initGameContext();
static void initGameContext();
// Construeix l'escena apropiada segons game_state_ i info::ctx.
// Retorna nullptr si l'state actual no té escena registrada (bug).
std::unique_ptr<scenes::Scene> createNextScene();
auto createNextScene() const -> std::unique_ptr<scenes::Scene>;
// Buffers persistents entre iteracions. Abans eren locals a run(),
// ara són membres perquè iterate() els pot reutilitzar sense tornar-los