- [WIP] Puta que susto, una regla mal feta en el .gitignore i ja no comitaba res
- Canvi de comp
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
mini.exe
|
/mini.exe
|
||||||
mini
|
/mini
|
||||||
mini_debug.exe
|
mini_debug.exe
|
||||||
mini_debug
|
mini_debug
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|||||||
115
source/mini/audio/audio.cpp
Normal file
115
source/mini/audio/audio.cpp
Normal file
@@ -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<MAX_SOUNDS;++i) sounds[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void quit() {
|
||||||
|
if (current_music != NULL) JA_DeleteMusic(current_music);
|
||||||
|
for (int i=0;i<MAX_SOUNDS;++i) if (sounds[i]!=NULL) JA_DeleteSound(sounds[i]);
|
||||||
|
JA_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace music
|
||||||
|
{
|
||||||
|
void play(const char *filename, const int loop) {
|
||||||
|
int size;
|
||||||
|
char *buffer = file::getfilebuffer(filename, size);
|
||||||
|
if (current_music != NULL) JA_DeleteMusic(current_music);
|
||||||
|
current_music = JA_LoadMusic((Uint8*)buffer, size);
|
||||||
|
JA_PlayMusic(current_music, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pause() {
|
||||||
|
JA_PauseMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume() {
|
||||||
|
JA_ResumeMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop(const int t) {
|
||||||
|
JA_StopMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pos {
|
||||||
|
void set(float value)
|
||||||
|
{
|
||||||
|
JA_SetMusicPosition(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
float get()
|
||||||
|
{
|
||||||
|
return JA_GetMusicPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace enable {
|
||||||
|
void set(const bool value)
|
||||||
|
{
|
||||||
|
JA_EnableMusic(value);
|
||||||
|
file::setconfigvalue("music", value?"true":"false");
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool get()
|
||||||
|
{
|
||||||
|
return JA_IsMusicEnabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sound
|
||||||
|
{
|
||||||
|
int load(const char *filename) {
|
||||||
|
int size;
|
||||||
|
char *buffer = file::getfilebuffer(filename, size);
|
||||||
|
int i=0;
|
||||||
|
while (i<MAX_SOUNDS && sounds[i]!=NULL) i++;
|
||||||
|
if (i==MAX_SOUNDS) { i=0; JA_DeleteSound(sounds[i]); }
|
||||||
|
sounds[i]=JA_LoadSound((Uint8*)buffer, size);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(int soundfile) {
|
||||||
|
JA_DeleteSound(sounds[soundfile]);
|
||||||
|
sounds[soundfile] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int play(int soundfile, const int volume) {
|
||||||
|
const int channel = JA_PlaySound(sounds[soundfile], 0);
|
||||||
|
JA_SetSoundVolume(128);
|
||||||
|
//Mix_Volume(channel, volume!=-1?volume:MIX_MAX_VOLUME);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop(int soundchannel) {
|
||||||
|
JA_StopChannel(soundchannel);
|
||||||
|
//Mix_HaltChannel(soundchannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace enable {
|
||||||
|
void set(const bool value)
|
||||||
|
{
|
||||||
|
JA_EnableSound(value);
|
||||||
|
file::setconfigvalue("sound", value?"true":"false");
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool get()
|
||||||
|
{
|
||||||
|
return JA_IsSoundEnabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
source/mini/audio/audio.h
Normal file
38
source/mini/audio/audio.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace mini
|
||||||
|
{
|
||||||
|
namespace audio
|
||||||
|
{
|
||||||
|
void init();
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
namespace music
|
||||||
|
{
|
||||||
|
void play(const char *filename, const int loop=-1);
|
||||||
|
void pause();
|
||||||
|
void resume();
|
||||||
|
void stop(const int t=1000);
|
||||||
|
namespace pos {
|
||||||
|
void set(float value);
|
||||||
|
float get();
|
||||||
|
}
|
||||||
|
namespace enable {
|
||||||
|
void set(const bool value);
|
||||||
|
const bool get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sound
|
||||||
|
{
|
||||||
|
int load(const char *filename);
|
||||||
|
void free(int soundfile);
|
||||||
|
int play(int soundfile, const int volume=-1);
|
||||||
|
void stop(int soundchannel);
|
||||||
|
namespace enable {
|
||||||
|
void set(const bool value);
|
||||||
|
const bool get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
509
source/mini/audio/jail_audio.cpp
Normal file
509
source/mini/audio/jail_audio.cpp
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
#ifndef JA_USESDLMIXER
|
||||||
|
#include "jail_audio.h"
|
||||||
|
#include "external/stb_vorbis.h"
|
||||||
|
#include "aux/log.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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
|
||||||
42
source/mini/audio/jail_audio.h
Normal file
42
source/mini/audio/jail_audio.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
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);
|
||||||
21
source/mini/config/config.cpp
Normal file
21
source/mini/config/config.cpp
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
source/mini/config/config.h
Normal file
15
source/mini/config/config.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace mini
|
||||||
|
{
|
||||||
|
namespace config
|
||||||
|
{
|
||||||
|
namespace key {
|
||||||
|
void set(const char* key, const char* value);
|
||||||
|
const char *get(const char* key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *folder();
|
||||||
|
}
|
||||||
|
}
|
||||||
445
source/mini/draw/draw.cpp
Normal file
445
source/mini/draw/draw.cpp
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
#include "draw.h"
|
||||||
|
#include "mini/surf/surf.h"
|
||||||
|
#include "mini/view/view.h"
|
||||||
|
#include "mini/font/font.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
source/mini/draw/draw.h
Normal file
58
source/mini/draw/draw.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
390
source/mini/file/file.cpp
Normal file
390
source/mini/file/file.cpp
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <pwd.h>
|
||||||
|
#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<file_t> 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<keyvalue_t> 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; i<num_files; ++i)
|
||||||
|
{
|
||||||
|
uint32_t file_offset, file_size;
|
||||||
|
fi.read( (char*)&file_offset, 4 );
|
||||||
|
fi.read( (char*)&file_size, 4 );
|
||||||
|
uint8_t path_size;
|
||||||
|
fi.read( (char*)&path_size, 1 );
|
||||||
|
char *file_name = (char*)malloc(path_size+1);
|
||||||
|
fi.read( file_name, path_size );
|
||||||
|
file_name[path_size] = 0;
|
||||||
|
std::string filename = file_name;
|
||||||
|
free(file_name);
|
||||||
|
toc.push_back({filename, file_size, file_offset});
|
||||||
|
}
|
||||||
|
fi.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getfilenamewithfolder(const char* filename) {
|
||||||
|
strcpy(scratch, resource_folder);
|
||||||
|
strcat(scratch, filename);
|
||||||
|
return scratch;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *getfilepointer(const char *resourcename, int& filesize, const bool binary) {
|
||||||
|
|
||||||
|
if (file_source==SOURCE_FILE and toc.size()==0) {
|
||||||
|
if (not getdictionary()) setsource(SOURCE_FOLDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (file_source==SOURCE_FILE) {
|
||||||
|
bool found = false;
|
||||||
|
uint32_t count = 0;
|
||||||
|
while( !found && count < toc.size() ) {
|
||||||
|
found = ( std::string(resourcename) == toc[count].path );
|
||||||
|
if( !found ) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !found ) {
|
||||||
|
perror("El recurs no s'ha trobat en l'arxiu de recursos");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
filesize = toc[count].size;
|
||||||
|
|
||||||
|
f = fopen(resource_filename, binary?"rb":"r");
|
||||||
|
if (not f) {
|
||||||
|
perror("No s'ha pogut obrir l'arxiu de recursos");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fseek(f, toc[count].offset, SEEK_SET);
|
||||||
|
} else {
|
||||||
|
f = fopen(getfilenamewithfolder(resourcename), binary?"rb":"r");
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
filesize = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getfilebuffer(const char *resourcename, int& filesize, const bool zero_terminate) {
|
||||||
|
FILE *f = getfilepointer(resourcename, filesize, true);
|
||||||
|
char* buffer = (char*)malloc(zero_terminate?filesize:filesize+1);
|
||||||
|
fread(buffer, filesize, 1, f);
|
||||||
|
if (zero_terminate) buffer[filesize]=0;
|
||||||
|
fclose(f);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *getfilepointerex(const char *filename, int& filesize, const bool binary) {
|
||||||
|
|
||||||
|
FILE *f;
|
||||||
|
f = fopen(filename, binary?"rb":"r");
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
filesize = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getfilebufferex(const char *filename, int& filesize, const bool zero_terminate) {
|
||||||
|
FILE *f = getfilepointerex(filename, filesize, true);
|
||||||
|
char* buffer = (char*)malloc(zero_terminate?filesize:filesize+1);
|
||||||
|
fread(buffer, filesize, 1, f);
|
||||||
|
if (zero_terminate) buffer[filesize]=0;
|
||||||
|
fclose(f);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea la carpeta del sistema donde guardar datos
|
||||||
|
void setconfigfolder(const char *foldername)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
config_folder = std::string(getenv("APPDATA")) + "/" + foldername;
|
||||||
|
#elif __APPLE__
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
const char *homedir = pw->pw_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<std::string> listresourcesdir(const char *folder, const char *extension)
|
||||||
|
{
|
||||||
|
std::vector<std::string> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
source/mini/file/file.h
Normal file
32
source/mini/file/file.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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<std::string> listresourcesdir(const char *folder, const char *extension=NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
121
source/mini/font/font.cpp
Normal file
121
source/mini/font/font.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#include "font.h"
|
||||||
|
#include "mini/surf/surf.h"
|
||||||
|
#include "mini/file/file.h"
|
||||||
|
#include "aux/log.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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; i<MAX_FONTS; ++i)
|
||||||
|
if (fonts[i].name && strcmp(fonts[i].name, filename)==0) {
|
||||||
|
log_msg(LOG_INFO, "Carrega de '%s' abortada: Reusant: %i.\n", filename, i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agafar la pròxima font lliure
|
||||||
|
unsigned int i = 0;
|
||||||
|
while (i<MAX_FONTS && fonts[i].name != NULL) ++i;
|
||||||
|
if (i==MAX_FONTS) return 255;
|
||||||
|
|
||||||
|
// Carregar l'arxiu de disc
|
||||||
|
int size;
|
||||||
|
char *buffer;
|
||||||
|
buffer = file::getfilebuffer(filename, size);
|
||||||
|
|
||||||
|
// Si no s'ha pogut, petar
|
||||||
|
if (!buffer) {
|
||||||
|
log_msg(LOG_FAIL, "Error al intentar obrir l'arxiu '%s'\n", filename);
|
||||||
|
exit(-1);
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_from_buffer(buffer, i, filename);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
log_msg(LOG_INFO, "Arxiu '%s' carregat en font: %i.\n", filename, i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace current {
|
||||||
|
void set(uint8_t font) {
|
||||||
|
current_font = &fonts[font];
|
||||||
|
}
|
||||||
|
uint8_t get() {
|
||||||
|
for (unsigned int i=0; i<MAX_FONTS; ++i) if (current_font == &fonts[i]) return i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace spacing {
|
||||||
|
void set(uint8_t spacing) {
|
||||||
|
current_font->spacing = spacing;
|
||||||
|
}
|
||||||
|
uint8_t get() {
|
||||||
|
return current_font->spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
source/mini/font/font.h
Normal file
33
source/mini/font/font.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
source/mini/key/key.cpp
Normal file
49
source/mini/key/key.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#include "key.h"
|
||||||
|
#include "mini/pad/pad.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
23
source/mini/key/key.h
Normal file
23
source/mini/key/key.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
171
source/mini/lua/lua.cpp
Normal file
171
source/mini/lua/lua.cpp
Normal file
@@ -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 <algorithm>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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<std::string> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1360
source/mini/lua/lua.debug.cpp
Normal file
1360
source/mini/lua/lua.debug.cpp
Normal file
File diff suppressed because it is too large
Load Diff
17
source/mini/lua/lua.debug.h
Normal file
17
source/mini/lua/lua.debug.h
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
18
source/mini/lua/lua.h
Normal file
18
source/mini/lua/lua.h
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1322
source/mini/lua/lua.wrappers.cpp
Normal file
1322
source/mini/lua/lua.wrappers.cpp
Normal file
File diff suppressed because it is too large
Load Diff
11
source/mini/lua/lua.wrappers.h
Normal file
11
source/mini/lua/lua.wrappers.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct lua_State;
|
||||||
|
|
||||||
|
namespace mini
|
||||||
|
{
|
||||||
|
namespace lua
|
||||||
|
{
|
||||||
|
void push_functions(lua_State *L);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
source/mini/main.cpp
Normal file
31
source/mini/main.cpp
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
586
source/mini/mini.cpp
Normal file
586
source/mini/mini.cpp
Normal file
@@ -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 <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define UPDATE_ALWAYS 0
|
||||||
|
#define UPDATE_WAIT 1
|
||||||
|
#define UPDATE_TIMEOUT 2
|
||||||
|
|
||||||
|
#ifdef MACOS_BUNDLE
|
||||||
|
#include <libgen.h>
|
||||||
|
#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; i<MAX_SURFACES; ++i) if (map_surface == &mini::surf::state.surfaces[i]) return i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void tileblit(uint8_t n, int x, int y) {
|
||||||
|
const int tw = tile_width;
|
||||||
|
const int th = tile_height;
|
||||||
|
|
||||||
|
const int tiles_per_row = mini::surf::state.source_surface->w / 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; i<MAX_SURFACES; ++i) mini::surf::destroy(i);
|
||||||
|
mini::surf::target::set(mini::surf::create(mini::win::state.width, mini::win::state.height));
|
||||||
|
mini::surf::state.dest_surface = mini::surf::state.screen_surface;
|
||||||
|
|
||||||
|
// Reset pal state
|
||||||
|
mini::pal::subpal::reset();
|
||||||
|
ds::trans=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace mini;
|
||||||
|
|
||||||
|
int main(int argc,char*argv[]){
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
|
||||||
|
log_msg(LOG_UNSALTED, "MINI v%s\n",MINI_VERSION);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Gestió de arguments
|
||||||
|
// ===============================================================
|
||||||
|
if (argc>1)
|
||||||
|
{
|
||||||
|
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;i<surf::state.screen_surface->size;++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<MAX_SURFACES;++i) surf::destroy(i);
|
||||||
|
surf::state.dest_surface = surf::state.source_surface = map_surface = NULL;
|
||||||
|
|
||||||
|
win::quit();
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
lua::cleanup();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void createNewProject() {
|
||||||
|
if (mini::file::createFolder("data")) {
|
||||||
|
log_msg(LOG_OK, "Directori 'data' creat\n");
|
||||||
|
} else {
|
||||||
|
log_msg(LOG_FAIL, "No s'ha pogut crear la carpeta 'data'\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *f = fopen("./data/game.ini", "w");
|
||||||
|
if (f) {
|
||||||
|
log_msg(LOG_OK, "Arxiu 'data/game.ini' creat\n");
|
||||||
|
} else {
|
||||||
|
log_msg(LOG_FAIL, "No s'ha pogut crear l'arxiu 'data/game.ini'\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
fprintf(f, "title=NEW MINI PROJECT\nconfig=newminiproject\nwidth=320\nheight=240\nzoom=2\n");
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
f = fopen("./data/main.lua", "w");
|
||||||
|
if (f) {
|
||||||
|
log_msg(LOG_OK, "Arxiu 'data/main.lua' creat\n");
|
||||||
|
} else {
|
||||||
|
log_msg(LOG_FAIL, "No s'ha pogut crear l'arxiu 'data/main.lua'\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
fprintf(f, "function mini.init()\n\nend\n\nfunction mini.update()\n surf.cls(0)\nend\n");
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
log_msg(LOG_OK, "Projecte nou creat. Ja està fet lo més dificil del jailgame!\n");
|
||||||
|
}
|
||||||
65
source/mini/mini.h
Normal file
65
source/mini/mini.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
44
source/mini/mouse/mouse.cpp
Normal file
44
source/mini/mouse/mouse.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include "mouse.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
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<x+w) && (state.y<y+h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
source/mini/mouse/mouse.h
Normal file
26
source/mini/mouse/mouse.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
48
source/mini/pad/pad.cpp
Normal file
48
source/mini/pad/pad.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "pad.h"
|
||||||
|
|
||||||
|
#include "aux/log.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
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<num_joysticks; ++i) {
|
||||||
|
if (SDL_IsGamepad(joysticks[i])) {
|
||||||
|
state.gamepad = SDL_OpenGamepad(joysticks[i]);
|
||||||
|
if (SDL_GamepadConnected(state.gamepad)) {
|
||||||
|
SDL_SetGamepadEventsEnabled(true);
|
||||||
|
log_msg(LOG_OK, "Gamepad found and initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool down(int8_t i) {
|
||||||
|
if (!state.gamepad) return false;
|
||||||
|
return SDL_GetGamepadButton(state.gamepad, SDL_GamepadButton(i)) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool press(int8_t i) {
|
||||||
|
if (state.just_pressed == i) {
|
||||||
|
state.just_pressed = -1;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int press() {
|
||||||
|
return state.just_pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
source/mini/pad/pad.h
Normal file
21
source/mini/pad/pad.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
267
source/mini/shader/shader.cpp
Normal file
267
source/mini/shader/shader.cpp
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
#include "shader.h"
|
||||||
|
#include "mini/file/file.h"
|
||||||
|
#include "mini/win/win.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include "CoreFoundation/CoreFoundation.h"
|
||||||
|
#include <OpenGL/OpenGL.h>
|
||||||
|
|
||||||
|
#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
|
||||||
|
#include <OpenGL/gl3.h>
|
||||||
|
#else
|
||||||
|
#include <OpenGL/gl.h>
|
||||||
|
#endif //!ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
|
||||||
|
#else
|
||||||
|
#include <SDL3/SDL_opengl.h>
|
||||||
|
#include <SDL3/SDL_opengl_glext.h>
|
||||||
|
#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); }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
27
source/mini/shader/shader.h
Normal file
27
source/mini/shader/shader.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
203
source/mini/surf/surf.cpp
Normal file
203
source/mini/surf/surf.cpp
Normal file
@@ -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 <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
namespace mini
|
||||||
|
{
|
||||||
|
namespace surf
|
||||||
|
{
|
||||||
|
|
||||||
|
state_t state;
|
||||||
|
|
||||||
|
uint8_t create(int w, int h) {
|
||||||
|
unsigned int i = 0;
|
||||||
|
while (i<MAX_SURFACES && state.surfaces[i].p != NULL) ++i;
|
||||||
|
if (i==MAX_SURFACES) return 255;
|
||||||
|
state.surfaces[i].name = nullptr;
|
||||||
|
state.surfaces[i].w = w;
|
||||||
|
state.surfaces[i].h = h;
|
||||||
|
state.surfaces[i].size = w*h;
|
||||||
|
state.surfaces[i].p = (uint8_t*)calloc(state.surfaces[i].size,1);
|
||||||
|
state.surfaces[i].flags = SURF_GENERATED;
|
||||||
|
clip::reset(i);
|
||||||
|
log_msg(LOG_INFO, "Surface %i creada.\n", i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t load(uint8_t* buffer, const char* name) {
|
||||||
|
// Agafar la pròxima textura lliure
|
||||||
|
unsigned int i = 0;
|
||||||
|
while (i<MAX_SURFACES && state.surfaces[i].p != NULL) ++i;
|
||||||
|
if (i==MAX_SURFACES) return 255;
|
||||||
|
|
||||||
|
state.surfaces[i].p = LoadGif(buffer, &state.surfaces[i].w, &state.surfaces[i].h);
|
||||||
|
state.surfaces[i].size = state.surfaces[i].w * state.surfaces[i].h;
|
||||||
|
state.surfaces[i].name = (char*)malloc(strlen(name)+1);
|
||||||
|
state.surfaces[i].flags = SURF_GENERATED;
|
||||||
|
clip::reset(i);
|
||||||
|
strcpy(state.surfaces[i].name, name);
|
||||||
|
|
||||||
|
log_msg(LOG_INFO, "Buffer '%s' carregat en surface: %i.\n", name, i);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t load(const char* filename, const bool external) {
|
||||||
|
// Si el gif ja s'ha carregat en una textura, tornem eixa textura
|
||||||
|
for (unsigned int i=0; i<MAX_SURFACES; ++i)
|
||||||
|
if (state.surfaces[i].name && strcmp(state.surfaces[i].name, filename)==0) {
|
||||||
|
log_msg(LOG_INFO, "Carrega de '%s' abortada: Reusant: %i.\n", filename, i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agafar la pròxima textura lliure
|
||||||
|
unsigned int i = 0;
|
||||||
|
while (i<MAX_SURFACES && state.surfaces[i].p != NULL) ++i;
|
||||||
|
if (i==MAX_SURFACES) return 255;
|
||||||
|
|
||||||
|
state.surfaces[i].flags = SURF_NOTHING;
|
||||||
|
// Carregar l'arxiu de disc
|
||||||
|
int size;
|
||||||
|
uint8_t *buffer;
|
||||||
|
if (external) {
|
||||||
|
buffer = (uint8_t*)file::getfilebufferex(filename, size);
|
||||||
|
state.surfaces[i].flags |= SURF_EXTERNAL;
|
||||||
|
} else {
|
||||||
|
buffer = (uint8_t*)file::getfilebuffer(filename, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no s'ha pogut, petar
|
||||||
|
if (!buffer) {
|
||||||
|
log_msg(LOG_FAIL, "Error al intentar obrir l'arxiu '%s'\n", filename);
|
||||||
|
exit(-1);
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalment, carregar el gif en la surface
|
||||||
|
state.surfaces[i].p = LoadGif(buffer, &state.surfaces[i].w, &state.surfaces[i].h);
|
||||||
|
state.surfaces[i].size = state.surfaces[i].w * state.surfaces[i].h;
|
||||||
|
state.surfaces[i].name = (char*)malloc(strlen(filename)+1);
|
||||||
|
clip::reset(i);
|
||||||
|
strcpy(state.surfaces[i].name, filename);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
log_msg(LOG_INFO, "Arxiu '%s' carregat en surface: %i.\n", filename, i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reloadsurfs() {
|
||||||
|
for (unsigned int i=0; i<MAX_SURFACES; ++i)
|
||||||
|
if (state.surfaces[i].name) {
|
||||||
|
if (state.surfaces[i].flags & SURF_GENERATED) {
|
||||||
|
log_msg(LOG_INFO, "Ignorant surface generada %i.\n", i);
|
||||||
|
} else {
|
||||||
|
log_msg(LOG_INFO, "Recarregant de disc surface %i:'%s'.\n", i, state.surfaces[i].name);
|
||||||
|
|
||||||
|
int size;
|
||||||
|
uint8_t *buffer;
|
||||||
|
if (state.surfaces[i].flags & SURF_EXTERNAL) {
|
||||||
|
buffer = (uint8_t*)file::getfilebufferex(state.surfaces[i].name, size);
|
||||||
|
} else {
|
||||||
|
buffer = (uint8_t*)file::getfilebuffer(state.surfaces[i].name, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no s'ha pogut, petar
|
||||||
|
if (!buffer) {
|
||||||
|
log_msg(LOG_FAIL, "Error al intentar obrir l'arxiu '%s'\n", state.surfaces[i].name);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.surfaces[i].p = LoadGif(buffer, &state.surfaces[i].w, &state.surfaces[i].h);
|
||||||
|
state.surfaces[i].size = state.surfaces[i].w * state.surfaces[i].h;
|
||||||
|
clip::reset(i);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void save(uint8_t surface, const char* filename, uint8_t *pal, uint8_t colors)
|
||||||
|
{
|
||||||
|
gif::write_gif(filename, state.surfaces[surface].p, state.surfaces[surface].w, state.surfaces[surface].h, pal, colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy(uint8_t surface) {
|
||||||
|
if (state.surfaces[surface].name != NULL) free(state.surfaces[surface].name);
|
||||||
|
state.surfaces[surface].name = NULL;
|
||||||
|
if (state.surfaces[surface].p != NULL) {
|
||||||
|
free(state.surfaces[surface].p);
|
||||||
|
log_msg(LOG_INFO, "Surface %i alliberada.\n", surface);
|
||||||
|
}
|
||||||
|
state.surfaces[surface].p = NULL;
|
||||||
|
state.surfaces[surface].flags = SURF_NOTHING;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width(uint8_t surface) {
|
||||||
|
return state.surfaces[surface].w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int height(uint8_t surface) {
|
||||||
|
return state.surfaces[surface].h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cls(uint8_t color) {
|
||||||
|
const uint8_t col = draw::state.draw_palette[color];
|
||||||
|
SDL_memset(state.dest_surface->p, 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; i<MAX_SURFACES; ++i) if (state.dest_surface == &state.surfaces[i]) return i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace source {
|
||||||
|
void set(uint8_t surface) {
|
||||||
|
state.source_surface = &state.surfaces[surface];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t get() {
|
||||||
|
for (unsigned int i=0; i<MAX_SURFACES; ++i) if (state.source_surface == &state.surfaces[i]) return i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace clip {
|
||||||
|
void set(int x, int y, int w, int h) {
|
||||||
|
state.dest_surface->clip[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
56
source/mini/surf/surf.h
Normal file
56
source/mini/surf/surf.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
source/mini/version.h
Normal file
3
source/mini/version.h
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define MINI_VERSION "1.4.10"
|
||||||
28
source/mini/view/view.cpp
Normal file
28
source/mini/view/view.cpp
Normal file
@@ -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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
source/mini/view/view.h
Normal file
20
source/mini/view/view.h
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
105
source/mini/win/win.cpp
Normal file
105
source/mini/win/win.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#include "win.h"
|
||||||
|
#include "aux/log.h"
|
||||||
|
#include "mini/file/file.h"
|
||||||
|
#include "mini/shader/shader.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
source/mini/win/win.h
Normal file
53
source/mini/win/win.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user