From 0142d79d9179bc2e13eafb9aa832f37da17759c3 Mon Sep 17 00:00:00 2001 From: Raimon Zamora Date: Sat, 11 Apr 2026 16:39:47 +0200 Subject: [PATCH] - [WIP] Puta que susto, una regla mal feta en el .gitignore i ja no comitaba res - Canvi de comp --- .gitignore | 4 +- source/mini/audio/audio.cpp | 115 +++ source/mini/audio/audio.h | 38 + source/mini/audio/jail_audio.cpp | 509 +++++++++++ source/mini/audio/jail_audio.h | 42 + source/mini/config/config.cpp | 21 + source/mini/config/config.h | 15 + source/mini/draw/draw.cpp | 445 ++++++++++ source/mini/draw/draw.h | 58 ++ source/mini/file/file.cpp | 390 +++++++++ source/mini/file/file.h | 32 + source/mini/font/font.cpp | 121 +++ source/mini/font/font.h | 33 + source/mini/key/key.cpp | 49 ++ source/mini/key/key.h | 23 + source/mini/lua/lua.cpp | 171 ++++ source/mini/lua/lua.debug.cpp | 1360 ++++++++++++++++++++++++++++++ source/mini/lua/lua.debug.h | 17 + source/mini/lua/lua.h | 18 + source/mini/lua/lua.wrappers.cpp | 1322 +++++++++++++++++++++++++++++ source/mini/lua/lua.wrappers.h | 11 + source/mini/main.cpp | 31 + source/mini/mini.cpp | 586 +++++++++++++ source/mini/mini.h | 65 ++ source/mini/mouse/mouse.cpp | 44 + source/mini/mouse/mouse.h | 26 + source/mini/pad/pad.cpp | 48 ++ source/mini/pad/pad.h | 21 + source/mini/shader/shader.cpp | 267 ++++++ source/mini/shader/shader.h | 27 + source/mini/surf/surf.cpp | 203 +++++ source/mini/surf/surf.h | 56 ++ source/mini/version.h | 3 + source/mini/view/view.cpp | 28 + source/mini/view/view.h | 20 + source/mini/win/win.cpp | 105 +++ source/mini/win/win.h | 53 ++ 37 files changed, 6375 insertions(+), 2 deletions(-) create mode 100644 source/mini/audio/audio.cpp create mode 100644 source/mini/audio/audio.h create mode 100644 source/mini/audio/jail_audio.cpp create mode 100644 source/mini/audio/jail_audio.h create mode 100644 source/mini/config/config.cpp create mode 100644 source/mini/config/config.h create mode 100644 source/mini/draw/draw.cpp create mode 100644 source/mini/draw/draw.h create mode 100644 source/mini/file/file.cpp create mode 100644 source/mini/file/file.h create mode 100644 source/mini/font/font.cpp create mode 100644 source/mini/font/font.h create mode 100644 source/mini/key/key.cpp create mode 100644 source/mini/key/key.h create mode 100644 source/mini/lua/lua.cpp create mode 100644 source/mini/lua/lua.debug.cpp create mode 100644 source/mini/lua/lua.debug.h create mode 100644 source/mini/lua/lua.h create mode 100644 source/mini/lua/lua.wrappers.cpp create mode 100644 source/mini/lua/lua.wrappers.h create mode 100644 source/mini/main.cpp create mode 100644 source/mini/mini.cpp create mode 100644 source/mini/mini.h create mode 100644 source/mini/mouse/mouse.cpp create mode 100644 source/mini/mouse/mouse.h create mode 100644 source/mini/pad/pad.cpp create mode 100644 source/mini/pad/pad.h create mode 100644 source/mini/shader/shader.cpp create mode 100644 source/mini/shader/shader.h create mode 100644 source/mini/surf/surf.cpp create mode 100644 source/mini/surf/surf.h create mode 100644 source/mini/version.h create mode 100644 source/mini/view/view.cpp create mode 100644 source/mini/view/view.h create mode 100644 source/mini/win/win.cpp create mode 100644 source/mini/win/win.h diff --git a/.gitignore b/.gitignore index 3bdc774..4c1c383 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -mini.exe -mini +/mini.exe +/mini mini_debug.exe mini_debug .vscode/* diff --git a/source/mini/audio/audio.cpp b/source/mini/audio/audio.cpp new file mode 100644 index 0000000..361e729 --- /dev/null +++ b/source/mini/audio/audio.cpp @@ -0,0 +1,115 @@ +#include "audio.h" +#include "jail_audio.h" +#include "mini/file/file.h" + +namespace mini +{ + namespace audio + { + JA_Music_t *current_music = NULL; + #define MAX_SOUNDS 50 + JA_Sound_t *sounds[MAX_SOUNDS]; + + void init() { + JA_Init(48000, SDL_AUDIO_S16, 1); + for (int i=0;i + +#include + +#define JA_MAX_SIMULTANEOUS_CHANNELS 5 + +struct JA_Sound_t +{ + SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 }; + Uint32 length { 0 }; + Uint8 *buffer { NULL }; +}; + +struct JA_Channel_t +{ + JA_Sound_t *sound { nullptr }; + int pos { 0 }; + int times { 0 }; + SDL_AudioStream *stream { nullptr }; + JA_Channel_state state { JA_CHANNEL_FREE }; +}; + +struct JA_Music_t +{ + SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 }; + Uint32 length { 0 }; + Uint8 *buffer { nullptr }; + + int pos { 0 }; + int times { 0 }; + SDL_AudioStream *stream { nullptr }; + JA_Music_state state { JA_MUSIC_INVALID }; +}; + +JA_Music_t *current_music { nullptr }; +JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS]; + +SDL_AudioSpec JA_audioSpec { SDL_AUDIO_S16, 2, 48000 }; +float JA_musicVolume { 1.0f }; +float JA_soundVolume { 0.5f }; +bool JA_musicEnabled { true }; +bool JA_soundEnabled { true }; +SDL_AudioDeviceID sdlAudioDevice { 0 }; +SDL_TimerID JA_timerID { 0 }; + +bool fading = false; +int fade_start_time; +int fade_duration; +int fade_initial_volume; + +/* +void audioCallback(void * userdata, uint8_t * stream, int len) { + SDL_memset(stream, 0, len); + if (current_music != NULL && current_music->state == JA_MUSIC_PLAYING) { + const int size = SDL_min(len, current_music->samples*2-current_music->pos); + SDL_MixAudioFormat(stream, (Uint8*)(current_music->output+current_music->pos), AUDIO_S16, size, JA_musicVolume); + current_music->pos += size/2; + if (size < len) { + if (current_music->times != 0) { + SDL_MixAudioFormat(stream+size, (Uint8*)current_music->output, AUDIO_S16, len-size, JA_musicVolume); + current_music->pos = (len-size)/2; + if (current_music->times > 0) current_music->times--; + } else { + current_music->pos = 0; + current_music->state = JA_MUSIC_STOPPED; + } + } + } + // Mixar els channels mi amol + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { + if (channels[i].state == JA_CHANNEL_PLAYING) { + const int size = SDL_min(len, channels[i].sound->length - channels[i].pos); + SDL_MixAudioFormat(stream, channels[i].sound->buffer + channels[i].pos, AUDIO_S16, size, JA_soundVolume); + channels[i].pos += size; + if (size < len) { + if (channels[i].times != 0) { + SDL_MixAudioFormat(stream + size, channels[i].sound->buffer, AUDIO_S16, len-size, JA_soundVolume); + channels[i].pos = len-size; + if (channels[i].times > 0) channels[i].times--; + } else { + JA_StopChannel(i); + } + } + } + } +} +*/ + +Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval) +{ + if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING) + { + if (fading) { + int time = SDL_GetTicks(); + if (time > (fade_start_time+fade_duration)) { + fading = false; + JA_StopMusic(); + return 30; + } else { + const int time_passed = time - fade_start_time; + const float percent = (float)time_passed / (float)fade_duration; + SDL_SetAudioStreamGain(current_music->stream, 1.0 - percent); + } + } + + if (current_music->times != 0) + { + if (SDL_GetAudioStreamAvailable(current_music->stream) < int(current_music->length/2)) { + SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length); + } + if (current_music->times>0) current_music->times--; + } + else + { + if (SDL_GetAudioStreamAvailable(current_music->stream) == 0) JA_StopMusic(); + } + } + + if (JA_soundEnabled) + { + for (int i=0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) + if (channels[i].state == JA_CHANNEL_PLAYING) + { + if (channels[i].times != 0) + { + if (SDL_GetAudioStreamAvailable(channels[i].stream) < int(channels[i].sound->length/2)) + SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length); + if (channels[i].times>0) channels[i].times--; + } + } + else + { + if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i); + } + + } + + return 30; +} + +void JA_Init(const int freq, const SDL_AudioFormat format, const int channels) +{ + #ifdef DEBUG + SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG); + #endif + + JA_audioSpec = {format, channels, freq }; + if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); + sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec); + if (!sdlAudioDevice) { + log_msg(LOG_FAIL, "Failed to initialize SDL audio: %s\n", SDL_GetError()); + } else { + log_msg(LOG_OK, "Audio subsytem initialized\n"); + } + //SDL_PauseAudioDevice(sdlAudioDevice); + JA_timerID = SDL_AddTimer(30, JA_UpdateCallback, nullptr); +} + +void JA_Quit() +{ + if (JA_timerID) SDL_RemoveTimer(JA_timerID); + + if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); + sdlAudioDevice = 0; +} + +JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length) +{ + JA_Music_t *music = new JA_Music_t(); + + int chan, samplerate; + short *output; + music->length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2; + + music->spec.channels = chan; + music->spec.freq = samplerate; + music->spec.format = SDL_AUDIO_S16; + music->buffer = (Uint8*)SDL_malloc(music->length); + SDL_memcpy(music->buffer, output, music->length); + free(output); + music->pos = 0; + music->state = JA_MUSIC_STOPPED; + + return music; +} + +JA_Music_t *JA_LoadMusic(const char* filename) +{ + // [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid. + FILE *f = fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + Uint8 *buffer = (Uint8*)malloc(fsize + 1); + if (fread(buffer, fsize, 1, f)!=1) return NULL; + fclose(f); + + JA_Music_t *music = JA_LoadMusic(buffer, fsize); + + free(buffer); + + return music; +} + +void JA_PlayMusic(JA_Music_t *music, const int loop) +{ + if (!JA_musicEnabled) return; + + JA_StopMusic(); + + current_music = music; + current_music->pos = 0; + current_music->state = JA_MUSIC_PLAYING; + current_music->times = loop; + + current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec); + if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) log_msg(LOG_FAIL, "SDL_PutAudioStreamData failed!\n"); + SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume); + if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) log_msg(LOG_FAIL, "SDL_BindAudioStream failed!\n"); + //SDL_ResumeAudioStreamDevice(current_music->stream); +} + +void JA_PauseMusic() +{ + if (!JA_musicEnabled) return; + if (!current_music || current_music->state == JA_MUSIC_INVALID) return; + + current_music->state = JA_MUSIC_PAUSED; + //SDL_PauseAudioStreamDevice(current_music->stream); + SDL_UnbindAudioStream(current_music->stream); +} + +void JA_ResumeMusic() +{ + if (!JA_musicEnabled) return; + if (!current_music || current_music->state == JA_MUSIC_INVALID) return; + + current_music->state = JA_MUSIC_PLAYING; + //SDL_ResumeAudioStreamDevice(current_music->stream); + SDL_BindAudioStream(sdlAudioDevice, current_music->stream); +} + +void JA_StopMusic() +{ + if (!JA_musicEnabled) return; + if (!current_music || current_music->state == JA_MUSIC_INVALID) return; + + current_music->pos = 0; + current_music->state = JA_MUSIC_STOPPED; + //SDL_PauseAudioStreamDevice(current_music->stream); + SDL_DestroyAudioStream(current_music->stream); + current_music->stream = nullptr; +} + +void JA_FadeOutMusic(const int milliseconds) +{ + if (!JA_musicEnabled) return; + if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return; + + fading = true; + fade_start_time = SDL_GetTicks(); + fade_duration = milliseconds; + fade_initial_volume = JA_musicVolume; +} + +JA_Music_state JA_GetMusicState() +{ + if (!JA_musicEnabled) return JA_MUSIC_DISABLED; + if (!current_music) return JA_MUSIC_INVALID; + + return current_music->state; +} + +void JA_DeleteMusic(JA_Music_t *music) +{ + if (current_music == music) current_music = nullptr; + SDL_free(music->buffer); + if (music->stream) SDL_DestroyAudioStream(music->stream); + delete music; +} + +float JA_SetMusicVolume(float volume) +{ + JA_musicVolume = SDL_clamp( volume, 0.0f, 1.0f ); + if (current_music) SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume); + return JA_musicVolume; +} + +void JA_SetMusicPosition(float value) +{ + if (!current_music) return; + current_music->pos = value * current_music->spec.freq; +} + +float JA_GetMusicPosition() +{ + if (!current_music) return 0; + return float(current_music->pos)/float(current_music->spec.freq); +} + +void JA_EnableMusic(const bool value) +{ + if ( !value && current_music && (current_music->state==JA_MUSIC_PLAYING) ) JA_StopMusic(); + + JA_musicEnabled = value; +} + +const bool JA_IsMusicEnabled() +{ + return JA_musicEnabled; +} + + + + +JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length) +{ + JA_Sound_t *sound = new JA_Sound_t(); + sound->buffer = buffer; + sound->length = length; + return sound; +} + +JA_Sound_t *JA_LoadSound(uint8_t* buffer, uint32_t size) +{ + JA_Sound_t *sound = new JA_Sound_t(); + SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &sound->spec, &sound->buffer, &sound->length); + + return sound; +} + +JA_Sound_t *JA_LoadSound(const char* filename) +{ + JA_Sound_t *sound = new JA_Sound_t(); + SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length); + + return sound; +} + +int JA_PlaySound(JA_Sound_t *sound, const int loop) +{ + if (!JA_soundEnabled) return -1; + + int channel = 0; + while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; } + if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) channel = 0; + JA_StopChannel(channel); + + channels[channel].sound = sound; + channels[channel].times = loop; + channels[channel].pos = 0; + channels[channel].state = JA_CHANNEL_PLAYING; + channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec); + SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length); + SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume); + SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream); + + return channel; +} + +int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop) +{ + if (!JA_soundEnabled) return -1; + + if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return -1; + JA_StopChannel(channel); + + channels[channel].sound = sound; + channels[channel].times = loop; + channels[channel].pos = 0; + channels[channel].state = JA_CHANNEL_PLAYING; + channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec); + SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length); + SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume); + SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream); + + return channel; +} + +void JA_DeleteSound(JA_Sound_t *sound) +{ + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { + if (channels[i].sound == sound) JA_StopChannel(i); + } + SDL_free(sound->buffer); + delete sound; +} + +void JA_PauseChannel(const int channel) +{ + 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_PauseAudioStreamDevice(channels[i].stream); + SDL_UnbindAudioStream(channels[i].stream); + } + } + else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) + { + if (channels[channel].state == JA_CHANNEL_PLAYING) + { + channels[channel].state = JA_CHANNEL_PAUSED; + //SDL_PauseAudioStreamDevice(channels[channel].stream); + SDL_UnbindAudioStream(channels[channel].stream); + } + } +} + +void JA_ResumeChannel(const int channel) +{ + 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_ResumeAudioStreamDevice(channels[i].stream); + SDL_BindAudioStream(sdlAudioDevice, channels[i].stream); + } + } + else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) + { + if (channels[channel].state == JA_CHANNEL_PAUSED) + { + channels[channel].state = JA_CHANNEL_PLAYING; + //SDL_ResumeAudioStreamDevice(channels[channel].stream); + SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream); + } + } +} + +void JA_StopChannel(const int channel) +{ + if (!JA_soundEnabled) return; + + if (channel == -1) + { + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { + if (channels[i].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[i].stream); + channels[i].stream = nullptr; + channels[i].state = JA_CHANNEL_FREE; + channels[i].pos = 0; + channels[i].sound = NULL; + } + } + else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) + { + if (channels[channel].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[channel].stream); + channels[channel].stream = nullptr; + channels[channel].state = JA_CHANNEL_FREE; + channels[channel].pos = 0; + channels[channel].sound = NULL; + } +} + +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; + + return channels[channel].state; +} + +float JA_SetSoundVolume(float volume) +{ + JA_soundVolume = SDL_clamp( volume, 0.0f, 1.0f ); + + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) + if ( (channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED) ) + SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume); + + return JA_soundVolume; +} + +void JA_EnableSound(const bool value) +{ + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) + { + if (channels[i].state == JA_CHANNEL_PLAYING) JA_StopChannel(i); + } + JA_soundEnabled = value; +} + +const bool JA_IsSoundEnabled() +{ + return JA_soundEnabled; +} + +float JA_SetVolume(float volume) +{ + JA_SetSoundVolume(JA_SetMusicVolume(volume) / 2.0f); + + return JA_musicVolume; +} + +#endif \ No newline at end of file diff --git a/source/mini/audio/jail_audio.h b/source/mini/audio/jail_audio.h new file mode 100644 index 0000000..e145161 --- /dev/null +++ b/source/mini/audio/jail_audio.h @@ -0,0 +1,42 @@ +#pragma once +#include + +enum JA_Channel_state { JA_CHANNEL_INVALID, JA_CHANNEL_FREE, JA_CHANNEL_PLAYING, JA_CHANNEL_PAUSED, JA_SOUND_DISABLED }; +enum JA_Music_state { JA_MUSIC_INVALID, JA_MUSIC_PLAYING, JA_MUSIC_PAUSED, JA_MUSIC_STOPPED, JA_MUSIC_DISABLED }; + +struct JA_Sound_t; +struct JA_Music_t; + +void JA_Init(const int freq, const SDL_AudioFormat format, const int channels); +void JA_Quit(); + +JA_Music_t *JA_LoadMusic(const char* filename); +JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length); +void JA_PlayMusic(JA_Music_t *music, const int loop = -1); +void JA_PauseMusic(); +void JA_ResumeMusic(); +void JA_StopMusic(); +void JA_FadeOutMusic(const int milliseconds); +JA_Music_state JA_GetMusicState(); +void JA_DeleteMusic(JA_Music_t *music); +float JA_SetMusicVolume(float volume); +void JA_SetMusicPosition(float value); +float JA_GetMusicPosition(); +void JA_EnableMusic(const bool value); +const bool JA_IsMusicEnabled(); + +JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length); +JA_Sound_t *JA_LoadSound(Uint8* buffer, Uint32 length); +JA_Sound_t *JA_LoadSound(const char* filename); +int JA_PlaySound(JA_Sound_t *sound, const int loop = 0); +int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop = 0); +void JA_PauseChannel(const int channel); +void JA_ResumeChannel(const int channel); +void JA_StopChannel(const int channel); +JA_Channel_state JA_GetChannelState(const int channel); +void JA_DeleteSound(JA_Sound_t *sound); +float JA_SetSoundVolume(float volume); +void JA_EnableSound(const bool value); +const bool JA_IsSoundEnabled(); + +float JA_SetVolume(float volume); diff --git a/source/mini/config/config.cpp b/source/mini/config/config.cpp new file mode 100644 index 0000000..ed91dde --- /dev/null +++ b/source/mini/config/config.cpp @@ -0,0 +1,21 @@ +#include "config.h" +#include "mini/file/file.h" + +namespace mini +{ + namespace config + { + namespace key { + void set(const char* key, const char* value) { + file::setconfigvalue(key, value); + } + const char* get(const char* key) { + return file::getconfigvalue(key); + } + } + + const char *folder() { + return file::getconfigfolder(); + } + } +} \ No newline at end of file diff --git a/source/mini/config/config.h b/source/mini/config/config.h new file mode 100644 index 0000000..4f3bc4b --- /dev/null +++ b/source/mini/config/config.h @@ -0,0 +1,15 @@ +#pragma once +#include + +namespace mini +{ + namespace config + { + namespace key { + void set(const char* key, const char* value); + const char *get(const char* key); + } + + const char *folder(); + } +} diff --git a/source/mini/draw/draw.cpp b/source/mini/draw/draw.cpp new file mode 100644 index 0000000..1acb7fe --- /dev/null +++ b/source/mini/draw/draw.cpp @@ -0,0 +1,445 @@ +#include "draw.h" +#include "mini/surf/surf.h" +#include "mini/view/view.h" +#include "mini/font/font.h" + +#include + +#define DEST(x, y) surf::state.dest_surface->p[x+y*surf::state.dest_surface->w] +#define SOURCE(x, y) surf::state.source_surface->p[x+y*surf::state.source_surface->w] + +namespace mini +{ + namespace draw + { + state_t state; + + namespace pixel { + static inline void pset_fast(int x, int y, uint8_t color) { + if (state.trans != color) DEST(x, y) = state.draw_palette[color]; + } + + static inline void pset_bool(int x, int y, uint8_t color) { + if (state.trans != color) { + switch (state.mode) { + case DRAWMODE_AND: + DEST(x, y) = DEST(x, y) & color; + break; + case DRAWMODE_OR: + DEST(x, y) = DEST(x, y) | color; + break; + case DRAWMODE_XOR: + DEST(x, y) = DEST(x, y) ^ color; + break; + case DRAWMODE_NOT: + DEST(x, y) = ~DEST(x, y); + break; + } + } + } + + static inline void pset_pattern(int x, int y, uint8_t color) { + int pbx = x % 4, pby = y % 4; + int pb = pbx+pby*4; + if (state.fill_pattern & (1 << pb)) if (state.trans != color) DEST(x, y) = color; + } + + // Per a les funcions que pinten tot del mateix color + static inline void direct_pset(int x, int y, uint8_t color) { + x += view::state.origin[0]; y += view::state.origin[1]; + if (x < surf::state.dest_surface->clip[0] || x > surf::state.dest_surface->clip[2] || y < surf::state.dest_surface->clip[1] || y > surf::state.dest_surface->clip[3]) return; + switch (state.mode) { + case DRAWMODE_NORMAL: pset_fast(x,y,color); break; + case DRAWMODE_PATTERN: pset_pattern(x,y,color); break; + default: pset_bool(x,y,color); break; + } + } + + // Per a les funcions que van canviant de color (surf.pixel i draw.surf, bàsicament) + static inline void subst_pset(int x, int y, uint8_t color) { + direct_pset(x, y, state.draw_palette[color]); + } + + void set(int x, int y, uint8_t color) { + subst_pset(x,y,color); + } + + uint8_t get(int x, int y) { + if (!surf::state.source_surface) return 0; + if (x < 0 || x > (surf::state.source_surface->w-1) || y < 0 || y > (surf::state.source_surface->h-1)) return 0; + return SOURCE(x, y); + } + } + + // Bresenham Line Algorithm + void line(int x0, int y0, int x1, int y1, uint8_t color) { + int x, y; + int dx, dy; + int incx, incy; + int balance; + color = state.draw_palette[color]; + + if (x1 >= x0) { dx = x1 - x0; incx = 1; } else { dx = x0 - x1; incx = -1; } + if (y1 >= y0) { dy = y1 - y0; incy = 1; } else { dy = y0 - y1; incy = -1; } + + x = x0; y = y0; + + if (dx >= dy) { + dy <<= 1; + balance = dy - dx; + dx <<= 1; + + while (x != x1) { + pixel::direct_pset(x, y, color); + if (balance >= 0) { y += incy; balance -= dx; } + balance += dy; + x += incx; + } + pixel::direct_pset(x, y, color); + } else { + dx <<= 1; + balance = dx - dy; + dy <<= 1; + + while (y != y1) { + pixel::direct_pset(x, y, color); + if (balance >= 0) { x += incx; balance -= dy; } + balance += dx; + y += incy; + } + pixel::direct_pset(x, y, color); + } + } + + void hline(int x0, int y, int x1, uint8_t color) { + color = state.draw_palette[color]; + if (x0>x1) { const int tmp=x0;x0=x1;x1=tmp; } + for (int x=x0; x<=x1; ++x) pixel::direct_pset(x, y, color); + } + + void vline(int x, int y0, int y1, uint8_t color) { + color = state.draw_palette[color]; + if (y0>y1) { const int tmp=y0;y0=y1;y1=tmp; } + for (int y=y0; y<=y1; ++y) pixel::direct_pset(x, y, color); + } + + void rect(int x, int y, int w, int h, uint8_t color) { + int x1 = w+x-1; + int y1 = h+y-1; + hline(x, y, x1, color); + hline(x, y1, x1, color); + vline(x, y, y1, color); + vline(x1, y, y1, color); + } + + void rectf(int x, int y, int w, int h, uint8_t color) { + int x1 = w+x-1; + int y1 = h+y-1; + for (int i=y; i<=y1; ++i) hline(x, i, x1, color); + } + + static inline void circ_scanline(int xc, int yc, int x, int y, uint8_t color) { + pixel::direct_pset(xc+x, yc+y, color); + pixel::direct_pset(xc-x, yc+y, color); + pixel::direct_pset(xc+x, yc-y, color); + pixel::direct_pset(xc-x, yc-y, color); + pixel::direct_pset(xc+y, yc+x, color); + pixel::direct_pset(xc-y, yc+x, color); + pixel::direct_pset(xc+y, yc-x, color); + pixel::direct_pset(xc-y, yc-x, color); + } + + void circ(int x, int y, uint8_t r, uint8_t color) { + color = state.draw_palette[color]; + int xi=0, yi=r; + int d=3-2*r; + circ_scanline(x, y, xi, yi, color); + while (yi>=xi++) { + d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6; + circ_scanline(x, y, xi, yi, color); + } + } + + static inline void circf_scanline(int xc, int yc, int x, int y, uint8_t color) { + hline(xc-x, yc+y, xc+x, color); + hline(xc-x, yc-y, xc+x, color); + hline(xc-y, yc+x, xc+y, color); + hline(xc-y, yc-x, xc+y, color); + } + + void circf(int x, int y, uint8_t r, uint8_t color) { + int xi=0, yi=r; + int d=3-2*r; + circf_scanline(x, y, xi, yi, color); + while (yi>=xi++) { + d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6; + circf_scanline(x, y, xi, yi, color); + } + } + + void roundrect(int x, int y, int w, int h, uint8_t r, uint8_t color) { + int xi=0, yi=r; + int d=3-2*r; + + int xf = w+x-1; + int yf = h+y-1; + int x1 = x+r, y1 = y+r; + int x2 = xf-r, y2 = yf-r; + hline(x1, y, x2, color); + hline(x1, yf, x2, color); + vline(x, y1, y2, color); + vline(xf, y1, y2, color); + + color = state.draw_palette[color]; + while (yi>=xi++) { + d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6; + pixel::direct_pset(x2+xi, y2+yi, color); + pixel::direct_pset(x1-xi, y2+yi, color); + pixel::direct_pset(x2+xi, y1-yi, color); + pixel::direct_pset(x1-xi, y1-yi, color); + pixel::direct_pset(x2+yi, y2+xi, color); + pixel::direct_pset(x1-yi, y2+xi, color); + pixel::direct_pset(x2+yi, y1-xi, color); + pixel::direct_pset(x1-yi, y1-xi, color); + } + } + + void roundrectf(int x, int y, int w, int h, uint8_t r, uint8_t color) { + int xi=0, yi=r; + int d=3-2*r; + + int xf = w+x-1; + int yf = h+y-1; + int x1 = x+r, y1 = y+r; + int x2 = xf-r, y2 = yf-r; + for (int i=y1; i<=y2; ++i) hline(x, i, xf, color); + + while (yi>=xi++) { + d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6; + hline(x1-xi, y2+yi, x2+xi, color); + hline(x1-xi, y1-yi, x2+xi, color); + hline(x1-yi, y2+xi, x2+yi, color); + hline(x1-yi, y1-xi, x2+yi, color); + } + } + + void oval_scanline(int xc, int yc, int x, int y, float xf, float yf, uint8_t color) { + pixel::direct_pset((xc+x)*xf, (yc+y)*yf, color); + pixel::direct_pset((xc-x)*xf, (yc+y)*yf, color); + pixel::direct_pset((xc+x)*xf, (yc-y)*yf, color); + pixel::direct_pset((xc-x)*xf, (yc-y)*yf, color); + pixel::direct_pset((xc+y)*xf, (yc+x)*yf, color); + pixel::direct_pset((xc-y)*xf, (yc+x)*yf, color); + pixel::direct_pset((xc+y)*xf, (yc-x)*yf, color); + pixel::direct_pset((xc-y)*xf, (yc-x)*yf, color); + } + + void oval(int x0, int y0, int x1, int y1, uint8_t color) { + color = state.draw_palette[color]; + int rx = (x1-x0)/2; + int ry = (y1-y0)/2; + int r = rx; + int x = x0 + rx; + int y = y0 + ry; + float xf = 1.0f, yf = 1.0f; + if (rx>=ry) {r=rx;yf=float(ry)/float(rx);} else {r=ry;xf=float(rx)/float(ry);} + int xi=0, yi=r; + int d=3-2*r; + oval_scanline(x, y, xi, yi, xf, yf, color); + while (yi>=xi++) { + d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6; + oval_scanline(x, y, xi, yi, xf, yf, color); + } + } + + static inline void ovalf_scanline(int xc, int yc, int x, int y, float xf, float yf, uint8_t color) { + hline((xc-x)*xf, (yc+y)*yf, (xc+x)*xf, color); + hline((xc-x)*xf, (yc-y)*yf, (xc+x)*xf, color); + hline((xc-y)*xf, (yc+x)*yf, (xc+y)*xf, color); + hline((xc-y)*xf, (yc-x)*yf, (xc+y)*xf, color); + } + + void ovalf(int x0, int y0, int x1, int y1, uint8_t color) { + int rx = (x1-x0)/2; + int ry = (y1-y0)/2; + int r = rx; + int x = x0 + rx; + int y = y0 + ry; + float xf = 1.0f, yf = 1.0f; + if (rx>=ry) {r=rx;yf=float(ry)/float(rx);} else {r=ry;xf=float(rx)/float(ry);} + int xi=0, yi=r; + int d=3-2*r; + ovalf_scanline(x, y, xi, yi, xf, yf, color); + while (yi>=xi++) { + d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6; + ovalf_scanline(x, y, xi, yi, xf, yf, color); + } + } + + namespace pattern { + void set(uint16_t pat, bool transparent) { + state.fill_pattern = pat; + } + } + + void surf(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flip_x, bool flip_y, bool invert) { + if (dw == 0) dw = sw; + if (dh == 0) dh = sh; + + // 16.16 fixed point + int sdx = (sw << 16) / dw; + int sdy = (sh << 16) / dh; + + if (flip_x) sdx = -sdx; + if (flip_y) sdy = -sdy; + + int ssx = flip_x ? ((sx + sw - 1) << 16) : (sx << 16); + int ssy = flip_y ? ((sy + sh - 1) << 16) : (sy << 16); + + int csy = ssy; + + if (!invert) { + for (int y = dy; y < dy + dh; ++y) { + int csx = ssx; + for (int x = dx; x < dx + dw; ++x) { + uint8_t color = pixel::get(csx >> 16, csy >> 16); + pixel::subst_pset(x, y, color); + csx += sdx; + } + csy += sdy; + } + } else { + for (int y = dy; y < dy + dh; ++y) { + int csx = ssx; + for (int x = dx; x < dx + dw; ++x) { + uint8_t color = pixel::get(csy >> 16, csx >> 16); + pixel::subst_pset(x, y, color); + csx += sdx; + } + csy += sdy; + } + } + } + + void surfrot(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flip_x, bool flip_y, float angle_deg) { + if (dw == 0) dw = sw; + if (dh == 0) dh = sh; + + // Centro del destino (rectángulo sin rotar) + float dcx = dx + dw * 0.5f; + float dcy = dy + dh * 0.5f; + + // Centro del subrectángulo origen + float scx = sx + sw * 0.5f; + float scy = sy + sh * 0.5f; + + // Escalado destino -> origen + float inv_scale_x = float(sw) / float(dw); + float inv_scale_y = float(sh) / float(dh); + + // Flips integrados en la escala + if (flip_x) inv_scale_x = -inv_scale_x; + if (flip_y) inv_scale_y = -inv_scale_y; + + // Ángulo en radianes + float a = angle_deg * 3.14159265f / 180.0f; + float ca = SDL_cosf(a); + float sa = SDL_sinf(a); + + // --- 1. Bounding box rotado del rectángulo destino --- + + float hx = dw * 0.5f; + float hy = dh * 0.5f; + + float vx[4] = { -hx, hx, -hx, hx }; + float vy[4] = { -hy, -hy, hy, hy }; + + float min_x = 1e9f, max_x = -1e9f; + float min_y = 1e9f, max_y = -1e9f; + + for (int i = 0; i < 4; ++i) { + float rr_x = vx[i] * ca - vy[i] * sa; + float rr_y = vx[i] * sa + vy[i] * ca; + + float dxp = dcx + rr_x; + float dyp = dcy + rr_y; + + if (dxp < min_x) min_x = dxp; + if (dxp > max_x) max_x = dxp; + if (dyp < min_y) min_y = dyp; + if (dyp > max_y) max_y = dyp; + } + + int bb_x0 = (int)SDL_floorf(min_x); + int bb_x1 = (int)SDL_ceilf (max_x); + int bb_y0 = (int)SDL_floorf(min_y); + int bb_y1 = (int)SDL_ceilf (max_y); + + // --- 2. Rotación inversa + escalado + clipping estricto --- + + for (int y = bb_y0; y <= bb_y1; ++y) { + for (int x = bb_x0; x <= bb_x1; ++x) { + + // Coordenadas relativas al centro destino + float rx = x - dcx; + float ry = y - dcy; + + // Rotación inversa + float ux = rx * ca + ry * sa; + float uy = -rx * sa + ry * ca; + + // Escalado destino -> origen (con flips) + float sxp = scx + ux * inv_scale_x; + float syp = scy + uy * inv_scale_y; + + // Clipping estricto al subrectángulo origen + if (sxp < sx || sxp >= sx + sw || + syp < sy || syp >= sy + sh) + continue; // no pintamos nada + + uint8_t color = pixel::get((int)sxp, (int)syp); + pixel::subst_pset(x, y, color); + } + } + } + + const uint8_t printchar(uint8_t c, int x, int y) { + font::char_t &chr = font::current_font->chars[c]; + draw::surf(chr.x, chr.y, chr.w, chr.h, x, y-chr.base); + return chr.w; + } + + void text(const char* str, int x, int y, uint8_t color) { + + const unsigned char* p = (const unsigned char*)str; + uint8_t cp; + uint8_t xpos = x; + + uint8_t old_source = surf::source::get(); + surf::source::set(font::current_font->surface); + uint8_t old_color = state.draw_palette[1]; + state.draw_palette[1] = color; + uint8_t old_trans = state.trans; + state.trans = 0; + while (p[0]!=0) { + if (p[0] < 0x80) { + cp = p[0]; + p+=1; + } else if ((p[0] & 0xE0) == 0xC0) { + cp = (p[0] << 6) | (p[1] & 0x3F); + p+=2; + } + xpos += printchar(cp, xpos, y) + font::current_font->spacing; + } + state.trans = old_trans; + state.draw_palette[1] = old_color; + surf::source::set(old_source); + } + + namespace mode { + void set(uint8_t mode) { + state.mode = mode; + } + } + } +} \ No newline at end of file diff --git a/source/mini/draw/draw.h b/source/mini/draw/draw.h new file mode 100644 index 0000000..55a28cb --- /dev/null +++ b/source/mini/draw/draw.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#define DRAWMODE_NORMAL 0 +#define DRAWMODE_PATTERN 1 +#define DRAWMODE_AND 2 +#define DRAWMODE_OR 3 +#define DRAWMODE_XOR 4 +#define DRAWMODE_NOT 5 + +namespace mini +{ + namespace draw + { + struct state_t { + uint8_t trans = 0; + uint16_t fill_pattern = 0b1111111111111111; + uint8_t draw_palette[256]; + uint8_t mode = DRAWMODE_NORMAL; + }; + extern state_t state; + + namespace pixel { + void set(int x, int y, uint8_t color); + uint8_t get(int x, int y); + } + + void line(int x0, int y0, int x1, int y1, uint8_t color); + void hline(int x0, int y, int x1, uint8_t color); + void vline(int x, int y0, int y1, uint8_t color); + + void rect(int x, int y, int w, int h, uint8_t color); + void rectf(int x, int y, int w, int h, uint8_t color); + + void circ(int x, int y, uint8_t r, uint8_t color); + void circf(int x, int y, uint8_t r, uint8_t color); + + void roundrect(int x, int y, int w, int h, uint8_t r, uint8_t color); + void roundrectf(int x, int y, int w, int h, uint8_t r, uint8_t color); + void oval(int x0, int y0, int x1, int y1, uint8_t color); + void ovalf(int x0, int y0, int x1, int y1, uint8_t color); + + namespace pattern { + void set(uint16_t pat, bool transparent = false); + } + + void surf(int sx, int sy, int sw, int sh, int dx, int dy, int dw=0, int dh=0, bool flip_x = false, bool flip_y = false, bool invert = false); + void surfrot(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flip_x, bool flip_y, float angle_deg); + + void text(const char *str, int x, int y, uint8_t color); + + namespace mode + { + void set(uint8_t mode); + } + } +} \ No newline at end of file diff --git a/source/mini/file/file.cpp b/source/mini/file/file.cpp new file mode 100644 index 0000000..e4fcb90 --- /dev/null +++ b/source/mini/file/file.cpp @@ -0,0 +1,390 @@ +#include "file.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#define DEFAULT_FILENAME "data.jf2" +#define DEFAULT_FOLDER "data/" +#define CONFIG_FILENAME "config.txt" + +namespace mini +{ + namespace file + { + struct file_t + { + std::string path; + uint32_t size; + uint32_t offset; + }; + + std::vector toc; + + /* El std::map me fa coses rares, vaig a usar un good old std::vector amb una estructura key,value propia i au, que sempre funciona */ + struct keyvalue_t { + std::string key, value; + }; + + char *resource_filename = NULL; + char *resource_folder = NULL; + int file_source = SOURCE_FILE; + char scratch[255]; + static std::string config_folder; + std::vector config; + + void setresourcefilename(const char *str) { + if (resource_filename != NULL) free(resource_filename); + resource_filename = (char*)malloc(strlen(str)+1); + strcpy(resource_filename, str); + } + + void setresourcefolder(const char *str) { + if (resource_folder != NULL) free(resource_folder); + resource_folder = (char*)malloc(strlen(str)+1); + strcpy(resource_folder, str); + } + + void setsource(const int src) { + file_source = src%2; // mod 2 so it always is a valid value, 0 (file) or 1 (folder) + if (src==SOURCE_FOLDER && resource_folder==NULL) setresourcefolder(DEFAULT_FOLDER); + } + + bool getdictionary() { + if (resource_filename == NULL) setresourcefilename(DEFAULT_FILENAME); + + std::ifstream fi (resource_filename, std::ios::binary); + if (!fi.is_open()) return false; + char header[4]; + fi.read(header, 4); + uint32_t num_files, toc_offset; + fi.read((char*)&num_files, 4); + fi.read((char*)&toc_offset, 4); + fi.seekg(toc_offset); + + for (uint32_t i=0; ipw_dir; + config_folder = std::string(homedir) + "/Library/Application Support/" + foldername; + #elif __linux__ + struct passwd *pw = getpwuid(getuid()); + const char *homedir = pw->pw_dir; + config_folder = std::string(homedir) + "/." + foldername; + config_folder = std::string(homedir) + "/.config/jailgames/" + foldername; + + { + // Intenta crear ".config", per si no existeix + std::string config_base_folder = std::string(homedir) + "/.config"; + int ret = mkdir(config_base_folder.c_str(), S_IRWXU); + if (ret == -1 && errno != EEXIST) + { + printf("ERROR CREATING CONFIG BASE FOLDER."); + exit(EXIT_FAILURE); + } + } + { + // Intenta crear ".config/jailgames", per si no existeix + std::string config_base_folder = std::string(homedir) + "/.config/jailgames"; + int ret = mkdir(config_base_folder.c_str(), S_IRWXU); + if (ret == -1 && errno != EEXIST) + { + printf("ERROR CREATING CONFIG BASE FOLDER."); + exit(EXIT_FAILURE); + } + } + + #endif + + struct stat st = {0}; + if (stat(config_folder.c_str(), &st) == -1) + { + #ifdef _WIN32 + int ret = mkdir(config_folder.c_str()); + #else + int ret = mkdir(config_folder.c_str(), S_IRWXU); + #endif + + if (ret == -1) + { + printf("ERROR CREATING CONFIG FOLDER."); + exit(EXIT_FAILURE); + } + } + } + + const char *getconfigfolder() { + static std::string folder = config_folder + "/"; + return folder.c_str(); + } + + void loadconfigvalues() { + config.clear(); + std::string config_file = config_folder + "/config.txt"; + FILE *f = fopen(config_file.c_str(), "r"); + if (!f) return; + + char line[1024]; + while (fgets(line, sizeof(line), f)) { + char *value = strchr(line, '='); + if (value) { + *value='\0'; value++; + value[strlen(value)-1] = '\0'; + config.push_back({line, value}); + } + } + fclose(f); + } + + void saveconfigvalues() { + std::string config_file = config_folder + "/config.txt"; + FILE *f = fopen(config_file.c_str(), "w"); + if (f) { + for (auto pair : config) { + fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str()); + } + fclose(f); + } + } + + const char* getconfigvalue(const char *key) { + if (config.empty()) loadconfigvalues(); + for (auto pair : config) { + if (pair.key == std::string(key)) { + strcpy(scratch, pair.value.c_str()); + return scratch; + } + } + return NULL; + } + + void setconfigvalue(const char* key, const char* value) { + if (config.empty()) loadconfigvalues(); + for (auto &pair : config) { + if (pair.key == std::string(key)) { + pair.value = value; + saveconfigvalues(); + return; + } + } + config.push_back({key, value}); + saveconfigvalues(); + return; + } + + bool createFolder(const char* name) { + char tmp[256]; + strcpy(tmp, "./"); + strcat(tmp, name); + #ifdef _WIN32 + return mkdir(tmp)==0; + #else + return mkdir(tmp, 0755)==0; + #endif + } + + static bool has_extension(const std::string &name, const char *ext) + { + if (!ext) return true; // sin filtro + + std::string e = ext; + std::string suffix = "." + e; + + if (name.size() < suffix.size()) + return false; + + return (name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0); + } + + std::vector listresourcesdir(const char *folder, const char *extension) + { + std::vector result; + std::string base(folder); + + // Normalizar: quitar "/" final si existe + if (!base.empty() && base.back() == '/') + base.pop_back(); + + // ------------------------------- + // 1. MODO: ARCHIVOS SUELTOS + // ------------------------------- + if (file_source == SOURCE_FOLDER) + { + std::string fullpath = std::string(resource_folder) + base; + + DIR *dir = opendir(fullpath.c_str()); + if (!dir) + return result; + + struct dirent *entry; + while ((entry = readdir(dir)) != nullptr) + { + std::string name = entry->d_name; + + // Ignorar "." y ".." + if (name == "." || name == "..") + continue; + + // Ignorar subdirectorios + std::string full = fullpath + "/" + name; + DIR *test = opendir(full.c_str()); + if (test) + { + closedir(test); + continue; // es un directorio + } + + // Filtrar por extensión + if (!has_extension(name, extension)) + continue; + + result.push_back(name); + } + + closedir(dir); + return result; + } + + // ------------------------------- + // 2. MODO: ARCHIVO CONTENEDOR + // ------------------------------- + if (file_source == SOURCE_FILE) + { + std::string prefix = base + "/"; + + for (auto &f : toc) + { + const std::string &path = f.path; + + // Debe empezar por "folder/" + if (path.compare(0, prefix.size(), prefix) != 0) + continue; + + // Extraer la parte después de "folder/" + std::string rest = path.substr(prefix.size()); + + // Ignorar subdirectorios + if (rest.find('/') != std::string::npos) + continue; + + // Filtrar por extensión + if (!has_extension(rest, extension)) + continue; + + result.push_back(rest); + } + + return result; + } + + return result; + } + } +} diff --git a/source/mini/file/file.h b/source/mini/file/file.h new file mode 100644 index 0000000..c9877d9 --- /dev/null +++ b/source/mini/file/file.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#define SOURCE_FILE 0 +#define SOURCE_FOLDER 1 + +namespace mini +{ + namespace file + { + void setconfigfolder(const char *foldername); + const char *getconfigfolder(); + + void setresourcefilename(const char *str); + void setresourcefolder(const char *str); + void setsource(const int src); + + FILE *getfilepointer(const char *resourcename, int& filesize, const bool binary=false); + char *getfilebuffer(const char *resourcename, int& filesize, const bool zero_terminate=false); + + FILE *getfilepointerex(const char *filename, int& filesize, const bool binary=false); + char *getfilebufferex(const char *filename, int& filesize, const bool zero_terminate=false); + + const char* getconfigvalue(const char *key); + void setconfigvalue(const char* key, const char* value); + + bool createFolder(const char* name); + std::vector listresourcesdir(const char *folder, const char *extension=NULL); + } +} diff --git a/source/mini/font/font.cpp b/source/mini/font/font.cpp new file mode 100644 index 0000000..b6141bd --- /dev/null +++ b/source/mini/font/font.cpp @@ -0,0 +1,121 @@ +#include "font.h" +#include "mini/surf/surf.h" +#include "mini/file/file.h" +#include "aux/log.h" + +#include +#include +#include + +#define MAX_FONTS 5 + +namespace mini +{ + namespace font + { + font_t fonts[MAX_FONTS]; + font_t *current_font; + + uint8_t load_from_buffer(const char* buffer, uint8_t index, const char* name) { + if (!buffer) return false; + + const char* ptr = buffer; + char line[256]; + + // --- Read first line --- + { + // Extract line + int len = 0; + while (ptr[len] && ptr[len] != '\n' && len < 255) len++; + + memcpy(line, ptr, len); + line[len] = '\0'; + ptr += (ptr[len] == '\n') ? len + 1 : len; // Advance pointer + if (len > 0 && line[len - 1] == '\r') line[len - 1] = '\0'; + + if (strncmp(line, "bitmap=", 7) != 0) return false; // Parse first line + + fonts[index].surface = surf::load(line + 7); + + fonts[index].name = (char*)malloc(strlen(name)+1); + strcpy(fonts[index].name, name); + } + + // --- Read character lines --- + while (*ptr) { + // Extract next line + int len = 0; + while (ptr[len] && ptr[len] != '\n' && len < 255) len++; + + memcpy(line, ptr, len); + line[len] = '\0'; + ptr += (ptr[len] == '\n') ? len + 1 : len; // Advance pointer + if (len > 0 && line[len - 1] == '\r') line[len - 1] = '\0'; + + // Remove comments + char* hash = strchr(line, '#'); + if (hash) *hash = '\0'; + + // Parse + int code, x, y, w, h, base; + if (sscanf(line, "%d: %d %d %d %d %d", &code, &x, &y, &w, &h, &base) == 6) { + if (code >= 0 && code < 256) { + fonts[index].chars[code] = { (uint8_t)x, (uint8_t)y, (uint8_t)w, (uint8_t)h, (uint8_t)base }; + } + } + } + return index; + } + + uint8_t load(const char *filename) { + // Si la font ja s'ha carregat, tornem eixa font + for (unsigned int i=0; ispacing = spacing; + } + uint8_t get() { + return current_font->spacing; + } + } + } +} \ No newline at end of file diff --git a/source/mini/font/font.h b/source/mini/font/font.h new file mode 100644 index 0000000..b1e4fda --- /dev/null +++ b/source/mini/font/font.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace mini +{ + namespace font + { + struct char_t { + uint8_t x,y,w,h,base; + }; + + struct font_t { + char *name {nullptr}; + uint8_t surface {0}; + uint8_t spacing {1}; + char_t chars[256]; + }; + extern font_t *current_font; + + uint8_t load(const char *filename); + + namespace current { + void set(uint8_t font); + uint8_t get(); + } + + namespace spacing { + void set(uint8_t spacing); + uint8_t get(); + } + } +} \ No newline at end of file diff --git a/source/mini/key/key.cpp b/source/mini/key/key.cpp new file mode 100644 index 0000000..690af4a --- /dev/null +++ b/source/mini/key/key.cpp @@ -0,0 +1,49 @@ +#include "key.h" +#include "mini/pad/pad.h" + +#include + +namespace mini +{ + namespace key + { + state_t state; + + bool down(uint8_t i) { + return state.keys[i]; + } + + bool press(uint8_t i) { + if (state.just_pressed == i) { + state.just_pressed=0; + return true; + } else { + return false; + } + } + + int press() { + return state.just_pressed; + } + + bool any() { + const bool something_pressed = (state.just_pressed != 0) || (pad::state.just_pressed != -1); + state.just_pressed=0; + pad::state.just_pressed=-1; + return something_pressed; + } + + void text(const bool enable) { + // [TODO] + //if (enable) + // SDL_StartTextInput(mini_win); + //else + // SDL_StopTextInput(mini_win); + } + + const char* utf8char() { + return state.has_text_input ? state.text_input_buffer : nullptr; + } + + } +} \ No newline at end of file diff --git a/source/mini/key/key.h b/source/mini/key/key.h new file mode 100644 index 0000000..0af72e9 --- /dev/null +++ b/source/mini/key/key.h @@ -0,0 +1,23 @@ +#pragma once +#include + +namespace mini +{ + namespace key + { + struct state_t { + const bool *keys; + uint8_t just_pressed = 0; + char text_input_buffer[10]; + bool has_text_input = false; + }; + extern state_t state; + + bool down(uint8_t i); + bool press(uint8_t i); + int press(); + bool any(); + void text(const bool enable); + const char *utf8char(); + } +} diff --git a/source/mini/lua/lua.cpp b/source/mini/lua/lua.cpp new file mode 100644 index 0000000..33ae023 --- /dev/null +++ b/source/mini/lua/lua.cpp @@ -0,0 +1,171 @@ +#include "lua.h" +#include "lua.wrappers.h" +#include "lua.debug.h" +#include "external/lua/lua.hpp" +#include "mini/file/file.h" +#include "aux/log.h" + +#include +#include + +namespace mini +{ + namespace lua + { + lua_State *L; + bool is_playing = false; + bool init_exists = false; + bool update_exists = false; + + bool running() { + debug::process_commands(L); + return is_playing; + } + + int MiniLoader(lua_State *L) { + const char *name = luaL_checkstring(L, 1); + + // 1. Convertir puntos en barras + std::string path(name); + std::string regpath(name); + std::replace(path.begin(), path.end(), '.', '/'); + + // 2. Detectar comodín "/*" + bool load_all = false; + if (path.size() >= 2 && path.substr(path.size()-2) == "/*") { + load_all = true; + path = path.substr(0, path.size()-2); // quitar "/*" + regpath = regpath.substr(0, regpath.size()-2); // quitar "/*" + } + + if (load_all) { + std::vector files = file::listresourcesdir(path.c_str(), "lua"); + + // Ejecutar todos los módulos + for (auto &f : files) { + std::string fullpath = path + "/" + f; + std::string registerpath = std::string(regpath + "." + f.substr(0,f.size()-4)); + + int size; + char* buffer = file::getfilebuffer(fullpath.c_str(), size); + if (!buffer) continue; + + if (luaL_loadbuffer(L, buffer, size, registerpath.c_str()) == LUA_OK) { + lua_pcall(L, 0, 0, 0); // ejecutar módulo, sin devolver nada + lua_getglobal(L, "package"); + lua_getfield(L, -1, "loaded"); + lua_pushboolean(L, 1); + lua_setfield(L, -2, registerpath.c_str()); + lua_pop(L, 2); + } else { + log_msg(LOG_LUALD, "Error cargando %s: %s\n", fullpath.c_str(), lua_tostring(L, -1)); + lua_pop(L, 1); + } + + free(buffer); + } + + // Devolver un loader vacío + lua_pushcfunction(L, [](lua_State* L) -> int { + lua_pushboolean(L, 1); // require devuelve true + return 1; + }); + + return 1; + } + + // 3. Cargar un único archivo + std::string filename = path + ".lua"; + + int size; + char* buffer = file::getfilebuffer(filename.c_str(), size); + if (!buffer) { + lua_pushnil(L); + return 1; + } + + if (luaL_loadbuffer(L, buffer, size, name)) { + log_msg(LOG_LUALD, "%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); + free(buffer); + lua_pushnil(L); + return 1; + } + + free(buffer); + return 1; + } + + void init(const char *main_lua_file) { + L = luaL_newstate(); + luaL_openlibs(L); + + push_functions(L); + lua_register(L, "mini_loader", MiniLoader); + luaL_dostring(L, "table.insert(package.searchers,2,mini_loader)\n"); + + int size; + char* buffer = file::getfilebuffer(main_lua_file, size); + if (luaL_loadbuffer(L, buffer, size, "main")) { + log_msg(LOG_LUALD, "%s\n", lua_tostring(L, -1)); + lua_pop(L,1); + return; + } + free(buffer); + if (lua_pcall(L,0, LUA_MULTRET, 0)) { + //luaL_traceback(L, L, NULL, 1); + log_msg(LOG_LUART, "%s\n", lua_tostring(L, -1)); + lua_pop(L,1); + return; + } + + // Check if _init and _update exist + lua_getglobal(L, "mini"); + lua_getfield(L, -1, "init"); + if (lua_isfunction(L,-1)) init_exists = true; + lua_pop(L,1); + lua_pop(L,1); + + lua_getglobal(L, "mini"); + lua_getfield(L, -1, "update"); + if (lua_isfunction(L,-1)) update_exists = true; + lua_pop(L,1); + lua_pop(L,1); + + //std::thread(debugCommandThread).detach(); + printf("stdin isatty: %d\n", isatty(0)); + is_playing = true; + } + + void quit() { + if (!is_playing) return; + is_playing = false; + lua_close(L); + } + + void cleanup() { + debug::kill_thread(); + } + + namespace callbacks + { + void init() { + if (!init_exists) return; + debug::process_commands(L); + lua_getglobal(L, "mini"); + lua_getfield(L, -1, "init"); + + is_playing = debug::call_and_handle_exceptions(L); + } + + void update() { + if (!update_exists) return; + lua_getglobal(L, "mini"); + lua_getfield(L, -1, "update"); + + is_playing = debug::call_and_handle_exceptions(L); + } + + } + } +} diff --git a/source/mini/lua/lua.debug.cpp b/source/mini/lua/lua.debug.cpp new file mode 100644 index 0000000..c440ee0 --- /dev/null +++ b/source/mini/lua/lua.debug.cpp @@ -0,0 +1,1360 @@ +#include "lua.debug.h" +#include "external/lua/lua.hpp" +#include "mini/win/win.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace mini +{ + namespace lua + { + namespace debug + { + struct Breakpoint { + int line; + std::string condition; // puede estar vacío + std::string logMessage; // "" si no es logpoint + std::string hitCondition; // "" si no hay hit count + }; + + struct FrameInfo { + int level; // nivel en lua_getstack + std::string source; + int line; + //std::vector locals; + //std::vector upvalues; + }; + + struct StackFrameInfo { + std::string file; + int line; + std::string function; + }; + std::string lastExceptionTraceback; + std::vector lastExceptionStack; + std::string lastExceptionMessage; + bool hasException = false; + + std::set exceptionFilters;// {"all"}; + std::vector g_frames; + + //std::unordered_map> g_breakpoints; + std::unordered_map> g_breakpoints; + std::mutex g_breakMutex; + + std::queue g_debugCommands; + std::mutex g_cmdMutex; + + std::atomic g_running = true; + std::atomic g_paused = false; + + std::string g_pauseFile; + int g_pauseLine = 0; + bool function_has_breakpoints = false; + std::stack funBreakStack; + bool debug_enabled = true; + + enum StepMode { + STEP_NONE, + STEP_INTO, + STEP_OVER, + STEP_OUT + }; + + StepMode g_stepMode = STEP_NONE; + int g_stepDepth = 0; + + using json = nlohmann::json; + + std::string pathToChunk(const std::string& path) { + std::filesystem::path p(path); + + // 1. Normalizar la ruta + p = p.lexically_normal(); + + // 2. Buscar el directorio "data" + auto it = std::find(p.begin(), p.end(), "data"); + if (it == p.end()) + return ""; // no es un script Lua válido + + // 3. Construir la parte relativa después de "data" + std::filesystem::path rel; + for (++it; it != p.end(); ++it) + rel /= *it; + + // 4. Quitar ".lua" + std::string s = rel.string(); + if (s.ends_with(".lua")) + s = s.substr(0, s.size() - 4); + + // 5. Convertir "/" → "." + for (char& c : s) + if (c == '/') + c = '.'; + + return s; + } + + std::string chunkToPath(const std::string& chunk) { + // 1. Convertir "ia.test" → "ia/test" + std::string rel; + rel.reserve(chunk.size() + 10); + + for (char c : chunk) + rel += (c == '.' ? '/' : c); + + // 2. Añadir prefijo y sufijo + rel = "data/" + rel + ".lua"; + + // 3. Convertir a ruta absoluta + std::filesystem::path abs = std::filesystem::current_path() / rel; + + return abs.lexically_normal().string(); + } + + int getStackDepth(lua_State* L) { + lua_Debug ar; + int depth = 0; + + while (lua_getstack(L, depth, &ar)) { + depth++; + } + + return depth; + } + + json getStackTrace(lua_State* L) { + json frames = json::array(); + lua_Debug ar; + + int depth = 0; + while (lua_getstack(L, depth, &ar)) { + lua_getinfo(L, "nSl", &ar); + + //const char* src = chunkToPath(ar.source).c_str(); + + json frame = { + { "file", chunkToPath(ar.source).c_str() }, + { "line", ar.currentline }, + { "name", ar.name ? ar.name : "?" } + }; + + frames.push_back(frame); + depth++; + } + + return json{ + { "frames", frames } + }; + } + + int allocateRefForTable(lua_State* L, int index) { + lua_pushvalue(L, index); // copia la tabla + int ref = luaL_ref(L, LUA_REGISTRYINDEX); + return ref; + } + + bool pushTableFromRef(lua_State* L, int ref) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return false; + } + + return true; + } + + json getLocals(lua_State* L, int frame) { + json vars = json::array(); + + lua_Debug ar; + // Obtenemos el frame solicitado + if (!lua_getstack(L, frame, &ar)) { + printf("INVALID STACK FRAME\n"); + return json{ {"variables", vars} }; + } + + // Pedimos info del frame + lua_getinfo(L, "nSl", &ar); + + int i = 1; + const char* name; + + while ((name = lua_getlocal(L, &ar, i)) != NULL) { + + if (strcmp(name, "(temporary)") == 0) { + lua_pop(L, 1); + i++; + continue; + } + + json v; + v["name"] = name; + + int type = lua_type(L, -1); + + switch (type) { + case LUA_TNUMBER: + v["value"] = std::to_string(lua_tonumber(L, -1)); + v["type"] = "number"; + v["ref"] = 0; + break; + + case LUA_TBOOLEAN: + v["value"] = lua_toboolean(L, -1) ? "true" : "false"; + v["type"] = "boolean"; + v["ref"] = 0; + break; + + case LUA_TSTRING: + v["value"] = lua_tostring(L, -1); + v["type"] = "string"; + v["ref"] = 0; + break; + + case LUA_TTABLE: + v["value"] = "{table}"; + v["type"] = "table"; + v["ref"] = allocateRefForTable(L, -1); + break; + + case LUA_TFUNCTION: + v["value"] = "function"; + v["type"] = "function"; + v["ref"] = 0; + break; + + case LUA_TNIL: + v["value"] = "nil"; + v["type"] = "nil"; + v["ref"] = 0; + break; + + default: + v["value"] = lua_typename(L, type); + v["type"] = lua_typename(L, type); + v["ref"] = 0; + break; + } + + vars.push_back(v); + + // Quitamos el valor de la pila + lua_pop(L, 1); + + i++; + } + + return json{ + { "variables", vars } + }; + } + + json getUpvalues(lua_State* L, int frame) { + json vars = json::array(); + + lua_Debug ar; + if (!lua_getstack(L, frame, &ar)) { + return json{ {"variables", vars} }; + } + + // Pedimos info del frame, incluyendo la función ("f") + lua_getinfo(L, "nSlf", &ar); + + // La función está ahora en la pila + int funcIndex = lua_gettop(L); + + int i = 1; + const char* name; + + while ((name = lua_getupvalue(L, funcIndex, i)) != NULL) { + json v; + v["name"] = name; + + int type = lua_type(L, -1); + + switch (type) { + case LUA_TNUMBER: + v["value"] = std::to_string(lua_tonumber(L, -1)); + v["type"] = "number"; + v["ref"] = 0; + break; + + case LUA_TBOOLEAN: + v["value"] = lua_toboolean(L, -1) ? "true" : "false"; + v["type"] = "boolean"; + v["ref"] = 0; + break; + + case LUA_TSTRING: + v["value"] = lua_tostring(L, -1); + v["type"] = "string"; + v["ref"] = 0; + break; + + case LUA_TTABLE: + v["value"] = "{table}"; + v["type"] = "table"; + v["ref"] = allocateRefForTable(L, -1); + break; + + case LUA_TNIL: + v["value"] = "nil"; + v["type"] = "nil"; + v["ref"] = 0; + break; + + default: + v["value"] = lua_typename(L, type); + v["type"] = lua_typename(L, type); + v["ref"] = 0; + break; + } + + vars.push_back(v); + + lua_pop(L, 1); // quitamos el valor del upvalue + i++; + } + + // Quitamos la función del stack + lua_pop(L, 1); + + return json{ + { "variables", vars } + }; + } + + json getGlobals(lua_State* L) { + json vars = json::array(); + + // Empujamos _G a la pila + lua_pushglobaltable(L); // equivalente a lua_getglobal(L, "_G") + + lua_pushnil(L); // primera clave + while (lua_next(L, -2) != 0) { + // Ahora en la pila: + // -1 → valor + // -2 → clave + // -3 → _G + + // Solo aceptamos claves string + if (lua_type(L, -2) == LUA_TSTRING) { + const char* name = lua_tostring(L, -2); + + json v; + v["name"] = name; + + int type = lua_type(L, -1); + + switch (type) { + case LUA_TNUMBER: + v["value"] = std::to_string(lua_tonumber(L, -1)); + v["type"] = "number"; + v["ref"] = 0; + break; + + case LUA_TBOOLEAN: + v["value"] = lua_toboolean(L, -1) ? "true" : "false"; + v["type"] = "boolean"; + v["ref"] = 0; + break; + + case LUA_TSTRING: + v["value"] = lua_tostring(L, -1); + v["type"] = "string"; + v["ref"] = 0; + break; + + case LUA_TTABLE: + v["value"] = "{table}"; + v["type"] = "table"; + v["ref"] = allocateRefForTable(L, -1); + break; + + case LUA_TFUNCTION: + v["value"] = "function"; + v["type"] = "function"; + v["ref"] = 0; + break; + + case LUA_TNIL: + v["value"] = "nil"; + v["type"] = "nil"; + v["ref"] = 0; + break; + + default: + v["value"] = lua_typename(L, type); + v["type"] = lua_typename(L, type); + v["ref"] = 0; + break; + } + + vars.push_back(v); + } + + lua_pop(L, 1); // pop valor, deja clave para lua_next + } + + lua_pop(L, 1); // pop _G + + return json{ + { "variables", vars } + }; + } + + json expandTable(lua_State* L, int ref) { + json vars = json::array(); + + // Recuperamos la tabla del registry + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + json v; + + // clave + if (lua_type(L, -2) == LUA_TSTRING) { + v["name"] = lua_tostring(L, -2); + } else if (lua_type(L, -2) == LUA_TNUMBER) { + v["name"] = std::to_string(lua_tonumber(L, -2)); + } else { + v["name"] = ""; + } + + // valor + int type = lua_type(L, -1); + + switch (type) { + case LUA_TNUMBER: + v["value"] = std::to_string(lua_tonumber(L, -1)); + v["type"] = "number"; + v["ref"] = 0; + break; + + case LUA_TBOOLEAN: + v["value"] = lua_toboolean(L, -1) ? "true" : "false"; + v["type"] = "boolean"; + v["ref"] = 0; + break; + + case LUA_TSTRING: + v["value"] = lua_tostring(L, -1); + v["type"] = "string"; + v["ref"] = 0; + break; + + case LUA_TTABLE: + v["value"] = "{table}"; + v["type"] = "table"; + v["ref"] = allocateRefForTable(L, -1); + break; + + default: + v["value"] = lua_typename(L, type); + v["type"] = lua_typename(L, type); + v["ref"] = 0; + break; + } + + vars.push_back(v); + + lua_pop(L, 1); // pop valor + } + + lua_pop(L, 1); // pop tabla + + return json{ + { "kind", "expand" }, + { "variables", vars } + }; + } + + json setVariable(lua_State* L, int frame, const std::string& scope, + const std::string& name, const std::string& valueStr) + { + lua_Debug ar; + + if (!lua_getstack(L, frame, &ar)) { + return { {"error", "invalid frame"} }; + } + + lua_getinfo(L, "nSl", &ar); + + // Convertir valueStr a valor Lua + auto pushValue = [&](const std::string& s) { + // número + char* end; + double num = strtod(s.c_str(), &end); + if (*end == '\0') { + lua_pushnumber(L, num); + return; + } + + // boolean + if (s == "true") { lua_pushboolean(L, 1); return; } + if (s == "false") { lua_pushboolean(L, 0); return; } + + // nil + if (s == "nil") { lua_pushnil(L); return; } + + // string + lua_pushstring(L, s.c_str()); + }; + + if (scope == "locals") { + int i = 1; + const char* localName; + while ((localName = lua_getlocal(L, &ar, i)) != NULL) { + lua_pop(L, 1); // pop old value + + if (name == localName) { + pushValue(valueStr); + lua_setlocal(L, &ar, i); + return { {"value", valueStr} }; + } + + i++; + } + } + + if (scope == "upvalues") { + lua_getinfo(L, "f", &ar); // push function + + int i = 1; + const char* upName; + while ((upName = lua_getupvalue(L, -1, i)) != NULL) { + lua_pop(L, 1); // pop old value + + if (name == upName) { + pushValue(valueStr); + lua_setupvalue(L, -1, i); + lua_pop(L, 1); // pop function + return { {"value", valueStr} }; + } + + i++; + } + + lua_pop(L, 1); // pop function + } + + if (scope == "globals") { + pushValue(valueStr); + lua_setglobal(L, name.c_str()); + return { {"value", valueStr} }; + } + + return { {"error", "variable not found"} }; + } + + json setTableField(lua_State* L, int ref, const std::string& key, const std::string& valueStr) + { + // Recuperar la tabla desde tu sistema de referencias + if (!pushTableFromRef(L, ref)) { + return { {"error", "invalid table ref"} }; + } + + // Convertir el valor + auto pushValue = [&](const std::string& s) { + char* end; + double num = strtod(s.c_str(), &end); + if (*end == '\0') { lua_pushnumber(L, num); return; } + if (s == "true") { lua_pushboolean(L, 1); return; } + if (s == "false") { lua_pushboolean(L, 0); return; } + if (s == "nil") { lua_pushnil(L); return; } + lua_pushstring(L, s.c_str()); + }; + + pushValue(valueStr); + + // tabla[key] = valor + lua_setfield(L, -2, key.c_str()); + + lua_pop(L, 1); // pop tabla + + return { {"value", valueStr} }; + } + + json evalExpression(lua_State* L, const std::string& expr) { + lua_Debug ar; + if (!lua_getstack(L, 0, &ar)) { + return { {"error", "no stack"} }; + } + + lua_getinfo(L, "nSl", &ar); + + // Creamos un entorno para la evaluación + lua_newtable(L); // env + int envIndex = lua_gettop(L); + + // 1. Copiar locals al env + int i = 1; + const char* name; + while ((name = lua_getlocal(L, &ar, i)) != NULL) { + lua_setfield(L, envIndex, name); + i++; + } + + // 2. Copiar upvalues al env + lua_getinfo(L, "f", &ar); + int funcIndex = lua_gettop(L); + + i = 1; + while ((name = lua_getupvalue(L, funcIndex, i)) != NULL) { + lua_setfield(L, envIndex, name); + i++; + } + + lua_pop(L, 1); // pop función + + // 3. Copiar globals (_G) + lua_pushglobaltable(L); + lua_setfield(L, envIndex, "_G"); + // mt = { __index = _G } + lua_newtable(L); // mt + lua_pushglobaltable(L); // _G + lua_setfield(L, -2, "__index"); // mt.__index = _G + + // setmetatable(env, mt) + lua_setmetatable(L, envIndex); + + // 4. Construir código: función que recibe _ENV + std::string code = "return function(_ENV) return " + expr + " end"; + + if (luaL_loadbuffer(L, code.c_str(), code.size(), "eval") != LUA_OK) { + std::string err = lua_tostring(L, -1); + lua_pop(L, 1); + return { {"error", err} }; + } + + // Ejecutar para obtener la función interna + if (lua_pcall(L, 0, 1, 0) != LUA_OK) { + std::string err = lua_tostring(L, -1); + lua_pop(L, 1); + return { {"error", err} }; + } + + // Ahora en la pila está la función interna + // Le pasamos env como argumento + lua_pushvalue(L, envIndex); + + // Llamamos a la función(env) + if (lua_pcall(L, 1, 1, 0) != LUA_OK) { + std::string err = lua_tostring(L, -1); + lua_pop(L, 1); + return { {"error", err} }; + } + + // Convertir el resultado + json result; + + int type = lua_type(L, -1); + switch (type) { + case LUA_TNUMBER: + result["value"] = std::to_string(lua_tonumber(L, -1)); + result["type"] = "number"; + result["ref"] = 0; + break; + + case LUA_TBOOLEAN: + result["value"] = lua_toboolean(L, -1) ? "true" : "false"; + result["type"] = "boolean"; + result["ref"] = 0; + break; + + case LUA_TSTRING: + result["value"] = lua_tostring(L, -1); + result["type"] = "string"; + result["ref"] = 0; + break; + + case LUA_TTABLE: + result["value"] = "{table}"; + result["type"] = "table"; + result["ref"] = allocateRefForTable(L, -1); + break; + + default: + result["value"] = lua_typename(L, type); + result["type"] = lua_typename(L, type); + result["ref"] = 0; + break; + } + + lua_pop(L, 1); // pop result + lua_pop(L, 1); // pop env + + return result; + } + + json evalAssign(lua_State* L, int frame, const std::string& expr) { + // 1. Separar LHS y RHS + size_t eq = expr.find('='); + if (eq == std::string::npos) { + return { {"error", "not an assignment"} }; + } + + std::string lhs = expr.substr(0, eq); + std::string rhs = expr.substr(eq + 1); + + // limpiar espacios + auto trim = [](std::string& s) { + size_t a = s.find_first_not_of(" \t"); + size_t b = s.find_last_not_of(" \t"); + if (a == std::string::npos) { s = ""; return; } + s = s.substr(a, b - a + 1); + }; + + trim(lhs); + trim(rhs); + + // 2. Evaluar RHS usando tu evalExpression + json rhsValue = evalExpression(L, rhs); + if (rhsValue.contains("error")) { + return rhsValue; + } + + std::string rhsStr = rhsValue["value"]; + + // 3. Determinar si LHS es: + // - variable simple: x + // - acceso tabla: player.health + // - acceso profundo: a.b.c + if (lhs.find('.') == std::string::npos) { + // variable simple → locals, upvalues o globals + // Intentar locals + json r = setVariable(L, frame, "locals", lhs, rhsStr); + if (!r.contains("error")) return r; + + // Intentar upvalues + r = setVariable(L, frame, "upvalues", lhs, rhsStr); + if (!r.contains("error")) return r; + + // Intentar globals + r = setVariable(L, frame, "globals", lhs, rhsStr); + return r; + } + + // 4. Acceso tabla: a.b.c + // separar en partes + std::vector parts; + { + std::stringstream ss(lhs); + std::string item; + while (std::getline(ss, item, '.')) { + trim(item); + parts.push_back(item); + } + } + + if (parts.size() < 2) { + return { {"error", "invalid table assignment"} }; + } + + // La clave final + std::string finalKey = parts.back(); + parts.pop_back(); + + // 5. Evaluar la ruta de tabla (a.b.c → obtener tabla c) + // usando tu evalExpression + std::string tableExpr; + for (size_t i = 0; i < parts.size(); i++) { + if (i > 0) tableExpr += "."; + tableExpr += parts[i]; + } + + json tableValue = evalExpression(L, tableExpr); + if (tableValue.contains("error")) { + return tableValue; + } + + if (tableValue["type"] != "table") { + return { {"error", "LHS is not a table"} }; + } + + int ref = tableValue["ref"]; + + // 6. Asignar dentro de la tabla + return setTableField(L, ref, finalKey, rhsStr); + } + + void sendDebugResponse(const std::string& type, const json& payload) { + json msg = { + { "type", type }, + { "payload", payload } + }; + //printf("STACKTRACE: %s", msg.dump().c_str()); + std::cout << "@@DEBUG@@" << msg.dump() << std::endl; + } + + void parseLuaTraceback(const char* tb, std::vector& out) { + out.clear(); + if (!tb) return; + + std::stringstream ss(tb); + std::string line; + + bool inFrames = false; + + while (std::getline(ss, line)) { + // Quitar espacios iniciales y tabs + while (!line.empty() && (line[0] == ' ' || line[0] == '\t')) + line.erase(line.begin()); + + // Saltar la primera línea (mensaje de error) + if (!inFrames) { + if (line.rfind("stack traceback:", 0) == 0) { + inFrames = true; + } + continue; + } + + // Formato esperado: + // [string "modules.ia.hero"]:189: in field 'ia' + // + // O: + // [string "main"]:34: in function <[string "main"]:32> + + if (line.rfind("[string \"", 0) != 0) + continue; + + // Extraer chunk name + size_t q1 = line.find('"'); + size_t q2 = line.find('"', q1 + 1); + if (q1 == std::string::npos || q2 == std::string::npos) + continue; + + std::string chunk = line.substr(q1 + 1, q2 - q1 - 1); + + // Extraer línea + size_t colon1 = line.find(':', q2 + 1); + if (colon1 == std::string::npos) + continue; + + size_t colon2 = line.find(':', colon1 + 1); + if (colon2 == std::string::npos) + continue; + + int lineNumber = std::stoi(line.substr(colon1 + 1, colon2 - colon1 - 1)); + + // Extraer nombre de función (si existe) + std::string func = "unknown"; + + size_t inField = line.find("in field '"); + if (inField != std::string::npos) { + size_t start = inField + 10; + size_t end = line.find("'", start); + func = line.substr(start, end - start); + } + + size_t inFunc = line.find("in function <"); + if (inFunc != std::string::npos) { + func = "anonymous"; + } + + // Convertir chunk → ruta real + std::string filePath = chunkToPath(chunk); + + out.push_back({ filePath, lineNumber, func }); + } + } + + void processDebugCommand(lua_State* L, const std::string& line) { + //printf("COMANDO PROCESADO: %s\n", line.c_str()); + if (!line.starts_with("@@DEBUGCMD@@")) + return; + + json j = json::parse(line.substr(12)); + + std::string cmd = j["cmd"]; + + if (cmd == "setBreakpoints") { + std::string file = j["file"]; + std::string chunk = pathToChunk(file); + std::lock_guard lock(g_breakMutex); + + g_breakpoints[chunk].clear(); + + for (auto& bp : j["breakpoints"]) { + Breakpoint b; + b.line = bp["line"]; + + if (bp.contains("condition") && bp["condition"].is_string()) + b.condition = bp["condition"]; + else + b.condition = ""; + + if (bp.contains("logMessage") && bp["logMessage"].is_string()) + b.logMessage = bp["logMessage"]; + else + b.logMessage = ""; + + if (bp.contains("hitCondition") && bp["hitCondition"].is_string()) + b.hitCondition = bp["hitCondition"]; + else + b.hitCondition = ""; + + g_breakpoints[chunk].push_back(b); + } + } + else if (cmd == "continue") { + printf("CONTINUA\n\n"); + g_stepMode = STEP_NONE; + g_paused = false; + mini::win::raise(); + } + else if (cmd == "pause") { + g_stepMode = STEP_INTO; + g_paused = false; + } + else if (cmd == "stepOver") { + g_stepMode = STEP_OVER; + g_stepDepth = getStackDepth(L); + g_paused = false; + } + else if (cmd == "stepInto") { + g_stepMode = STEP_INTO; + g_paused = false; + } + else if (cmd == "stepOut") { + g_stepMode = STEP_OUT; + g_stepDepth = getStackDepth(L); + g_paused = false; + } + else if (cmd == "stackTrace") { + if (hasException) { + parseLuaTraceback(lastExceptionTraceback.c_str(), lastExceptionStack); + + json frames = json::array(); + + for (size_t i = 0; i < lastExceptionStack.size(); i++) { + frames.push_back({ + { "id", (int)i + 1 }, + { "name", lastExceptionStack[i].function }, + { "line", lastExceptionStack[i].line }, + { "column", 1 }, + { "source", { + { "path", lastExceptionStack[i].file } + }} + }); + } + + json result = { + { "stackFrames", frames }, + { "totalFrames", frames.size() } + }; + + sendDebugResponse("stackTrace", result); + } else { + json result = getStackTrace(L); + sendDebugResponse("stackTrace", result); + } + } + else if (cmd == "locals") { + int frame = j.value("frame", 0); + json result = getLocals(L, frame); + json payload = { + { "kind", "locals" }, + { "variables", result["variables"] } + }; + sendDebugResponse("variables", payload); + } + else if (cmd == "upvalues") { + int frame = j.value("frame", 0); + json result = getUpvalues(L, frame); + json payload = { + { "kind", "upvalues" }, + { "variables", result["variables"] } + }; + sendDebugResponse("variables", payload); + } + else if (cmd == "globals") { + json result = getGlobals(L); + json payload = { + { "kind", "globals" }, + { "variables", result["variables"] } + }; + sendDebugResponse("variables", payload); + } + else if (cmd == "expand") { + int ref = j["ref"]; + json result = expandTable(L, ref); + sendDebugResponse("variables", result); + } + else if (cmd == "eval") { + std::string expr = j["expr"]; + json result = evalExpression(L, expr); + + json payload = { + { "kind", "eval" }, + { "result", result } + }; + sendDebugResponse("eval", payload); + } + else if (cmd == "evalAssign") { + int frame = j.value("frame", 0); + std::string expr = j["expr"]; + + json result = evalAssign(L, frame, expr); + + json payload = { + { "kind", "eval" }, + { "result", result } + }; + + sendDebugResponse("eval", payload); + } + else if (cmd == "setVariable") { + int frame = j.value("frame", 0); + std::string scope = j["scope"]; + std::string name = j["name"]; + std::string value = j["value"]; + + json result = setVariable(L, frame, scope, name, value); + + sendDebugResponse("setVariable", result); + } + else if (cmd == "setTableField") { + int ref = j["ref"]; + std::string key = j["key"]; + std::string valueStr = j["value"]; + + json result = setTableField(L, ref, key, valueStr); + + sendDebugResponse("setVariable", result); + } + else if (cmd == "setExceptionFilters") { + exceptionFilters.clear(); + for (auto& f : j["filters"]) { + exceptionFilters.insert(f.get()); + } + } + } + + bool checkBreakpointCondition(lua_State* L, const Breakpoint& bp) { + if (bp.condition.empty()) return true; + + json result = evalExpression(L, bp.condition); + if (result.contains("error")) return false; + if (result["type"] == "boolean") return result["value"] == "true"; + + // Si la condición no devuelve booleano, se considera false + return false; + } + + std::string expandLogMessage(lua_State* L, const std::string& msg) { + std::string out; + size_t i = 0; + + while (i < msg.size()) { + if (msg[i] == '{') { + size_t j = msg.find('}', i + 1); + if (j == std::string::npos) break; + + std::string expr = msg.substr(i + 1, j - i - 1); + + json r = evalExpression(L, expr); + out += r.value("value", "nil"); + + i = j + 1; + } else { + out += msg[i++]; + } + } + + return out; + } + + void sendLogOutput(const std::string& text) { + json payload = { + { "kind", "log" }, + { "text", text } + }; + sendDebugResponse("log", payload); + } + + bool isBreakpoint(lua_State* L, const std::string& file, int line) { + std::lock_guard lock(g_breakMutex); + + auto it = g_breakpoints.find(file); + if (it == g_breakpoints.end()) + return false; + + for (auto& bp : it->second) { + if (bp.line == line) { + if (!bp.logMessage.empty()) { + std::string msg = expandLogMessage(L, bp.logMessage); + sendLogOutput(msg); + return false; // NO parar + } else if (checkBreakpointCondition(L, bp)) { + return true; + } + + } + } + + return false; + } + + void sendBreakEvent(const std::string& file, int line) { + json j = { + {"type", "break"}, + {"file", file}, + {"line", line} + }; + + std::cout << "@@DEBUG@@" << j.dump() << std::endl; + std::cout.flush(); + } + + std::string waitForDebugCommand() { + //printf("HOLA"); + while (true) { + { + std::lock_guard lock(g_cmdMutex); + if (!g_debugCommands.empty()) { + std::string cmd = g_debugCommands.front(); + g_debugCommands.pop(); + return cmd; + } + } + + SDL_Delay(1); + } + } + + bool shouldPauseForStepping(lua_State* L, lua_Debug* ar) { + int depth = getStackDepth(L); + + switch (g_stepMode) { + case STEP_INTO: + return true; // siempre parar en la siguiente línea + + case STEP_OVER: + return depth <= g_stepDepth; + + case STEP_OUT: + return depth < g_stepDepth; + + default: + return false; + } + } + + void captureStack(lua_State* L) { + g_frames.clear(); + + lua_Debug ar; + int level = 0; + + while (lua_getstack(L, level, &ar)) { + lua_getinfo(L, "nSl", &ar); + + FrameInfo fi; + fi.level = level; + fi.source = ar.source; + fi.line = ar.currentline; + + g_frames.push_back(fi); + level++; + } + } + + void pauseHere(lua_State* L, const std::string& src, int line) { + g_paused = true; + g_pauseFile = src; + g_pauseLine = line; + g_stepMode = STEP_NONE; + + captureStack(L); + + sendBreakEvent(chunkToPath(src), line); + + while (g_paused) { + std::string cmd = waitForDebugCommand(); + processDebugCommand(L, cmd); + } + } + + extern "C" void luaHook(lua_State* L, lua_Debug* ar) { + lua_getinfo(L, "Sl", ar); + + const char* src = ar->source; + if (src[0]=='=') return; + + if (ar->event == LUA_HOOKCALL) { + funBreakStack.push(function_has_breakpoints); + bool new_function_has_breakpoints = false; + { + std::lock_guard lock(g_breakMutex); + auto it = g_breakpoints.find(src); + new_function_has_breakpoints = (it != g_breakpoints.end() && !it->second.empty()); + } + if (new_function_has_breakpoints || g_stepMode != STEP_NONE) { + if (!function_has_breakpoints) + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKLINE | LUA_MASKRET, 0); + } else { + //if (function_has_breakpoints) + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKRET, 0); + } + function_has_breakpoints = new_function_has_breakpoints; + } + else if (ar->event == LUA_HOOKRET) { + // Siempre volver al hook base + bool new_function_has_breakpoints = funBreakStack.empty() ? false : funBreakStack.top(); + funBreakStack.pop(); + if (new_function_has_breakpoints || g_stepMode != STEP_NONE) { + if (!function_has_breakpoints) + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKLINE | LUA_MASKRET, 0); + } else { + //if (function_has_breakpoints) + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKRET, 0); + } + function_has_breakpoints = new_function_has_breakpoints; + } + else if (ar->event == LUA_HOOKLINE) { + if (ar->currentline <= 0) return; + const char* src = ar->source; + int line = ar->currentline; + + if (isBreakpoint(L, src, line)) { + pauseHere(L, src, line); + return; + } + + // 2. Stepping + if (g_stepMode != STEP_NONE) { + if (shouldPauseForStepping(L, ar)) { + pauseHere(L, src, line); + return; + } + } + } + } + + void sendExceptionStoppedEvent(const std::string& msg) { + json payload = { + { "reason", "exception" }, + { "text", msg } + }; + sendDebugResponse("stopped", payload); + } + + int luaErrorHandler(lua_State* L) { + const char* msg = lua_tostring(L, 1); + if (!msg) msg = "Unknown error"; + + // Construir traceback + luaL_traceback(L, L, msg, 1); + + const char* tb = lua_tostring(L, -1); + if (tb) { + lastExceptionTraceback = tb; + } else { + lastExceptionTraceback = msg; + } + + // Devolver el traceback a Lua (aunque luego Lua lo sustituya) + return 1; + } + + bool call_and_handle_exceptions(lua_State* L) + { + lua_pushcfunction(L, luaErrorHandler); + lua_insert(L, -2); // poner handler debajo de la función + int status = lua_pcall(L, 0, 0, -2); + lua_pop(L, 1); // quitar handler + + if (status != LUA_OK) { + lastExceptionMessage = lastExceptionTraceback; + lua_pop(L, 1); + hasException = true; + + if (exceptionFilters.count("all") || exceptionFilters.count("uncaught")) { + sendExceptionStoppedEvent(lastExceptionMessage); + return false; + } + + sendLogOutput(lastExceptionMessage); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Mini Runtime Exception", lastExceptionTraceback.c_str(), NULL); + return false; + } + return true; + } + + std::jthread stdinThread([](std::stop_token st) { + std::string line; + + struct pollfd pfd; + pfd.fd = 0; // stdin + pfd.events = POLLIN; // queremos saber si hay datos + + while (!st.stop_requested()) { + int ret = poll(&pfd, 1, 10); // timeout 10 ms + + if (ret > 0 && (pfd.revents & POLLIN)) { + if (!std::getline(std::cin, line)) { + break; // EOF + } + + { + std::lock_guard lock(g_cmdMutex); + g_debugCommands.push(line); + printf("COMANDO RECIBIDO: %s\n", line.c_str()); + } + } else { + SDL_Delay(1); + } + } + }); + + void process_commands(lua_State* L) { + function_has_breakpoints = false; + while (!funBreakStack.empty()) funBreakStack.pop(); + + while (true) { + std::string cmd; + + { + std::lock_guard lock(g_cmdMutex); + if (g_debugCommands.empty()) + break; + + cmd = g_debugCommands.front(); + g_debugCommands.pop(); + } + + processDebugCommand(L, cmd); + } + } + + void kill_thread() { + g_running = false; + stdinThread.request_stop(); + } + + void toggle(lua_State* L) { + if (debug_enabled) { + debug_enabled = false; + lua_sethook(L, NULL,0, 0); + } else { + debug_enabled = true; + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKRET, 0); + } + } + + } + } +} diff --git a/source/mini/lua/lua.debug.h b/source/mini/lua/lua.debug.h new file mode 100644 index 0000000..fdb81ce --- /dev/null +++ b/source/mini/lua/lua.debug.h @@ -0,0 +1,17 @@ +#pragma once +struct lua_State; + +namespace mini +{ + namespace lua + { + namespace debug + { + bool call_and_handle_exceptions(lua_State* L); + void kill_thread(); + void process_commands(lua_State* L); + void toggle(lua_State* L); + } + + } +} diff --git a/source/mini/lua/lua.h b/source/mini/lua/lua.h new file mode 100644 index 0000000..99347b8 --- /dev/null +++ b/source/mini/lua/lua.h @@ -0,0 +1,18 @@ +#pragma once + +namespace mini +{ + namespace lua + { + bool running(); + void init(const char* main_lua_file = "main.lua"); + void quit(); + void cleanup(); + + namespace callbacks + { + void init(); + void update(); + } + } +} diff --git a/source/mini/lua/lua.wrappers.cpp b/source/mini/lua/lua.wrappers.cpp new file mode 100644 index 0000000..8b8151a --- /dev/null +++ b/source/mini/lua/lua.wrappers.cpp @@ -0,0 +1,1322 @@ +#include "lua.wrappers.h" +#include "external/lua/lua.hpp" + +#include "mini/mini.h" +#include "mini/win/win.h" +#include "mini/view/view.h" +#include "mini/shader/shader.h" +#include "mini/audio/audio.h" +#include "mini/config/config.h" +#include "mini/mouse/mouse.h" +#include "mini/key/key.h" +#include "mini/pad/pad.h" + +#include "mini/version.h" + +#include + +#include +#include +#include + +namespace mini +{ + namespace lua + { + namespace wrappers + { + static int utf8_sub(lua_State *L) { + size_t slen; + const char *s = luaL_checklstring(L, 1, &slen); + int i = luaL_checkinteger(L, 2); + int j = luaL_optinteger(L, 3, -1); + + // Get utf8.offset + lua_getglobal(L, "utf8"); + lua_getfield(L, -1, "offset"); + + // Compute start byte index + lua_pushvalue(L, 1); // string + lua_pushinteger(L, i); // start index + lua_call(L, 2, 1); // utf8.offset(s, i) + lua_Integer start = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (start == 0) { + lua_pushliteral(L, ""); + lua_pop(L, 1); // pop utf8 table + return 1; + } + + // Compute end byte index (j+1) + lua_getfield(L, -1, "offset"); + lua_pushvalue(L, 1); + lua_pushinteger(L, j + 1); + lua_call(L, 2, 1); + lua_Integer end = lua_tointeger(L, -1); + lua_pop(L, 2); // pop result + utf8 table + + if (end == 0) { + // until end of string + lua_pushlstring(L, s + start - 1, slen - (start - 1)); + return 1; + } + + lua_pushlstring(L, s + start - 1, (end - 1) - (start - 1)); + return 1; + } + + namespace surf + { + static int create(lua_State *L) { + int w = luaL_checknumber(L, 1); + int h = luaL_checknumber(L, 2); + uint8_t s = mini::surf::create(w, h); + if (s==255) { + luaL_error(L, "Error while creating new surface: Max surfaces reached"); + return 0; + } + lua_pushinteger(L, s); + return 1; + } + + static int load(lua_State *L) { + const char* str = luaL_checkstring(L, 1); + uint8_t s = mini::surf::load(str); + if (s==255) { + luaL_error(L, "Error while loading surface: Max surfaces reached"); + return 0; + } + lua_pushinteger(L, s); + return 1; + } + + static int loadex(lua_State *L) { + const char* str = luaL_checkstring(L, 1); + uint8_t s = mini::surf::load(str, true); + if (s==255) { + luaL_error(L, "Error while loading surface: Max surfaces reached"); + return 0; + } + lua_pushinteger(L, s); + return 1; + } + + static int save(lua_State *L) { + uint8_t surface = luaL_checkinteger(L, 1); + const char* str = luaL_checkstring(L, 2); + + if (lua_istable(L, -1)) { + const int len = SDL_min(256, lua_rawlen(L, -1)); + uint8_t *pal = (uint8_t*)malloc(len*3); + uint8_t *p=pal; + for (int i=1;i<=len;++i) { + lua_rawgeti(L, -1, i); + lua_getfield(L, -1, "r"); + *(p++) = luaL_checknumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, -1, "g"); + *(p++) = luaL_checknumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, -1, "b"); + *(p++) = luaL_checknumber(L, -1); + lua_pop(L, 1); + lua_pop(L, 1); + //pal[i-1] = (r<<16)+(g<<8)+b; + } + mini::surf::save(surface, str, pal, len); + } else { + mini::surf::save(surface, str, nullptr); + } + + return 0; + } + + static int free(lua_State *L) { + uint8_t surface = luaL_checkinteger(L, 1); + mini::surf::destroy(surface); + return 0; + } + + static int size(lua_State *L) { + uint8_t surface = luaL_checkinteger(L, 1); + lua_pushinteger(L, mini::surf::width(surface)); + lua_pushinteger(L, mini::surf::height(surface)); + return 2; + } + + static int target(lua_State *L) { + const int numargs = lua_gettop(L); + switch (numargs) { + case 0: { + lua_pushinteger(L, mini::surf::target::get()); + return 1; + } + case 1: { + uint8_t surface = luaL_checkinteger(L, 1); + mini::surf::target::set(surface); + return 0; + } + default: + return luaL_error(L, "Function 'surface.target' Unexpected number of parameters."); + }; + } + + static int source(lua_State *L) { + const int numargs = lua_gettop(L); + switch (numargs) { + case 0: { + lua_pushinteger(L, mini::surf::source::get()); + return 1; + } + case 1: { + uint8_t surface = luaL_checkinteger(L, 1); + mini::surf::source::set(surface); + return 0; + } + default: + return luaL_error(L, "Function 'surface.source' Unexpected number of parameters."); + }; + } + + static int cls(lua_State *L) { + uint8_t color = luaL_optinteger(L, 1, 0); + mini::surf::cls(color); + return 0; + } + + static int clip(lua_State *L) { + if (lua_gettop(L)==0) { + mini::surf::clip::reset(); + return 0; + } else { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int w = luaL_checknumber(L, 3); + int h = luaL_checknumber(L, 4); + mini::surf::clip::set(x, y, w, h); + return 0; + } + } + + static int pixel(lua_State *L) { + if (lua_gettop(L)==2) { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + lua_pushinteger(L, mini::surf::pixel::get(x, y)); + return 1; + } else { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + uint8_t color = luaL_checkinteger(L, 3); + mini::surf::pixel::set(x, y, color); + return 0; + } + } + } + + namespace map + { + static int surf(lua_State *L) { + if (lua_gettop(L)==1) { + uint8_t surface = luaL_checkinteger(L, 1); + mini::map::surf::set(surface); + } else { + lua_pushinteger(L, mini::map::surf::get()); + return 1; + } + + return 0; + } + + static int draw(lua_State *L) { + mini::map::draw(); + return 0; + } + + static int tile(lua_State *L) { + if (lua_gettop(L)==2) { + int celx = luaL_checknumber(L, 1); + int cely = luaL_checknumber(L, 2); + lua_pushinteger(L, mini::map::tile::get(celx, cely)); + return 1; + } else { + int celx = luaL_checknumber(L, 1); + int cely = luaL_checknumber(L, 2); + uint8_t snum = luaL_checkinteger(L, 3); + mini::map::tile::set(celx, cely, snum); + return 0; + } + } + + static int cell(lua_State *L) { + if (lua_gettop(L)==2) { + int celx = luaL_checknumber(L, 1); + int cely = luaL_checknumber(L, 2); + mini::map::cell::set(celx, cely); + return 0; + } else { + lua_pushinteger(L, mini::map::cell::getw()); + lua_pushinteger(L, mini::map::cell::geth()); + return 2; + } + } + + } + + namespace pal + { + static int load(lua_State *L) { + const char* str = luaL_checkstring(L, 1); + uint16_t size; + uint32_t *pal = mini::pal::load(str, &size); + lua_createtable(L, 2, 0); + for (int i=0;i>16)&0xff); + lua_setfield(L, -2, "r"); + + lua_pushinteger(L, (color>>8)&0xff); + lua_setfield(L, -2, "g"); + + lua_pushinteger(L, color&0xff); + lua_setfield(L, -2, "b"); + + lua_rawseti(L, -2, i+1); + } + free(pal); + return 1; + } + + static int set(lua_State *L) { + int r,g,b; + if (lua_istable(L, -1)) { + uint32_t pal[256]; + const int len = SDL_min(256, lua_rawlen(L, -1)); + for (int i=0;i>16)&0xff); + lua_pushinteger(L, (color>>8)&0xff); + lua_pushinteger(L, color&0xff); + return 3; + } else { + uint8_t index = luaL_checkinteger(L, 1); + uint8_t r = luaL_checkinteger(L, 2); + uint8_t g = luaL_optinteger(L, 3, 0); + uint8_t b = luaL_optinteger(L, 4, 0); + uint32_t color = (r<<16) + (g<<8) + b; + mini::pal::color::set(index, color); + return 0; + } + } + + static int trans(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushinteger(L, mini::pal::trans::get()); + return 1; + } else { + uint8_t index = luaL_checkinteger(L, 1); + mini::pal::trans::set(index); + return 0; + } + } + + static int subpal(lua_State *L) { + const int num_params = lua_gettop(L); + uint8_t index, index2, color; + switch (num_params) { + case 0: + mini::pal::subpal::reset(); + break; + case 1: + index = luaL_checkinteger(L, 1); + lua_pushinteger(L, mini::pal::subpal::set(index,index)); + return 1; + break; + case 2: + index = luaL_checkinteger(L, 1); + color = luaL_checkinteger(L, 2); + lua_pushinteger(L, mini::pal::subpal::set(index, color)); + return 1; + break; + case 3: + index = luaL_checkinteger(L, 1); + index2 = luaL_checkinteger(L, 2); + color = luaL_checkinteger(L, 3); + for (int i=index;i<=index2;++i) mini::pal::subpal::set(i, color); + break; + } + return 0; + } + } + + namespace view + { + static int origin(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushinteger(L, mini::view::origin::getx()); + lua_pushinteger(L, mini::view::origin::gety()); + return 2; + } else { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + mini::view::origin::set(x, y); + return 0; + } + } + + static int tolocal(lua_State *L) { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + lua_pushinteger(L, x+mini::view::origin::getx()); + lua_pushinteger(L, y+mini::view::origin::gety()); + return 2; + } + } + + namespace draw + { + static int line(lua_State *L) { + int x0 = luaL_checknumber(L, 1); + int y0 = luaL_checknumber(L, 2); + int x1 = luaL_checknumber(L, 3); + int y1 = luaL_checknumber(L, 4); + uint8_t color = luaL_checkinteger(L, 5); + mini::draw::line(x0, y0, x1, y1, color); + return 0; + } + + static int hline(lua_State *L) { + int x0 = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int x1 = luaL_checknumber(L, 3); + uint8_t color = luaL_checkinteger(L, 4); + mini::draw::hline(x0, y, x1, color); + return 0; + } + + static int vline(lua_State *L) { + int x = luaL_checknumber(L, 1); + int y0 = luaL_checknumber(L, 2); + int y1 = luaL_checknumber(L, 3); + uint8_t color = luaL_checkinteger(L, 4); + mini::draw::vline(x, y0, y1, color); + return 0; + } + + static int rect(lua_State *L) { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int w = luaL_checknumber(L, 3); + int h = luaL_checknumber(L, 4); + uint8_t color = luaL_checkinteger(L, 5); + mini::draw::rect(x, y, w, h, color); + return 0; + } + + static int rectf(lua_State *L) { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int w = luaL_checknumber(L, 3); + int h = luaL_checknumber(L, 4); + uint8_t color = luaL_checkinteger(L, 5); + mini::draw::rectf(x, y, w, h, color); + return 0; + } + + static int circ(lua_State *L) { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int r = luaL_optnumber(L, 3, 4); + uint8_t color = luaL_checkinteger(L, 4); + mini::draw::circ(x, y, r, color); + return 0; + } + + static int circf(lua_State *L) { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int r = luaL_optnumber(L, 3, 4); + uint8_t color = luaL_checkinteger(L, 4); + mini::draw::circf(x, y, r, color); + return 0; + } + + static int roundrect(lua_State *L) { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int w = luaL_checknumber(L, 3); + int h = luaL_checknumber(L, 4); + int r = luaL_optnumber(L, 5, 4); + uint8_t color = luaL_checkinteger(L, 6); + mini::draw::roundrect(x, y, w, h, r, color); + return 0; + } + + static int roundrectf(lua_State *L) { + int x = luaL_checknumber(L, 1); + int y = luaL_checknumber(L, 2); + int w = luaL_checknumber(L, 3); + int h = luaL_checknumber(L, 4); + int r = luaL_optnumber(L, 5, 4); + uint8_t color = luaL_checkinteger(L, 6); + mini::draw::roundrectf(x, y, w, h, r, color); + return 0; + } + + static int oval(lua_State *L) { + int x0 = luaL_checknumber(L, 1); + int y0 = luaL_checknumber(L, 2); + int x1 = luaL_checknumber(L, 3); + int y1 = luaL_checknumber(L, 4); + uint8_t color = luaL_checkinteger(L, 5); + mini::draw::oval(x0, y0, x1, y1, color); + return 0; + } + + static int ovalf(lua_State *L) { + int x0 = luaL_checknumber(L, 1); + int y0 = luaL_checknumber(L, 2); + int x1 = luaL_checknumber(L, 3); + int y1 = luaL_checknumber(L, 4); + uint8_t color = luaL_checkinteger(L, 5); + mini::draw::ovalf(x0, y0, x1, y1, color); + return 0; + } + + static int pattern(lua_State *L) { + if (lua_gettop(L) == 0) { + mini::draw::pattern::set(0xffff); + } else { + uint16_t pat = luaL_checkinteger(L, 1); + bool transparent = lua_toboolean(L, 2); + mini::draw::pattern::set(pat, transparent); + } + return 0; + } + + static int surf(lua_State *L) { + int sx = luaL_checknumber(L, 1); + int sy = luaL_checknumber(L, 2); + int sw = luaL_checknumber(L, 3); + int sh = luaL_checknumber(L, 4); + int dx = luaL_checknumber(L, 5); + int dy = luaL_checknumber(L, 6); + int dw = luaL_optnumber(L, 7, 0); + int dh = luaL_optnumber(L, 8, 0); + bool flip_x = lua_toboolean(L, 9); + bool flip_y = lua_toboolean(L, 10); + bool invert = lua_toboolean(L, 11); + mini::draw::surf(sx, sy, sw, sh, dx, dy, dw, dh, flip_x, flip_y, invert); + return 0; + } + + static int surfrot(lua_State *L) { + int sx = luaL_checknumber(L, 1); + int sy = luaL_checknumber(L, 2); + int sw = luaL_checknumber(L, 3); + int sh = luaL_checknumber(L, 4); + int dx = luaL_checknumber(L, 5); + int dy = luaL_checknumber(L, 6); + float a = luaL_checknumber(L, 7); + int dw = luaL_optnumber(L, 8, 0); + int dh = luaL_optnumber(L, 9, 0); + bool flip_x = lua_toboolean(L, 10); + bool flip_y = lua_toboolean(L, 11); + mini::draw::surfrot(sx, sy, sw, sh, dx, dy, dw, dh, flip_x, flip_y, a); + return 0; + } + + static int text(lua_State *L) { + const char* str = luaL_checkstring(L, 1); + int x = luaL_checknumber(L, 2); + int y = luaL_checknumber(L, 3); + uint8_t color = luaL_checkinteger(L, 4); + mini::draw::text(str, x, y, color); + return 0; + } + + static int mode(lua_State *L) { + int mode = luaL_checknumber(L, 1); + mini::draw::mode::set(mode); + return 0; + } + } + + namespace shader + { + static int init(lua_State *L) { + const char* vstr = luaL_optstring(L, 1, NULL); + const char* fstr = luaL_optstring(L, 2, NULL); + mini::shader::init(vstr, fstr); + return 0; + } + + static int enable(lua_State *L) { + mini::shader::enable(); + return 0; + } + + static int disable(lua_State *L) { + mini::shader::disable(); + return 0; + } + } + + namespace music + { + static int play(lua_State *L) { + const char* str = luaL_checkstring(L, 1); + const int loop = luaL_optinteger(L, 2, -1); + mini::audio::music::play(str, loop); + return 0; + } + + static int pause(lua_State *L) { + mini::audio::music::pause(); + return 0; + } + + static int resume(lua_State *L) { + mini::audio::music::resume(); + return 0; + } + + static int stop(lua_State *L) { + const int time = luaL_optinteger(L, 1, 1000); + mini::audio::music::stop(time); + return 0; + } + + static int pos(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushnumber(L, mini::audio::music::pos::get()); + return 1; + } else { + mini::audio::music::pos::set(luaL_checknumber(L, 1)); + return 0; + } + } + + static int enable(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushboolean(L, mini::audio::music::enable::get()); + return 1; + } else { + mini::audio::music::enable::set(lua_toboolean(L, 1)); + return 0; + } + } + } + + namespace sound + { + static int load(lua_State *L) { + const char* str = luaL_checkstring(L, 1); + lua_pushinteger(L, mini::audio::sound::load(str)); + return 1; + } + + static int free(lua_State *L) { + const int sound = luaL_checknumber(L, 1); + mini::audio::sound::free(sound); + return 0; + } + + static int play(lua_State *L) { + const int sound = luaL_checknumber(L, 1); + const int volume = luaL_optinteger(L, 2, -1); + lua_pushinteger(L, mini::audio::sound::play(sound, volume)); + return 1; + } + + static int stop(lua_State *L) { + const int sound = luaL_checknumber(L, 1); + mini::audio::sound::stop(sound); + return 0; + } + + static int enable(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushboolean(L, mini::audio::sound::enable::get()); + return 1; + } else { + mini::audio::sound::enable::set(lua_toboolean(L, 1)); + return 0; + } + } + } + + namespace sys + { + static int delta(lua_State *L) { + lua_pushnumber(L, mini::sys::delta()); + return 1; + } + + static int time(lua_State *L) { + lua_pushnumber(L, mini::sys::time()); + return 1; + } + + static uint32_t chrono_time = 0; + static int chrono(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushnumber(L, float(SDL_GetTicks()-chrono_time)/1000.0f); + return 1; + } else { + chrono_time = SDL_GetTicks(); + return 0; + } + } + + static int beat(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushboolean(L, mini::sys::beat(-1)); + return 1; + } else { + int16_t beats = luaL_optnumber(L, 1, -1); + mini::sys::beat(beats); + return 0; + } + } + + static int update(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushinteger(L, mini::sys::update::get()); + return 1; + } else { + int mode = luaL_checknumber(L, 1); + int interval = luaL_optinteger(L, 2, 0); + mini::sys::update::set(mode, interval); + return 0; + } + } + + namespace fs = std::filesystem; + static int dir(lua_State *L) { + std::string path = "./data"; + if (lua_gettop(L) > 0) path = luaL_checkstring(L, 1); + + // Collect entries + std::vector entries; + for (const auto& entry : fs::directory_iterator(path)) { + entries.push_back(entry); + } + + // Sort: directories first, then files; both alphabetically + std::sort(entries.begin(), entries.end(), + [](const fs::directory_entry& a, const fs::directory_entry& b) { + bool adir = a.is_directory(); + bool bdir = b.is_directory(); + if (adir != bdir) return adir > bdir; // directories before files + return a.path().filename().string() < b.path().filename().string(); + }); + + // Build Lua table + lua_newtable(L); + int i = 1; + for (const auto& entry : entries) { + lua_newtable(L); + + // name field (canonical absolute path) + lua_pushstring(L, (const char*)fs::canonical(entry.path()).u8string().c_str()); + lua_setfield(L, -2, "name"); + + // dir field + lua_pushboolean(L, entry.is_directory()); + lua_setfield(L, -2, "dir"); + + lua_rawseti(L, -2, i++); + } + + return 1; + } + + static int exit(lua_State *L) { + mini::sys::exit(); + return 0; + } + + static int fps(lua_State *L) { + lua_pushnumber(L, mini::sys::getfps()); + return 1; + } + + static int debug(lua_State *L) { + #ifdef DEBUG + lua_pushboolean(L, true); + #else + lua_pushboolean(L, false); + #endif + return 1; + } + + static int clipboard(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushstring(L, SDL_GetClipboardText()); + return 1; + } else { + const char* value = luaL_checkstring(L, 1); + SDL_SetClipboardText(value); + return 0; + } + } + + static int version(lua_State *L) { + lua_pushstring(L, MINI_VERSION); + return 1; + } + } + + namespace win + { + static int zoom(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushinteger(L, mini::win::zoom::get()); + return 1; + } else { + const int value = luaL_optinteger(L, 1, 0); + mini::win::zoom::set(value); + return 0; + } + } + + static int fullscreen(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushboolean(L, mini::win::fullscreen::get()); + return 1; + } else { + mini::win::fullscreen::set(lua_toboolean(L, 1)); + return 0; + } + } + + static int cursor(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushboolean(L, mini::win::cursor::get()); + return 1; + } else { + mini::win::cursor::set(lua_toboolean(L, 1)); + return 0; + } + } + + static int res(lua_State *L) { + if (lua_gettop(L) == 0) { + lua_pushinteger(L, mini::win::res::getw()); + lua_pushinteger(L, mini::win::res::geth()); + return 2; + } else { + const int w = luaL_optinteger(L, 1, 160); + const int h = luaL_optinteger(L, 2, 120); + mini::win::res::set(w, h); + return 0; + } + } + } + + namespace conf + { + static int key(lua_State *L) { + const char* key = luaL_checkstring(L, 1); + if (lua_gettop(L) > 1) { + const char* value = luaL_checkstring(L, 2); + mini::config::key::set(key, value); + return 0; + } else { + const char* value = mini::config::key::get(key); + if (value==NULL) { + lua_pushnil(L); + } else { + lua_pushstring(L, value); + } + return 1; + } + } + + static int folder(lua_State *L) { + lua_pushstring(L, mini::config::folder()); + return 1; + } + } + + namespace font + { + static int load(lua_State *L) { + const char* str = luaL_checkstring(L, 1); + uint8_t s = mini::font::load(str); + if (s==255) { + luaL_error(L, "Error while loading font: Max fonts reached"); + return 0; + } + lua_pushinteger(L, s); + return 1; + } + + static int current(lua_State *L) { + const int numargs = lua_gettop(L); + switch (numargs) { + case 0: { + lua_pushinteger(L, mini::font::current::get()); + return 1; + } + case 1: { + uint8_t font = luaL_checkinteger(L, 1); + mini::font::current::set(font); + return 0; + } + default: + return luaL_error(L, "Function 'font.current' Unexpected number of parameters."); + }; + } + + static int spacing(lua_State *L) { + const int numargs = lua_gettop(L); + switch (numargs) { + case 0: { + lua_pushinteger(L, mini::font::spacing::get()); + return 1; + } + case 1: { + uint8_t spacing = luaL_checkinteger(L, 1); + mini::font::spacing::set(spacing); + return 0; + } + default: + return luaL_error(L, "Function 'font.spacing' Unexpected number of parameters."); + }; + } + } + + namespace mouse + { + static int pos(lua_State *L) { + lua_pushinteger(L, mini::mouse::posx()); + lua_pushinteger(L, mini::mouse::posy()); + return 2; + } + + static int wheel(lua_State *L) { + lua_pushinteger(L, mini::mouse::wheel()); + return 1; + } + + static int down(lua_State *L) { + uint8_t i = luaL_checkinteger(L, 1); + lua_pushboolean(L, mini::mouse::down(i)); + return 1; + } + + static int press(lua_State *L) { + uint8_t i = luaL_checkinteger(L, 1); + lua_pushboolean(L, mini::mouse::press(i)); + return 1; + } + + static int dblclick(lua_State *L) { + lua_pushboolean(L, mini::mouse::dblclick()); + return 1; + } + + static int discard(lua_State *L) { + mini::mouse::discard(); + return 0; + } + + static int inside(lua_State *L) { + int x = luaL_checkinteger(L, 1); + int y = luaL_checkinteger(L, 2); + int w = luaL_checkinteger(L, 3); + int h = luaL_checkinteger(L, 4); + lua_pushboolean(L, mini::mouse::inside(x,y,w,h)); + return 1; + } + } + + namespace key + { + static int down(lua_State *L) { + uint8_t i = luaL_checkinteger(L, 1); + lua_pushboolean(L, mini::key::down(i)); + return 1; + } + + static int press(lua_State *L) { + if (lua_gettop(L) >=1 ) { + uint8_t i = luaL_checkinteger(L, 1); + lua_pushboolean(L, mini::key::press(i)); + } else { + lua_pushinteger(L, mini::key::press()); + } + return 1; + } + + static int any(lua_State *L) { + lua_pushboolean(L, mini::key::any()); + return 1; + } + + static int text(lua_State *L) { + mini::key::text(lua_toboolean(L, 1)); + return 0; + } + + static int utf8char(lua_State *L) { + lua_pushstring(L, mini::key::utf8char()); + return 1; + } + } + + namespace pad + { + static int down(lua_State *L) { + int8_t i = luaL_checkinteger(L, 1); + lua_pushboolean(L, mini::pad::down(i)); + return 1; + } + + static int press(lua_State *L) { + if (lua_gettop(L) >=1 ) { + int8_t i = luaL_checkinteger(L, 1); + lua_pushboolean(L, mini::pad::press(i)); + } else { + lua_pushinteger(L, mini::pad::press()); + } + return 1; + } + + static int any(lua_State *L) { + lua_pushinteger(L, mini::pad::press()); + return 1; + } + + } + } + + void push_functions(lua_State *L) { + lua_newtable(L); + lua_setglobal(L, "mini"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::surf::create); lua_setfield(L, -2, "new"); + lua_pushcfunction(L,wrappers::surf::load); lua_setfield(L, -2, "load"); + lua_pushcfunction(L,wrappers::surf::loadex); lua_setfield(L, -2, "loadex"); + lua_pushcfunction(L,wrappers::surf::save); lua_setfield(L, -2, "save"); + lua_pushcfunction(L,wrappers::surf::free); lua_setfield(L, -2, "free"); + lua_pushcfunction(L,wrappers::surf::size); lua_setfield(L, -2, "size"); + lua_pushcfunction(L,wrappers::surf::target); lua_setfield(L, -2, "target"); + lua_pushcfunction(L,wrappers::surf::source); lua_setfield(L, -2, "source"); + lua_pushcfunction(L,wrappers::surf::cls); lua_setfield(L, -2, "cls"); + lua_pushcfunction(L,wrappers::surf::clip); lua_setfield(L, -2, "clip"); + lua_pushcfunction(L,wrappers::surf::pixel); lua_setfield(L, -2, "pixel"); + + lua_pushinteger(L, 0); lua_setfield(L, -2, "SCREEN"); + lua_setglobal(L, "surf"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::map::surf); lua_setfield(L, -2, "surf"); + lua_pushcfunction(L,wrappers::map::draw); lua_setfield(L, -2, "draw"); + lua_pushcfunction(L,wrappers::map::tile); lua_setfield(L, -2, "tile"); + lua_pushcfunction(L,wrappers::map::cell); lua_setfield(L, -2, "cell"); + lua_setglobal(L, "map"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::pal::load); lua_setfield(L, -2, "load"); + lua_pushcfunction(L,wrappers::pal::set); lua_setfield(L, -2, "set"); + lua_pushcfunction(L,wrappers::pal::color); lua_setfield(L, -2, "color"); + lua_pushcfunction(L,wrappers::pal::trans); lua_setfield(L, -2, "trans"); + lua_pushcfunction(L,wrappers::pal::subpal); lua_setfield(L, -2, "subpal"); + lua_setglobal(L, "pal"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::view::origin); lua_setfield(L, -2, "origin"); + lua_pushcfunction(L,wrappers::view::tolocal); lua_setfield(L, -2, "tolocal"); + lua_setglobal(L, "view"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::draw::line); lua_setfield(L, -2, "line"); + lua_pushcfunction(L,wrappers::draw::hline); lua_setfield(L, -2, "hline"); + lua_pushcfunction(L,wrappers::draw::vline); lua_setfield(L, -2, "vline"); + lua_pushcfunction(L,wrappers::draw::rect); lua_setfield(L, -2, "rect"); + lua_pushcfunction(L,wrappers::draw::rectf); lua_setfield(L, -2, "rectf"); + lua_pushcfunction(L,wrappers::draw::circ); lua_setfield(L, -2, "circ"); + lua_pushcfunction(L,wrappers::draw::circf); lua_setfield(L, -2, "circf"); + lua_pushcfunction(L,wrappers::draw::roundrect); lua_setfield(L, -2, "rrect"); + lua_pushcfunction(L,wrappers::draw::roundrectf); lua_setfield(L, -2, "rrectf"); + lua_pushcfunction(L,wrappers::draw::oval); lua_setfield(L, -2, "oval"); + lua_pushcfunction(L,wrappers::draw::ovalf); lua_setfield(L, -2, "ovalf"); + lua_pushcfunction(L,wrappers::draw::pattern); lua_setfield(L, -2, "pattern"); + lua_pushcfunction(L,wrappers::draw::surf); lua_setfield(L, -2, "surf"); + lua_pushcfunction(L,wrappers::draw::surfrot); lua_setfield(L, -2, "surfrot"); + lua_pushcfunction(L,wrappers::draw::text); lua_setfield(L, -2, "text"); + lua_pushcfunction(L,wrappers::draw::mode); lua_setfield(L, -2, "mode"); + + lua_pushinteger(L, 0); lua_setfield(L, -2, "NORMAL"); + lua_pushinteger(L, 1); lua_setfield(L, -2, "PATTERN"); + lua_pushinteger(L, 2); lua_setfield(L, -2, "AND"); + lua_pushinteger(L, 3); lua_setfield(L, -2, "OR"); + lua_pushinteger(L, 4); lua_setfield(L, -2, "XOR"); + lua_pushinteger(L, 5); lua_setfield(L, -2, "NOT"); + + lua_setglobal(L, "draw"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::shader::init); lua_setfield(L, -2, "init"); + lua_pushcfunction(L,wrappers::shader::enable); lua_setfield(L, -2, "enable"); + lua_pushcfunction(L,wrappers::shader::disable); lua_setfield(L, -2, "disable"); + lua_setglobal(L, "shader"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::music::play); lua_setfield(L, -2, "play"); + lua_pushcfunction(L,wrappers::music::pause); lua_setfield(L, -2, "pause"); + lua_pushcfunction(L,wrappers::music::resume); lua_setfield(L, -2, "resume"); + lua_pushcfunction(L,wrappers::music::stop); lua_setfield(L, -2, "stop"); + lua_pushcfunction(L,wrappers::music::pos); lua_setfield(L, -2, "pos"); + lua_pushcfunction(L,wrappers::music::enable); lua_setfield(L, -2, "enabled"); + lua_setglobal(L, "music"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::sound::load); lua_setfield(L, -2, "load"); + lua_pushcfunction(L,wrappers::sound::free); lua_setfield(L, -2, "free"); + lua_pushcfunction(L,wrappers::sound::play); lua_setfield(L, -2, "play"); + lua_pushcfunction(L,wrappers::sound::stop); lua_setfield(L, -2, "stop"); + lua_pushcfunction(L,wrappers::sound::enable); lua_setfield(L, -2, "enabled"); + lua_setglobal(L, "sound"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::sys::delta); lua_setfield(L, -2, "delta"); + lua_pushcfunction(L,wrappers::sys::time); lua_setfield(L, -2, "time"); + lua_pushcfunction(L,wrappers::sys::chrono); lua_setfield(L, -2, "chrono"); + lua_pushcfunction(L,wrappers::sys::beat); lua_setfield(L, -2, "beat"); + lua_pushcfunction(L,wrappers::sys::update); lua_setfield(L, -2, "update"); + lua_pushcfunction(L,wrappers::sys::dir); lua_setfield(L, -2, "dir"); + lua_pushcfunction(L,wrappers::sys::exit); lua_setfield(L, -2, "quit"); + lua_pushcfunction(L,wrappers::sys::fps); lua_setfield(L, -2, "fps"); + lua_pushcfunction(L,wrappers::sys::debug); lua_setfield(L, -2, "debug"); + lua_pushcfunction(L,wrappers::sys::clipboard); lua_setfield(L, -2, "clipboard"); + lua_pushcfunction(L,wrappers::sys::version); lua_setfield(L, -2, "version"); + + lua_pushinteger(L, 0); lua_setfield(L, -2, "UPDATE_ALWAYS"); + lua_pushinteger(L, 1); lua_setfield(L, -2, "UPDATE_WAIT"); + lua_pushinteger(L, 2); lua_setfield(L, -2, "UPDATE_TIMEOUT"); + + lua_setglobal(L, "sys"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::win::zoom); lua_setfield(L, -2, "zoom"); + lua_pushcfunction(L,wrappers::win::fullscreen); lua_setfield(L, -2, "fullscreen"); + lua_pushcfunction(L,wrappers::win::cursor); lua_setfield(L, -2, "cursor"); + lua_pushcfunction(L,wrappers::win::res); lua_setfield(L, -2, "res"); + lua_setglobal(L, "win"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::conf::key); lua_setfield(L, -2, "key"); + lua_pushcfunction(L,wrappers::conf::folder); lua_setfield(L, -2, "folder"); + lua_setglobal(L, "config"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::font::load); lua_setfield(L, -2, "load"); + lua_pushcfunction(L,wrappers::font::current); lua_setfield(L, -2, "current"); + lua_pushcfunction(L,wrappers::font::spacing); lua_setfield(L, -2, "spacing"); + + lua_pushinteger(L, 0); lua_setfield(L, -2, "DEFAULT"); + lua_setglobal(L, "font"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::mouse::pos); lua_setfield(L, -2, "pos"); + lua_pushcfunction(L,wrappers::mouse::wheel); lua_setfield(L, -2, "wheel"); + lua_pushcfunction(L,wrappers::mouse::down); lua_setfield(L, -2, "down"); + lua_pushcfunction(L,wrappers::mouse::press); lua_setfield(L, -2, "press"); + lua_pushcfunction(L,wrappers::mouse::dblclick); lua_setfield(L, -2, "dblclick"); + lua_pushcfunction(L,wrappers::mouse::discard); lua_setfield(L, -2, "discard"); + lua_pushcfunction(L,wrappers::mouse::inside); lua_setfield(L, -2, "inside"); + + lua_pushinteger(L, 1); lua_setfield(L, -2, "LEFT"); + lua_pushinteger(L, 2); lua_setfield(L, -2, "MIDDLE"); + lua_pushinteger(L, 3); lua_setfield(L, -2, "RIGHT"); + lua_setglobal(L, "mouse"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::key::down); lua_setfield(L, -2, "down"); + lua_pushcfunction(L,wrappers::key::press); lua_setfield(L, -2, "press"); + lua_pushcfunction(L,wrappers::key::any); lua_setfield(L, -2, "any"); + lua_pushcfunction(L,wrappers::key::text); lua_setfield(L, -2, "text"); + lua_pushcfunction(L,wrappers::key::utf8char); lua_setfield(L, -2, "utf8char"); + //lua_setglobal(L, "key"); + + //lua_newtable(L); + lua_pushinteger(L, 0); lua_setfield(L, -2, "UNKNOWN"); + lua_pushinteger(L, 4); lua_setfield(L, -2, "A"); + lua_pushinteger(L, 5); lua_setfield(L, -2, "B"); + lua_pushinteger(L, 6); lua_setfield(L, -2, "C"); + lua_pushinteger(L, 7); lua_setfield(L, -2, "D"); + lua_pushinteger(L, 8); lua_setfield(L, -2, "E"); + lua_pushinteger(L, 9); lua_setfield(L, -2, "F"); + lua_pushinteger(L, 10); lua_setfield(L, -2, "G"); + lua_pushinteger(L, 11); lua_setfield(L, -2, "H"); + lua_pushinteger(L, 12); lua_setfield(L, -2, "I"); + lua_pushinteger(L, 13); lua_setfield(L, -2, "J"); + lua_pushinteger(L, 14); lua_setfield(L, -2, "K"); + lua_pushinteger(L, 15); lua_setfield(L, -2, "L"); + lua_pushinteger(L, 16); lua_setfield(L, -2, "M"); + lua_pushinteger(L, 17); lua_setfield(L, -2, "N"); + lua_pushinteger(L, 18); lua_setfield(L, -2, "O"); + lua_pushinteger(L, 19); lua_setfield(L, -2, "P"); + lua_pushinteger(L, 20); lua_setfield(L, -2, "Q"); + lua_pushinteger(L, 21); lua_setfield(L, -2, "R"); + lua_pushinteger(L, 22); lua_setfield(L, -2, "S"); + lua_pushinteger(L, 23); lua_setfield(L, -2, "T"); + lua_pushinteger(L, 24); lua_setfield(L, -2, "U"); + lua_pushinteger(L, 25); lua_setfield(L, -2, "V"); + lua_pushinteger(L, 26); lua_setfield(L, -2, "W"); + lua_pushinteger(L, 27); lua_setfield(L, -2, "X"); + lua_pushinteger(L, 28); lua_setfield(L, -2, "Y"); + lua_pushinteger(L, 29); lua_setfield(L, -2, "Z"); + lua_pushinteger(L, 30); lua_setfield(L, -2, "N1"); + lua_pushinteger(L, 31); lua_setfield(L, -2, "N2"); + lua_pushinteger(L, 32); lua_setfield(L, -2, "N3"); + lua_pushinteger(L, 33); lua_setfield(L, -2, "N4"); + lua_pushinteger(L, 34); lua_setfield(L, -2, "N5"); + lua_pushinteger(L, 35); lua_setfield(L, -2, "N6"); + lua_pushinteger(L, 36); lua_setfield(L, -2, "N7"); + lua_pushinteger(L, 37); lua_setfield(L, -2, "N8"); + lua_pushinteger(L, 38); lua_setfield(L, -2, "N9"); + lua_pushinteger(L, 39); lua_setfield(L, -2, "N0"); + lua_pushinteger(L, 40); lua_setfield(L, -2, "RETURN"); + lua_pushinteger(L, 41); lua_setfield(L, -2, "ESCAPE"); + lua_pushinteger(L, 42); lua_setfield(L, -2, "BACKSPACE"); + lua_pushinteger(L, 43); lua_setfield(L, -2, "TAB"); + lua_pushinteger(L, 44); lua_setfield(L, -2, "SPACE"); + lua_pushinteger(L, 45); lua_setfield(L, -2, "MINUS"); + lua_pushinteger(L, 46); lua_setfield(L, -2, "EQUALS"); + lua_pushinteger(L, 47); lua_setfield(L, -2, "LEFTBRACKET"); + lua_pushinteger(L, 48); lua_setfield(L, -2, "RIGHTBRACKET"); + lua_pushinteger(L, 49); lua_setfield(L, -2, "BACKSLASH"); + lua_pushinteger(L, 50); lua_setfield(L, -2, "NONUSHASH"); + lua_pushinteger(L, 51); lua_setfield(L, -2, "SEMICOLON"); + lua_pushinteger(L, 52); lua_setfield(L, -2, "APOSTROPHE"); + lua_pushinteger(L, 53); lua_setfield(L, -2, "GRAVE"); + lua_pushinteger(L, 54); lua_setfield(L, -2, "COMMA"); + lua_pushinteger(L, 55); lua_setfield(L, -2, "PERIOD"); + lua_pushinteger(L, 56); lua_setfield(L, -2, "SLASH"); + lua_pushinteger(L, 57); lua_setfield(L, -2, "CAPSLOCK"); + lua_pushinteger(L, 58); lua_setfield(L, -2, "F1"); + lua_pushinteger(L, 59); lua_setfield(L, -2, "F2"); + lua_pushinteger(L, 60); lua_setfield(L, -2, "F3"); + lua_pushinteger(L, 61); lua_setfield(L, -2, "F4"); + lua_pushinteger(L, 62); lua_setfield(L, -2, "F5"); + lua_pushinteger(L, 63); lua_setfield(L, -2, "F6"); + lua_pushinteger(L, 64); lua_setfield(L, -2, "F7"); + lua_pushinteger(L, 65); lua_setfield(L, -2, "F8"); + lua_pushinteger(L, 66); lua_setfield(L, -2, "F9"); + lua_pushinteger(L, 67); lua_setfield(L, -2, "F10"); + lua_pushinteger(L, 68); lua_setfield(L, -2, "F11"); + lua_pushinteger(L, 69); lua_setfield(L, -2, "F12"); + lua_pushinteger(L, 70); lua_setfield(L, -2, "PRINTSCREEN"); + lua_pushinteger(L, 71); lua_setfield(L, -2, "SCROLLLOCK"); + lua_pushinteger(L, 72); lua_setfield(L, -2, "PAUSE"); + lua_pushinteger(L, 73); lua_setfield(L, -2, "INSERT"); + lua_pushinteger(L, 74); lua_setfield(L, -2, "HOME"); + lua_pushinteger(L, 75); lua_setfield(L, -2, "PAGEUP"); + lua_pushinteger(L, 76); lua_setfield(L, -2, "DELETE"); + lua_pushinteger(L, 77); lua_setfield(L, -2, "END"); + lua_pushinteger(L, 78); lua_setfield(L, -2, "PAGEDOWN"); + lua_pushinteger(L, 79); lua_setfield(L, -2, "RIGHT"); + lua_pushinteger(L, 80); lua_setfield(L, -2, "LEFT"); + lua_pushinteger(L, 81); lua_setfield(L, -2, "DOWN"); + lua_pushinteger(L, 82); lua_setfield(L, -2, "UP"); + lua_pushinteger(L, 83); lua_setfield(L, -2, "NUMLOCKCLEAR"); + lua_pushinteger(L, 84); lua_setfield(L, -2, "KP_DIVIDE"); + lua_pushinteger(L, 85); lua_setfield(L, -2, "KP_MULTIPLY"); + lua_pushinteger(L, 86); lua_setfield(L, -2, "KP_MINUS"); + lua_pushinteger(L, 87); lua_setfield(L, -2, "KP_PLUS"); + lua_pushinteger(L, 88); lua_setfield(L, -2, "KP_ENTER"); + lua_pushinteger(L, 89); lua_setfield(L, -2, "KP_1"); + lua_pushinteger(L, 90); lua_setfield(L, -2, "KP_2"); + lua_pushinteger(L, 91); lua_setfield(L, -2, "KP_3"); + lua_pushinteger(L, 92); lua_setfield(L, -2, "KP_4"); + lua_pushinteger(L, 93); lua_setfield(L, -2, "KP_5"); + lua_pushinteger(L, 94); lua_setfield(L, -2, "KP_6"); + lua_pushinteger(L, 95); lua_setfield(L, -2, "KP_7"); + lua_pushinteger(L, 96); lua_setfield(L, -2, "KP_8"); + lua_pushinteger(L, 97); lua_setfield(L, -2, "KP_9"); + lua_pushinteger(L, 98); lua_setfield(L, -2, "KP_0"); + lua_pushinteger(L, 99); lua_setfield(L, -2, "KP_PERIOD"); + lua_pushinteger(L, 100); lua_setfield(L, -2, "NONUSBACKSLASH"); + lua_pushinteger(L, 101); lua_setfield(L, -2, "APPLICATION"); + lua_pushinteger(L, 224); lua_setfield(L, -2, "LCTRL"); + lua_pushinteger(L, 225); lua_setfield(L, -2, "LSHIFT"); + lua_pushinteger(L, 226); lua_setfield(L, -2, "LALT"); + lua_pushinteger(L, 227); lua_setfield(L, -2, "LGUI"); + lua_pushinteger(L, 228); lua_setfield(L, -2, "RCTRL"); + lua_pushinteger(L, 229); lua_setfield(L, -2, "RSHIFT"); + lua_pushinteger(L, 230); lua_setfield(L, -2, "RALT"); + lua_pushinteger(L, 231); lua_setfield(L, -2, "RGUI"); + + lua_setglobal(L, "key"); + + lua_newtable(L); + lua_pushcfunction(L,wrappers::pad::down); lua_setfield(L, -2, "down"); + lua_pushcfunction(L,wrappers::pad::press); lua_setfield(L, -2, "press"); + lua_pushcfunction(L,wrappers::pad::any); lua_setfield(L, -2, "any"); + //lua_setglobal(L, "gamepad"); + + //lua_newtable(L); + lua_pushinteger(L, -1); lua_setfield(L, -2, "INVALID"); + lua_pushinteger(L, 0); lua_setfield(L, -2, "A"); + lua_pushinteger(L, 1); lua_setfield(L, -2, "B"); + lua_pushinteger(L, 2); lua_setfield(L, -2, "X"); + lua_pushinteger(L, 3); lua_setfield(L, -2, "Y"); + lua_pushinteger(L, 4); lua_setfield(L, -2, "BACK"); + lua_pushinteger(L, 5); lua_setfield(L, -2, "GUIDE"); + lua_pushinteger(L, 6); lua_setfield(L, -2, "START"); + lua_pushinteger(L, 7); lua_setfield(L, -2, "LEFTSTICK"); + lua_pushinteger(L, 8); lua_setfield(L, -2, "RIGHTSTICK"); + lua_pushinteger(L, 9); lua_setfield(L, -2, "LEFTSHOULDER"); + lua_pushinteger(L, 10); lua_setfield(L, -2, "RIGHTSHOULDER"); + lua_pushinteger(L, 11); lua_setfield(L, -2, "UP"); + lua_pushinteger(L, 12); lua_setfield(L, -2, "DOWN"); + lua_pushinteger(L, 13); lua_setfield(L, -2, "LEFT"); + lua_pushinteger(L, 14); lua_setfield(L, -2, "RIGHT"); + lua_pushinteger(L, 15); lua_setfield(L, -2, "MISC1"); + lua_pushinteger(L, 16); lua_setfield(L, -2, "PADDLE1"); + lua_pushinteger(L, 17); lua_setfield(L, -2, "PADDLE2"); + lua_pushinteger(L, 18); lua_setfield(L, -2, "PADDLE3"); + lua_pushinteger(L, 19); lua_setfield(L, -2, "PADDLE4"); + lua_pushinteger(L, 20); lua_setfield(L, -2, "TOUCHPAD"); + + lua_setglobal(L, "pad"); + + + lua_getglobal(L, "utf8"); // push utf8 table + lua_pushcfunction(L, wrappers::utf8_sub); // push C function + lua_setfield(L, -2, "sub"); // utf8.sub = cpp_utf8_sub + lua_pop(L, 1); // pop utf8 table + } + } +} diff --git a/source/mini/lua/lua.wrappers.h b/source/mini/lua/lua.wrappers.h new file mode 100644 index 0000000..abf91e7 --- /dev/null +++ b/source/mini/lua/lua.wrappers.h @@ -0,0 +1,11 @@ +#pragma once + +struct lua_State; + +namespace mini +{ + namespace lua + { + void push_functions(lua_State *L); + } +} diff --git a/source/mini/main.cpp b/source/mini/main.cpp new file mode 100644 index 0000000..3d189bc --- /dev/null +++ b/source/mini/main.cpp @@ -0,0 +1,31 @@ +#include "mini.h" +#include "win/win.h" +#include "view/view.h" + +uint16_t ants = 0xc936; +float t = 0; + +using namespace mini; + +void exception_loop() { + //if (t==0) t= time(); + if (sys::time()-t > 0.25) { + t = sys::time(); + surf::clip::reset(); + view::origin::set(0,0); + surf::target::set(0); + pal::trans::set(255); + pal::color::set(0,0xffffff00); + pal::color::set(1,0xff000000); + const int w=win::res::getw(); + const int h=win::res::geth(); + mini::draw::mode::set(0); + mini::draw::rect(0,0,w,h,1); + mini::draw::rect(1,1,w-2,h-2,1); + mini::draw::mode::set(1); + draw::pattern::set(ants); + mini::draw::rect(0,0,w,h,0); + mini::draw::rect(1,1,w-2,h-2,0); + ants = (ants<<12) | (ants>>4); + } +} \ No newline at end of file diff --git a/source/mini/mini.cpp b/source/mini/mini.cpp new file mode 100644 index 0000000..2350803 --- /dev/null +++ b/source/mini/mini.cpp @@ -0,0 +1,586 @@ +#include "mini.h" +#include "version.h" + +#include "surf/surf.h" +#include "font/font.h" +#include "draw/draw.h" +#include "win/win.h" +#include "view/view.h" +#include "shader/shader.h" +#include "audio/audio.h" +#include "mouse/mouse.h" +#include "key/key.h" +#include "pad/pad.h" + +#include "file/file.h" +#include "lua/lua.h" +#include "aux/log.h" +#include "aux/default_font_gif.h" +#include "aux/default_font_fnt.h" + +#include + +#include + + + + +#define UPDATE_ALWAYS 0 +#define UPDATE_WAIT 1 +#define UPDATE_TIMEOUT 2 + +#ifdef MACOS_BUNDLE +#include +#endif + + + +int fps=0; + +char config_folder[256]; + + + +uint8_t tile_width = 8; +uint8_t tile_height = 8; + + +char main_lua_file[200] = "main.lua"; +bool override_ini = false; + + +int update_mode = UPDATE_ALWAYS; +int timeout = 0; +uint32_t last_update = 0; +float delta_time = 0.0f; + +bool should_exit = false; +bool should_quit = false; + +Uint32 *pixels; +int pitch; + +uint32_t palette[256] = { 0xFF1a1c2c, 0xFF5d275d, 0xFFb13e53, 0xFFef7d57, 0xFFffcd75, 0xFFa7f070, 0xFF38b764, 0xFF257179, + 0xFF29366f, 0xFF3b5dc9, 0xFF41a6f6, 0xFF73eff7, 0xFFf4f4f4, 0xFF94b0c2, 0xFF566c86, 0xFF333c57 }; + + +surf::surface_t* map_surface = NULL; + + +int16_t beats, num_beats = 0; + +void createNewProject(); + +char* get_value_from_line(char* line) { + char* equal_character = strchr(line, '='); + if (equal_character == NULL) return NULL; + *equal_character = '\0'; + return ++equal_character; +} + +void read_ini() { + int size; + FILE *f = mini::file::getfilepointer("game.ini", size); // fopen("game.ini", "r"); + char line[1024]; + if (f == NULL) { log_msg(LOG_FAIL, "No s'ha pogut obrir 'game.ini'\n"); exit(-1); } + log_msg(LOG_OK, "'game.ini' carregat\n"); + while (fgets(line, sizeof(line), f)) { + char *value = get_value_from_line(line); + if (value != NULL) { + value[strlen(value)-1] = '\0'; + if (strcmp(line, "title") == 0) { strcpy(mini::win::state.title, value); log_msg(LOG_VERBOSE, "title = %s\n", mini::win::state.title); } + else if (strcmp(line, "config") == 0) { strcpy(config_folder, value); log_msg(LOG_VERBOSE, "config = %s\n", config_folder); } + else if (strcmp(line, "width") == 0) { mini::win::state.width = atoi(value); log_msg(LOG_VERBOSE, "screen width = %i\n", mini::win::state.width); } + else if (strcmp(line, "height") == 0) { mini::win::state.height = atoi(value); log_msg(LOG_VERBOSE, "screen height = %i\n", mini::win::state.height); } + else if (strcmp(line, "zoom") == 0) { mini::win::state.zoom = atoi(value); log_msg(LOG_VERBOSE, "screen zoom = %i\n", mini::win::state.zoom); } + else if (strcmp(line, "fullscreen") == 0) { mini::win::state.fullscreen = atoi(value); log_msg(LOG_VERBOSE, "screen sullscreen = %i\n", mini::win::state.fullscreen); } + //else if (strcmp(line, "files") == 0) { + //lua_files = (char*)malloc(strlen(value)); + // strcpy(lua_files, value); + //} + } + } + fclose(f); + //SDL_Log("'game.ini' carregat!\n"); +} + +#define TILES(x, y) map_surface->p[x+y*map_surface->w] +#define CURRENT(x, y) surfaces[i].p[(x)+(y)*surfaces[i].w] + +namespace mini +{ + + namespace map + { + namespace surf { + void set(uint8_t surface) { + map_surface = &mini::surf::state.surfaces[surface]; + } + + uint8_t get() + { + for (unsigned int i=0; iw / tw; + + // Coordenadas del tile dentro del spritesheet + int tx = (n % tiles_per_row) * tw; + int ty = (n / tiles_per_row) * th; + + int src_y = ty; + + for (int yi = 0; yi < th; ++yi) { + int src_x = tx; + + for (int xi = 0; xi < tw; ++xi) { + uint8_t c = mini::draw::pixel::get(src_x, src_y); + mini::draw::pixel::subst_pset(x + xi, y + yi, c); + src_x++; + } + + src_y++; + } + } + + void draw() { + if (!map_surface || !mini::surf::state.source_surface) return; + + const int tw = tile_width; + const int th = tile_height; + + int celw = map_surface->w; + int celh = map_surface->h; + + int celx = 0; + int cely = 0; + + int ox = view::state.origin[0]; + int oy = view::state.origin[1]; + + int sx = ox; + int sy = oy; + + // Clipping global rápido + if (sx + celw * tw < mini::surf::state.dest_surface->clip[0] || + sy + celh * th < mini::surf::state.dest_surface->clip[1] || + sx > mini::surf::state.dest_surface->clip[2] || + sy > mini::surf::state.dest_surface->clip[3]) + return; + + // Recorte por izquierda + if (sx < mini::surf::state.dest_surface->clip[0]) { + int diff = (mini::surf::state.dest_surface->clip[0] - sx) / tw; + celx += diff; + celw -= diff; + sx += diff * tw; + } + + // Recorte por arriba + if (sy < mini::surf::state.dest_surface->clip[1]) { + int diff = (mini::surf::state.dest_surface->clip[1] - sy) / th; + cely += diff; + celh -= diff; + sy += diff * th; + } + + // Recorte por derecha + int max_x = mini::surf::state.dest_surface->clip[2] - sx; + if (max_x < celw * tw) celw = max_x / tw + 1; + + // Recorte por abajo + int max_y = mini::surf::state.dest_surface->clip[3] - sy; + if (max_y < celh * th) celh = max_y / th + 1; + + // Ahora sx, sy son relativos al origen y ya están recortados + for (int y = 0; y < celh; ++y) { + int ty = sy + y * th; + for (int x = 0; x < celw; ++x) { + uint8_t tile = tile::get(celx + x, cely + y); + if (!tile) continue; + int tx = sx + x * tw; + tileblit(tile, tx - ox, ty - oy); + } + } + } + + namespace tile { + uint8_t get(int celx, int cely) { + if (!map_surface) return 0; + if (celx < 0 || celx > (map_surface->w-1) || cely < 0 || cely > (map_surface->h-1)) return 0; + return TILES(celx, cely); + } + + void set(int celx, int cely, uint8_t snum) { + if (!map_surface) return; + if (celx < 0 || celx > (map_surface->w-1) || cely < 0 || cely > (map_surface->h-1)) return; + TILES(celx, cely) = snum; + } + } + + namespace cell { + uint8_t getw() { return tile_width; } + uint8_t geth() { return tile_height; } + + void set(int w, int h) { + tile_width = w; + tile_height = h; + } + } + + } + + namespace pal + { + uint32_t *load(const char* filename, uint16_t *palsize) { + int size; + uint8_t *buffer = (uint8_t*)file::getfilebuffer(filename, size); + // [TODO] + uint32_t *pal = nullptr; //LoadPalette(buffer, palsize); + free(buffer); + return pal; + } + + void set(uint32_t *pal) { + for (int i=0; i<256; ++i) palette[i] = pal[i] | 0xff000000; + } + + namespace color { + void set(uint8_t index, uint32_t color) { + palette[index] = color | 0xff000000; + } + + uint32_t get(uint8_t index) { + return palette[index]; + } + } + + namespace trans { + void set(uint8_t index) { + ds::trans = index; + } + + uint8_t get() { + return ds::trans; + } + } + + namespace subpal { + uint8_t set(uint8_t index, uint8_t color) { + const uint8_t old = ds::draw_palette[SDL_clamp(index,0,255)]; + ds::draw_palette[SDL_clamp(index,0,255)] = SDL_clamp(color,0,255); + return old; + } + + void reset() { + for (int i=0;i<256;++i) ds::draw_palette[i]=i; + } + } + } + + + + namespace sys + { + float delta() { + return delta_time; + } + + float time() { + return float(SDL_GetTicks())/1000.0f; + } + + bool beat(int16_t i) { + if (i<0) { + return beats==0; + } else { + beats=num_beats=i; + return false; + } + } + + int getfps() { + return fps; + } + + namespace update { + void set(const int value, const int t) { + update_mode = value; + timeout = t; + } + int get() { + return update_mode; + } + } + + void exit() { + should_exit = true; + should_quit = true; + } + } + + +} + + + +void reinit() { + log_msg(LOG_INFO, "STARTING A SYSTEM REINITIALIZATION\n"); + + // Reset view state + mini::view::init(); + + // Reset draw state + ds::mode = DRAWMODE_NORMAL; + ds::fill_pattern = 0b1111111111111111; + + // Reset surf state + for (unsigned int i=0; i1) + { + if (argv[1][0]=='-' && argv[1][1]=='-') { + const char *command = &argv[1][2]; + if (strcmp(command, "new")==0) { + createNewProject(); + exit(0); + } else if (strcmp(command, "version")==0) { + exit(0); + } + } else if (strstr(argv[1], ".lua")!=nullptr) { + file::setresourcefolder("./"); + file::setsource(SOURCE_FOLDER); + strcpy(main_lua_file, argv[1]); + strcpy(mini::win::state.title, argv[1]); + override_ini = true; + } else if (strstr(argv[1], ".jf2")!=nullptr) { + file::setresourcefilename(argv[1]); + file::setsource(SOURCE_FILE); + } else { + char path[256] = "./"; + strcat(path, argv[1]); + file::setresourcefolder(path); + } + } + #ifdef MACOS_BUNDLE + char res_file[255] = ""; + strcpy(res_file, dirname(argv[0])); + strcat(res_file, "/../Resources/data.jrf"); + file::setresourcefilename(res_file); + #endif + + while (!should_quit) { + should_exit=false; + + // READ INI + if (!override_ini) + { + read_ini(); + file::setconfigfolder(config_folder); + const char *zoom = file::getconfigvalue("zoom"); + if (zoom) win::state.zoom=atoi(zoom); + const char *fullscreen = file::getconfigvalue("fullscreen"); + if (fullscreen) win::state.fullscreen=strcmp(fullscreen, "true")==0?true:false; + const char *cursor = file::getconfigvalue("cursor"); + if (cursor) win::state.cursor=strcmp(cursor, "true")?true:false; + const char *music_enabled = file::getconfigvalue("music"); + if (music_enabled) audio::music::enable::set(strcmp(music_enabled, "true")==0?true:false); + const char *sound_enabled = file::getconfigvalue("sound"); + if (sound_enabled) audio::sound::enable::set(strcmp(sound_enabled, "true")==0?true:false); + } + + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD); + + win::init(); + pad::init(); + audio::init(); + + reinit(); + + uint8_t font_surf = surf::load(default_font_gif, "default_font"); + //surfaces[font_surf].flags |= SURF_GENERATED; + font::load_from_buffer((const char*)default_font_fnt, 0, "default_font"); + current_font = &fonts[0]; + + lua::init(main_lua_file); + lua::callbacks::init(); + + Uint32 dt=SDL_GetTicks(); + + key::state.just_pressed = 0; + key::state.has_text_input = false; + pad::state.just_pressed = SDL_GAMEPAD_BUTTON_INVALID; + mouse::state.just_pressed = 0; + mouse::state.wheel = 0; + mouse::state.double_click = false; + + int fps_counter=0; + uint32_t fps_timer=0; + + while(!should_exit) { + if (update_mode==UPDATE_WAIT) SDL_WaitEvent(NULL); + else if (update_mode==UPDATE_TIMEOUT) SDL_WaitEventTimeout(NULL, timeout); + SDL_Event mini_eve; + while(SDL_PollEvent(&mini_eve)) { + if (mini_eve.type == SDL_EVENT_QUIT) { should_exit=true; should_quit=true; break; } + if (mini_eve.type == SDL_EVENT_TEXT_INPUT) { + SDL_strlcpy(key::state.text_input_buffer, mini_eve.text.text, sizeof(key::state.text_input_buffer)); + key::state.has_text_input = true; + } + if (mini_eve.type == SDL_EVENT_KEY_DOWN) { +#ifdef DEBUG + if (mini_eve.key.scancode == SDL_SCANCODE_F12) { + mini::surf::reloadsurfs(); + //} else if (mini_eve.key.scancode == SDL_SCANCODE_F11) { + // mini::lua::debug::toggle(); + } else if (mini_eve.key.scancode == SDL_SCANCODE_F5) { + should_exit=true; + } else { + key::state.just_pressed = mini_eve.key.scancode; + } +#else + key::state.just_pressed = mini_eve.key.scancode; +#endif + } + if (mini_eve.type == SDL_EVENT_MOUSE_BUTTON_UP) { + if (mouse::state.discard) + mouse::state.discard = false; + else + if (mini_eve.button.clicks==2 && mini_eve.button.button==SDL_BUTTON_LEFT) { + mouse::state.double_click = true; + } else { + mouse::state.just_pressed = mini_eve.button.button; + } + } + if (mini_eve.type == SDL_EVENT_MOUSE_WHEEL) { + mouse::state.wheel = mini_eve.wheel.y; + } + if (mini_eve.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { + pad::state.just_pressed = mini_eve.gbutton.button; + } + /*if ( (mini_eve.type == SDL_WINDOWEVENT) && + (mini_eve.window.windowID == windowID) && + (mini_eve.window.event == SDL_WINDOWEVENT_MOVED) ) { + windowpos_x = mini_eve.window.data1-4; + windowpos_y = mini_eve.window.data2-36; + //SDL_GetWindowPosition(mini_win, &windowpos_x, &windowpos_y); + }*/ + } + key::state.keys = SDL_GetKeyboardState(NULL); + float real_mouse_x, real_mouse_y; + mouse::state.buttons = SDL_GetMouseState(&real_mouse_x, &real_mouse_y); + float mx, my; + SDL_RenderCoordinatesFromWindow(win::state.renderer, real_mouse_x, real_mouse_y, &mx, &my); + mouse::state.x = int(mx/win::state.zoom)-view::state.origin[0]; + mouse::state.y = int(my/win::state.zoom)-view::state.origin[1]; + + if (SDL_GetTicks()-dt>13) { + dt = SDL_GetTicks(); + + if (lua::running()) { + delta_time = float(SDL_GetTicks() - last_update)/1000.0f; + last_update = SDL_GetTicks(); + + lua::callbacks::update(); + } else { + exception_loop(); + } + if (beats==0)beats=num_beats; + if (beats>0)beats--; + key::state.just_pressed = 0; + mouse::state.just_pressed = 0; + key::state.has_text_input = false; + key::state.text_input_buffer[0] = '\0'; + mouse::state.double_click = false; + mouse::state.wheel = 0; + pad::state.just_pressed = SDL_GAMEPAD_BUTTON_INVALID; + } + SDL_SetRenderTarget(win::state.renderer, win::state.tex_shader); + SDL_SetRenderDrawColor(win::state.renderer, 0, 0, 0, 255); + SDL_RenderClear(win::state.renderer); + SDL_LockTexture(win::state.tex_back, NULL, (void**)&pixels, &pitch); + for (uint32_t i=0;isize;++i) pixels[i] = palette[surf::state.screen_surface->p[i]]; + SDL_UnlockTexture(win::state.tex_back); + SDL_RenderTexture(win::state.renderer, win::state.tex_back, NULL, NULL); //NEW + + shader::render(); + //SDL_RenderTexture(mini_ren, mini_bak, NULL, NULL); + //SDL_RenderPresent(mini_ren); + + fps_counter++; + if (SDL_GetTicks()>=(fps_timer+1000)) { + fps = fps_counter; + fps_counter=0; + fps_timer = SDL_GetTicks(); + } + } + lua::quit(); + audio::quit(); + + //Mix_Quit(); + + for (unsigned int i=0;i + +void exception_loop(); + + +namespace mini +{ + namespace map + { + namespace surf { + void set(uint8_t surface); + uint8_t get(); + } + void draw(); //int celx, int cely, int sx, int sy, uint8_t celw, uint8_t celh, uint8_t layer=0); + + namespace tile { + uint8_t get(int celx, int cely); + void set(int celx, int cely, uint8_t snum); + } + + namespace cell { + uint8_t getw(); + uint8_t geth(); + void set(int w, int h); + } + } + + namespace pal + { + uint32_t *load(const char* filename, uint16_t *palsize=nullptr); + void set(uint32_t *pal); + namespace color { + void set(uint8_t index, uint32_t color); + uint32_t get(uint8_t index); + } + namespace trans { + void set(uint8_t index); + uint8_t get(); + } + namespace subpal { + uint8_t set(uint8_t index, uint8_t color); + void reset(); + } + } + + namespace sys + { + float delta(); + float time(); + bool beat(int16_t i); + + int getfps(); + + namespace update { + void set(const int value, const int t=0); + int get(); + } + + void exit(); + } + + +} diff --git a/source/mini/mouse/mouse.cpp b/source/mini/mouse/mouse.cpp new file mode 100644 index 0000000..83bf86e --- /dev/null +++ b/source/mini/mouse/mouse.cpp @@ -0,0 +1,44 @@ +#include "mouse.h" + +#include + +namespace mini +{ + namespace mouse + { + state_t state; + + int posx() { + return state.x; + } + + int posy() { + return state.y; + } + + int wheel() { + return state.wheel; + } + + bool down(uint8_t i) { + if (state.discard) return false; + return state.buttons & SDL_BUTTON_MASK(i); + } + + bool press(uint8_t i) { + return state.just_pressed == i; + } + + bool dblclick() { + return state.double_click; + } + + void discard() { + state.discard = true; + } + + bool inside(int x, int y, int w, int h) { + return (state.x>=x) && (state.y>=y) && (state.x + +namespace mini +{ + namespace mouse + { + struct state_t { + int x, y, wheel; + uint32_t buttons; + uint8_t just_pressed = 0; + bool double_click = false; + bool discard = false; + }; + extern state_t state; + + int posx(); + int posy(); + int wheel(); + bool down(uint8_t i); + bool press(uint8_t i); + bool dblclick(); + void discard(); + bool inside(int x, int y, int w, int h); + } +} diff --git a/source/mini/pad/pad.cpp b/source/mini/pad/pad.cpp new file mode 100644 index 0000000..beeade6 --- /dev/null +++ b/source/mini/pad/pad.cpp @@ -0,0 +1,48 @@ +#include "pad.h" + +#include "aux/log.h" + +#include + +namespace mini +{ + namespace pad + { + state_t state; + + void init() { + int num_joysticks; + SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks); + if (joysticks) { + for (int i=0; i + +struct SDL_Gamepad; + +namespace mini +{ + namespace pad + { + struct state_t { + SDL_Gamepad *gamepad = nullptr; + int8_t just_pressed = -1; + }; + extern state_t state; + + void init(); + bool down(int8_t i); + bool press(int8_t i); + int press(); + } +} diff --git a/source/mini/shader/shader.cpp b/source/mini/shader/shader.cpp new file mode 100644 index 0000000..0aa348b --- /dev/null +++ b/source/mini/shader/shader.cpp @@ -0,0 +1,267 @@ +#include "shader.h" +#include "mini/file/file.h" +#include "mini/win/win.h" + +#include + +#include + +#ifdef __APPLE__ + #include "CoreFoundation/CoreFoundation.h" + #include + + #if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 + #include + #else + #include + #endif //!ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#else + #include + #include +#endif + +namespace mini +{ + namespace shader + { + SDL_Window *win = nullptr; + SDL_Renderer *renderer = nullptr; + GLuint programId = 0; + SDL_Texture* backBuffer = nullptr; + SDL_Point win_size = {640, 480}; + SDL_FPoint tex_size = {320, 240}; + bool can_use_opengl = false; + bool using_opengl = false; + GLuint texture_number; + GLuint nose; + + #ifndef __APPLE__ + + // I'm avoiding the use of GLEW or some extensions handler, but that + // doesn't mean you should... + PFNGLCREATESHADERPROC glCreateShader; + PFNGLSHADERSOURCEPROC glShaderSource; + PFNGLCOMPILESHADERPROC glCompileShader; + PFNGLGETSHADERIVPROC glGetShaderiv; + PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; + PFNGLDELETESHADERPROC glDeleteShader; + PFNGLATTACHSHADERPROC glAttachShader; + PFNGLCREATEPROGRAMPROC glCreateProgram; + PFNGLDELETEPROGRAMPROC glDeleteProgram; + PFNGLLINKPROGRAMPROC glLinkProgram; + PFNGLVALIDATEPROGRAMPROC glValidateProgram; + PFNGLGETPROGRAMIVPROC glGetProgramiv; + PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; + PFNGLUSEPROGRAMPROC glUseProgram; + PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; + PFNGLACTIVETEXTUREPROC glActiveTexture; + + + bool initGLExtensions() { + glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader"); + glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource"); + glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader"); + glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv"); + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog"); + glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader"); + glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader"); + glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram"); + glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram"); + glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram"); + glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram"); + glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv"); + glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog"); + glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram"); + glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)SDL_GL_GetProcAddress("glGetUniformLocation"); + glActiveTexture = (PFNGLACTIVETEXTUREPROC)SDL_GL_GetProcAddress("glActiveTexture"); + + return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && + glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && + glDeleteProgram && glLinkProgram && glValidateProgram && glGetProgramiv && + glGetProgramInfoLog && glUseProgram && glGetUniformLocation; + } + + #endif + + state_t state; + + void init(const char* vshader, const char* fshader) + { + int filesize; + char *vshaderfile = file::getfilebuffer(vshader, filesize, true); + + char *fshaderfile = nullptr; + if (fshader) { fshaderfile = file::getfilebuffer(fshader, filesize, true); } + init(win::state.window, win::state.tex_shader, vshaderfile, fshaderfile); + } + + + GLuint compileShader(const char* source, GLuint shaderType) { + // Create ID for shader + GLuint result = glCreateShader(shaderType); + // Add define depending on shader type + const char *sources[2] = { shaderType==GL_VERTEX_SHADER?"#define VERTEX\n":"#define FRAGMENT\n", source }; + // Define shader text + glShaderSource(result, 2, sources, NULL); + // Compile shader + glCompileShader(result); + + //Check vertex shader for errors + GLint shaderCompiled = GL_FALSE; + glGetShaderiv( result, GL_COMPILE_STATUS, &shaderCompiled ); + if (shaderCompiled != GL_TRUE) + { + std::cout << "Error en la compilación: " << result << "!" << std::endl; + GLint logLength; + glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) + { + GLchar *log = (GLchar*)malloc(logLength); + glGetShaderInfoLog(result, logLength, &logLength, log); + std::cout << "Shader compile log:" << log << std::endl; + //std::cout << source << std::endl; + free(log); + } + glDeleteShader(result); + result = 0; + } + return result; + } + + GLuint compileProgram(const char* vertexShaderSource, const char* fragmentShaderSource) + { + GLuint programId = 0; + GLuint vtxShaderId, fragShaderId; + + if (programId != 0) glDeleteProgram(programId); + programId = glCreateProgram(); + + + vtxShaderId = compileShader(vertexShaderSource, GL_VERTEX_SHADER); + fragShaderId = compileShader(fragmentShaderSource?fragmentShaderSource:vertexShaderSource, GL_FRAGMENT_SHADER); + + if(vtxShaderId && fragShaderId) + { + // Associate shader with program + glAttachShader(programId, vtxShaderId); + glAttachShader(programId, fragShaderId); + glLinkProgram(programId); + glValidateProgram(programId); + + // Check the status of the compile/link + GLint logLen; + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLen); + if (logLen > 0) + { + char* log = (char*) malloc(logLen * sizeof(char)); + // Show any errors as appropriate + glGetProgramInfoLog(programId, logLen, &logLen, log); + std::cout << "Prog Info Log: " << std::endl << log << std::endl; + free(log); + } + } + if (vtxShaderId) glDeleteShader(vtxShaderId); + if (fragShaderId) glDeleteShader(fragShaderId); + return programId; + } + + const bool init(SDL_Window* win, SDL_Texture* backBuffer, const char* vertexShader, const char* fragmentShader) + { + shader::win = win; + shader::renderer = SDL_GetRenderer(win); + shader::backBuffer = backBuffer; + SDL_GetWindowSize(win, &win_size.x, &win_size.y); + SDL_GetTextureSize(backBuffer, &tex_size.x, &tex_size.y); + //printf("tex size: %fx%f\n", tex_size.x, tex_size.y); + SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer); + texture_number = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, -1); + //printf("texture number: %i\n", texture_number); + int access = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, -1); + nose = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER, -1); + //printf("texture target number: %i\n", nose); + + if (access != SDL_TEXTUREACCESS_TARGET) + { + std::cout << "ERROR FATAL: La textura per al render ha de tindre SDL_TEXTUREACCESS_TARGET definit." << std::endl; + exit(1); + } + + const char * renderer_name = SDL_GetRendererName(renderer); + //printf("rendererInfo.name: %s\n", renderer_name); + + if(!strncmp(renderer_name, "opengl", 6)) { + #ifndef __APPLE__ + static bool gl_extensions_initialized = false; + if (!gl_extensions_initialized) { + if (!initGLExtensions()) { + std::cout << "WARNING: No s'han pogut inicialitzar les extensions d'OpenGL!" << std::endl; + can_use_opengl = false; + return false; + } + gl_extensions_initialized = true; + } + #endif + // Compilar el shader y dejarlo listo para usar. + if (!vertexShader) { + can_use_opengl = false; + return false; + } + programId = compileProgram(vertexShader, fragmentShader); + } else { + std::cout << "WARNING: El driver del renderer no es OpenGL." << std::endl; + can_use_opengl = false; + return false; + } + can_use_opengl = true; + return true; + } + + void enable() { if (can_use_opengl) using_opengl = true; } + void disable() { using_opengl = false; } + + void render() + { + SDL_FlushRenderer(renderer); + SDL_SetRenderTarget(renderer, NULL); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_FlushRenderer(renderer); + + if (using_opengl) + { + GLint oldProgramId; + if (programId != 0) + { + glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId); + glUseProgram(programId); + } + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 1); + glViewport(0, 0, win_size.x, win_size.y); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-1.0f, -1.0f); + glTexCoord2f(tex_size.x, 0.0f); + glVertex2f(1.0f, -1.0f); + glTexCoord2f(0.0f, tex_size.y); + glVertex2f(-1.0f, 1.0f); + glTexCoord2f(tex_size.x, tex_size.y); + glVertex2f(1.0f, 1.0f); + glEnd(); + + SDL_GL_SwapWindow(win); + + if (programId != 0) glUseProgram(oldProgramId); + + } else { + SDL_RenderTexture(renderer, backBuffer, NULL, NULL); + SDL_RenderPresent(renderer); + } + if (glGetError()) { printf("GLERROR!\n"); exit(1); } + } + + } +} diff --git a/source/mini/shader/shader.h b/source/mini/shader/shader.h new file mode 100644 index 0000000..2453de9 --- /dev/null +++ b/source/mini/shader/shader.h @@ -0,0 +1,27 @@ +#pragma once +#include + +struct SDL_Window; +struct SDL_Texture; + +namespace mini +{ + namespace shader + { + struct state_t { + const bool *keys; + uint8_t just_pressed = 0; + char text_input_buffer[10]; + bool has_text_input = false; + }; + extern state_t state; + + const bool init(SDL_Window* win, SDL_Texture* backBuffer, const char* vertexShader, const char* fragmentShader=nullptr); + void init(const char* vshader, const char* fshader); + + void enable(); + void disable(); + + void render(); + } +} diff --git a/source/mini/surf/surf.cpp b/source/mini/surf/surf.cpp new file mode 100644 index 0000000..5e4c9df --- /dev/null +++ b/source/mini/surf/surf.cpp @@ -0,0 +1,203 @@ +#include "surf.h" + +#include "aux/gif.h" +#include "aux/gifenc.h" +#include "aux/log.h" +#include "file/file.h" +#include "draw/draw.h" + +#include + +#include + +namespace mini +{ + namespace surf + { + + state_t state; + + uint8_t create(int w, int h) { + unsigned int i = 0; + while (ip, col, state.dest_surface->size); + } + + namespace target { + void set(uint8_t surface) { + state.dest_surface = &state.surfaces[surface]; + surf::clip::recalculate(); + } + uint8_t get() { + for (unsigned int i=0; iclip[0]=x; + state.dest_surface->clip[1]=y; + state.dest_surface->clip[2]=w-x-1; + state.dest_surface->clip[3]=h-y-1; + recalculate(); + } + void reset(int surface) { + surface_t *s = surface==-1 ? state.dest_surface : &state.surfaces[surface]; + s->clip[0]=0; + s->clip[1]=0; + s->clip[2]=s->w-1; + s->clip[3]=s->h-1; + //recalculate(); + } + void recalculate() { + if (state.dest_surface->clip[0] < 0) state.dest_surface->clip[0] = 0; + if (state.dest_surface->clip[1] < 0) state.dest_surface->clip[1] = 0; + if (state.dest_surface->clip[2] >= state.dest_surface->w) state.dest_surface->clip[2] = state.dest_surface->w-1; + if (state.dest_surface->clip[3] >= state.dest_surface->h) state.dest_surface->clip[3] = state.dest_surface->h-1; + } + } + + } +} \ No newline at end of file diff --git a/source/mini/surf/surf.h b/source/mini/surf/surf.h new file mode 100644 index 0000000..fb85b77 --- /dev/null +++ b/source/mini/surf/surf.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +namespace mini +{ + namespace surf + { + #define MAX_SURFACES 100 + + #define SURF_NOTHING 0 + #define SURF_EXTERNAL 1 + #define SURF_GENERATED 2 + + struct surface_t { + char* name {nullptr}; + uint8_t* p {nullptr}; + uint16_t w, h; + uint32_t size; + int clip[4] {0, 0, 0, 0}; + uint8_t flags {SURF_NOTHING}; + }; + + struct state_t { + surface_t surfaces[MAX_SURFACES]; + surface_t* screen_surface = &surfaces[0]; + surface_t* dest_surface = screen_surface; + surface_t* source_surface = NULL; + }; + extern state_t state; + + uint8_t create(int w, int h); + uint8_t load(const char* filename, const bool external = false); + void save(uint8_t surface, const char* filename, uint8_t *pal, uint8_t colors=0); + void destroy(uint8_t surface); + int width(uint8_t surface); + int height(uint8_t surface); + void cls(uint8_t color=0); + + namespace target { + void set(uint8_t surface); + uint8_t get(); + } + + namespace source { + void set(uint8_t surface); + uint8_t get(); + } + + namespace clip { + void set(int x, int y, int w, int h); + void reset(int surface=-1); + void recalculate(); + } + } +} \ No newline at end of file diff --git a/source/mini/version.h b/source/mini/version.h new file mode 100644 index 0000000..d2cbf1c --- /dev/null +++ b/source/mini/version.h @@ -0,0 +1,3 @@ +#pragma once + +#define MINI_VERSION "1.4.10" diff --git a/source/mini/view/view.cpp b/source/mini/view/view.cpp new file mode 100644 index 0000000..346483c --- /dev/null +++ b/source/mini/view/view.cpp @@ -0,0 +1,28 @@ +#include "view.h" + +namespace mini +{ + namespace view + { + state_t state; + + void init(){ + state.origin[0] = state.origin[1] = 0; + } + + namespace origin { + void set(int x, int y) { + state.origin[0] = x; + state.origin[1] = y; + } + + int getx() { + return state.origin[0]; + } + + int gety() { + return state.origin[1]; + } + } + } +} \ No newline at end of file diff --git a/source/mini/view/view.h b/source/mini/view/view.h new file mode 100644 index 0000000..ab05892 --- /dev/null +++ b/source/mini/view/view.h @@ -0,0 +1,20 @@ +#pragma once + +namespace mini +{ + namespace view + { + struct state_t { + int origin[2] {0, 0}; + }; + extern state_t state; + + void init(); + + namespace origin { + void set(int x, int y); + int getx(); + int gety(); + } + } +} \ No newline at end of file diff --git a/source/mini/win/win.cpp b/source/mini/win/win.cpp new file mode 100644 index 0000000..9ee41a6 --- /dev/null +++ b/source/mini/win/win.cpp @@ -0,0 +1,105 @@ +#include "win.h" +#include "aux/log.h" +#include "mini/file/file.h" +#include "mini/shader/shader.h" + +#include + +namespace mini +{ + namespace win + { + state_t state; + + void init() { + // Ajustar el zoom a un valor vàlid + if (state.zoom <= 0) state.zoom = 1; + const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay()); + while (state.width*state.zoom > dm->w || state.height*state.zoom > dm->h) state.zoom--; + + // Crear SDL_Window i SDL_Renderer + state.window = SDL_CreateWindow(state.title, state.width*state.zoom, state.height*state.zoom, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE|(state.fullscreen?SDL_WINDOW_FULLSCREEN:0)); + //windowID = SDL_GetWindowID(mini_win); + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); + state.renderer = SDL_CreateRenderer(state.window, NULL); + + // Mostrar o ocultar el cursor + if (state.cursor) SDL_ShowCursor(); else SDL_HideCursor(); + + // Crear textura backbuffer + state.tex_back = SDL_CreateTexture(state.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, state.width, state.height); + SDL_SetTextureScaleMode(state.tex_back, SDL_SCALEMODE_NEAREST); + + //SDL_PropertiesID props = SDL_GetTextureProperties(state.tex_back); + //int real_pixelformat = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_FORMAT_NUMBER, -1); + //if (real_pixelformat != SDL_PIXELFORMAT_ARGB8888) { + // log_msg(LOG_FAIL, "Pixelformat incorrecte: %i\n", real_pixelformat); + // exit(1); + //} + + // Crear textura shaders i inicialitzar shaders + state.tex_shader = SDL_CreateTexture(state.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, state.width*state.zoom, state.height*state.zoom); + SDL_SetTextureScaleMode(state.tex_shader, SDL_SCALEMODE_NEAREST); + shader::init(state.window, state.tex_shader, nullptr); + + log_msg(LOG_OK, "Graphics subsystem initialized\n"); + } + + void quit() { + SDL_DestroyTexture(state.tex_shader); + SDL_DestroyTexture(state.tex_back); + SDL_DestroyRenderer(state.renderer); + SDL_DestroyWindow(state.window); + } + + void raise() { + SDL_RaiseWindow(state.window); + } + + namespace zoom { + int get() { + return state.zoom; + } + void set(const int value) { + state.zoom = value; + quit(); + init(); + char strzoom[3]; + file::setconfigvalue("zoom", SDL_itoa(state.zoom, strzoom, 10)); + } + } + + namespace fullscreen { + bool get() { + return state.fullscreen; + } + void set(const bool value) { + state.fullscreen=value; + quit(); + init(); + file::setconfigvalue("fullscreen", state.fullscreen?"true":"false"); + } + } + + namespace cursor { + bool get() { + return state.cursor; + } + void set(const bool value) { + state.cursor=value; + if (state.cursor) SDL_ShowCursor(); else SDL_HideCursor(); + } + } + + namespace res { + void set(const int w, const int h) { + state.width = w; + state.height = h; + quit(); + init(); + } + int getw() { return state.width; } + int geth() { return state.height; } + } + } +} \ No newline at end of file diff --git a/source/mini/win/win.h b/source/mini/win/win.h new file mode 100644 index 0000000..34a44f5 --- /dev/null +++ b/source/mini/win/win.h @@ -0,0 +1,53 @@ +#pragma once +#include + +struct SDL_Window; +struct SDL_Renderer; +struct SDL_Texture; + +namespace mini +{ + namespace win + { + struct state_t { + char title[256]; + uint16_t width { 160 }; + uint16_t height { 120 }; + uint8_t zoom { 4 }; + bool fullscreen { false }; + bool cursor { true }; + + SDL_Window *window { nullptr }; + SDL_Renderer*renderer { nullptr }; + SDL_Texture *tex_back { nullptr }; + SDL_Texture *tex_shader { nullptr }; + //Uint32 windowID; + }; + extern state_t state; + + void init(); + void quit(); + void raise(); + + namespace zoom { + void set(const int value); + int get(); + } + + namespace fullscreen { + void set(const bool value); + bool get(); + } + + namespace cursor { + void set(const bool value); + bool get(); + } + + namespace res { + void set(const int w, const int h); + int getw(); + int geth(); + } + } +}