- [NEW] Passat a SDL3

- [NEW] Ara usa JailAudio
- [NEW] Usant última versió de Respak
- [NEW] Afegit lagueirtofile
This commit is contained in:
2025-06-29 16:56:28 +02:00
parent d5286d8abe
commit 737e18fd96
22 changed files with 6398 additions and 832 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ arounders_debug
release/* release/*
respak2 respak2
*.dSYM/* *.dSYM/*
build/*

5
lagueirtofile Normal file
View File

@@ -0,0 +1,5 @@
libs = -lSDL3 -lGL
cppflags = -D DEBUG -D VERBOSE -g -Wall
executable = arounders
sourcepath = source
buildpath = build

View File

@@ -22,7 +22,7 @@ namespace textfile
const bool open(std::string filename) const bool open(std::string filename)
{ {
buffer = file::getFileBuffer(filename.c_str(), &fsize); buffer = file_getfilebuffer(filename.c_str(), fsize);
p = 0; p = 0;
return true; return true;

View File

@@ -25,9 +25,7 @@ namespace gamestate
menu::fondo = draw::loadSurface("menuprin.gif", true); menu::fondo = draw::loadSurface("menuprin.gif", true);
menu::cursor = draw::loadSurface("cursor.gif"); menu::cursor = draw::loadSurface("cursor.gif");
if (audio::getMusicState() != audio::music_state::MUSIC_PLAYING || audio::whichMusic() != "mus3.ogg") { JA_LoadAndPlayMusic("mus3.ogg");
audio::playMusic(audio::loadMusic("mus3.ogg"));
}
font::selectFont(font::type::colored); font::selectFont(font::type::colored);
font::setColor(font::color::white); font::setColor(font::color::white);

View File

@@ -24,7 +24,7 @@ namespace gamestate
fondo = draw::loadSurface("mort.gif", true); fondo = draw::loadSurface("mort.gif", true);
cursor = draw::loadSurface("cursor.gif"); cursor = draw::loadSurface("cursor.gif");
audio::playMusic(audio::loadMusic("mus5.ogg")); JA_LoadAndPlayMusic("mus5.ogg");
draw::fadein(); draw::fadein();

View File

@@ -3,7 +3,7 @@
#include <string> #include <string>
#include "aux_font.h" #include "aux_font.h"
#include "proc_mapa.h" #include "proc_mapa.h"
#include <SDL2/SDL.h> #include <SDL3/SDL.h>
namespace gamestate namespace gamestate
{ {
@@ -120,7 +120,7 @@ namespace gamestate
{ {
password[10] = 0; password[10] = 0;
int filesize = 0; int filesize = 0;
const char *buffer = file::getFileBuffer("offsets.bal", &filesize); const char *buffer = file_getfilebuffer("offsets.bal", filesize);
int punter = 0; int punter = 0;

View File

@@ -1,6 +1,6 @@
#include "gamestates.h" #include "gamestates.h"
#include "jgame.h" #include "jgame.h"
#include <SDL2/SDL.h> #include <SDL3/SDL.h>
#include <string> #include <string>
#include "aux_font.h" #include "aux_font.h"
#include "proc_mapa.h" #include "proc_mapa.h"
@@ -72,8 +72,7 @@ namespace gamestate
// arounder_seleccionat = primerArounders // arounder_seleccionat = primerArounders
// Enxufa el arradio // Enxufa el arradio
audio::loadMusic((game::getConfig("fase")+1) % 5 == 0 ? "mus6.ogg" : "mus4.ogg"); JA_LoadAndPlayMusic((game::getConfig("fase")+1) % 5 == 0 ? "mus6.ogg" : "mus4.ogg");
audio::playMusic();
// Fiquem a contar el cronómetre de arounders // Fiquem a contar el cronómetre de arounders
play::arounderCount = play::startTicks = game::getTicks(); play::arounderCount = play::startTicks = game::getTicks();
@@ -225,11 +224,11 @@ namespace gamestate
if (mapa::arounders::arrivats >= mapa::arounders::necessaris) { if (mapa::arounders::arrivats >= mapa::arounders::necessaris) {
game::setConfig("fase", game::getConfig("fase")+1); game::setConfig("fase", game::getConfig("fase")+1);
draw::fadeout(); draw::fadeout();
audio::fadeoutMusic(); JA_FadeOutMusic(250);
play::exit = 1; play::exit = 1;
} else { } else {
draw::fadeout(); draw::fadeout();
audio::fadeoutMusic(); JA_FadeOutMusic(250);
play::exit = 2; play::exit = 2;
} }
} }
@@ -287,13 +286,13 @@ namespace gamestate
if (input::mouseY() >= 71 && input::mouseY() <= 76) { if (input::mouseY() >= 71 && input::mouseY() <= 76) {
free(play::original_palette); free(play::original_palette);
draw::fadeout(); draw::fadeout();
audio::fadeoutMusic(); JA_FadeOutMusic(250);
play::exit = 1; play::exit = 1;
} }
if (input::mouseY() >= 82 && input::mouseY() <= 87) { if (input::mouseY() >= 82 && input::mouseY() <= 87) {
free(play::original_palette); free(play::original_palette);
draw::fadeout(); draw::fadeout();
audio::fadeoutMusic(); JA_FadeOutMusic(250);
play::exit = 2; play::exit = 2;
} }
if (input::mouseY() >= 93 && input::mouseY() <= 98) { if (input::mouseY() >= 93 && input::mouseY() <= 98) {

View File

@@ -31,8 +31,7 @@ namespace gamestate
gamestate::sequence::init(); gamestate::sequence::init();
return; return;
} else { } else {
audio::loadMusic("mus3.ogg"); JA_LoadAndPlayMusic("mus3.ogg");
audio::playMusic();
} }
if (game::getConfig("fase") % 5 == 0) { if (game::getConfig("fase") % 5 == 0) {
@@ -104,7 +103,8 @@ namespace gamestate
std::string getPassword() std::string getPassword()
{ {
char *buffer = file::getFileBuffer("offsets.bal"); int size;
char *buffer = file_getfilebuffer("offsets.bal", size);
int punter = (game::getConfig("fase")-1)*10; int punter = (game::getConfig("fase")-1)*10;
char passFile[11]; char passFile[11];

View File

@@ -1,7 +1,7 @@
#include "gamestates.h" #include "gamestates.h"
#include "jgame.h" #include "jgame.h"
#include <string> #include <string>
#include <SDL2/SDL.h> #include <SDL3/SDL.h>
#include "aux_font.h" #include "aux_font.h"
namespace gamestate namespace gamestate
@@ -33,7 +33,8 @@ namespace gamestate
default: gamestate::prefase::init(); return; break; default: gamestate::prefase::init(); return; break;
} }
sequence_file = file::getFilePointer(filename); int size;
sequence_file = file_getfilepointer(filename.c_str(), size);
game::setState(&gamestate::sequence::loop); game::setState(&gamestate::sequence::loop);
} }
@@ -98,15 +99,14 @@ namespace gamestate
} else if (command=="PLAYMUSIC") { } else if (command=="PLAYMUSIC") {
fscanf(sequence_file, " '%[^']'", text); fscanf(sequence_file, " '%[^']'", text);
audio::loadMusic(text); JA_LoadAndPlayMusic(text);
audio::playMusic();
} else if (command=="FADEOUT") { } else if (command=="FADEOUT") {
draw::fadeout(); draw::fadeout();
} else if (command=="FADEOUTMUSIC") { } else if (command=="FADEOUTMUSIC") {
draw::fadeout(); draw::fadeout();
audio::fadeoutMusic(); JA_FadeOutMusic(250);
} else if (command=="END") { } else if (command=="END") {
fclose(sequence_file); fclose(sequence_file);

View File

@@ -2,6 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdint.h>
#define EXTENSION_INTRODUCER 0x21 #define EXTENSION_INTRODUCER 0x21
#define IMAGE_DESCRIPTOR 0x2C #define IMAGE_DESCRIPTOR 0x2C

509
source/jail_audio.cpp Normal file
View File

@@ -0,0 +1,509 @@
#ifndef JA_USESDLMIXER
#include "jail_audio.h"
#include "jfile.h"
#include "stb_vorbis.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 };
char *filename { 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, JA_musicVolume*(1.0 - percent));
}
}
if (current_music->times != 0)
{
if (SDL_GetAudioStreamAvailable(current_music->stream) < (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) < (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 num_channels)
{
#ifdef DEBUG
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
#endif
SDL_Log("Iniciant JailAudio...");
JA_audioSpec = {format, num_channels, freq };
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
SDL_Log( (sdlAudioDevice==0) ? "Failed to initialize SDL audio!\n" : "OK!\n");
for (int i=0;i<JA_MAX_SIMULTANEOUS_CHANNELS;++i) channels[i].state = JA_CHANNEL_FREE;
//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)
{
int size;
char *buffer = file_getfilebuffer(filename, size);
JA_Music_t *music = JA_LoadMusic((Uint8*)buffer, size);
music->filename = (char*)malloc(strlen(filename)+1);
strcpy(music->filename, filename);
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(&current_music->spec, &JA_audioSpec);
if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) printf("[ERROR] SDL_PutAudioStreamData failed!\n");
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) printf("[ERROR] SDL_BindAudioStream failed!\n");
//SDL_ResumeAudioStreamDevice(current_music->stream);
}
void JA_LoadAndPlayMusic(const char* filename, const bool force_reinit)
{
if ( (JA_GetMusicState() != JA_MUSIC_PLAYING) || (strcmp(JA_GetMusicFilename(), filename)!=0) || force_reinit ) {
JA_PlayMusic(JA_LoadMusic(filename));
}
}
char *JA_GetMusicFilename(JA_Music_t *music)
{
if (!music) music = current_music;
return music->filename;
}
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;
free(current_music->filename);
current_music->filename = 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;
}
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;
}
float JA_SetVolume(float volume)
{
JA_SetSoundVolume(JA_SetMusicVolume(volume) / 2.0f);
return JA_musicVolume;
}
#endif

42
source/jail_audio.h Normal file
View 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 num_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_LoadAndPlayMusic(const char* filename, const bool force_reinit=false);
char *JA_GetMusicFilename(JA_Music_t *music = nullptr);
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);
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);
float JA_SetVolume(float volume);

View File

@@ -1,168 +0,0 @@
#include "jaudio.h"
#include "jfile.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>
#include <stdio.h>
namespace audio
{
// Açò son estructures de mentires, per a usar estructures de SDL_Mixer de forma opaca.
// Al final es un punter, així que és irrelevant el tipus del punter,
// però si amague que son estructures de SDL_Mixer, no fa falta ficar el include a SDL_mixer fora de este arxiu
struct sound
{
}; // Dummy structs
static char *buffer = nullptr;
static Mix_Music *music = nullptr;
static std::string music_filename = "";
// Inicialitza el sistema de só
void init()
{
// Al final he ficat la configuració automàtica i au. Si en el futur necesitem canviar-ho pos se canvia
if (Mix_Init(MIX_INIT_OGG)==0) {
printf("Failed Mix_Init()\n");
}
if (Mix_OpenAudio(48000, AUDIO_S16, 2, 1024)!=0) {
printf("Failed Mix_OpenAudio()\n");
}
}
// Tanca el sistema de só (no shit, sherlock)
void quit()
{
Mix_CloseAudio();
}
// Carrega un arxiu de música en format OGG
const bool loadMusic(const std::string filename)
{
if (music != nullptr) {
Mix_FreeMusic(music);
free(buffer);
}
int filesize=0;
buffer = file::getFileBuffer(filename, &filesize);
music = Mix_LoadMUS_RW(SDL_RWFromMem(buffer, filesize), 1);
if (music==nullptr) return false;
music_filename = filename;
return true;
}
// Comença a reproduïr la música en questió
void playMusic(const int loop)
{
if (Mix_PlayMusic(music, loop) == -1) {
printf("Failed Mix_PlayMusic()\n");
}
}
// Pausa la música que està sonant ara
void pauseMusic()
{
Mix_PauseMusic();
}
// Continua la música pausada
void resumeMusic()
{
Mix_ResumeMusic();
}
// Para la música que estava sonant
void stopMusic()
{
Mix_HaltMusic();
}
// Para la música que estava sonant fent un fade
void fadeoutMusic()
{
Mix_FadeOutMusic(500);
}
// Obté el estat actual de la música
const music_state getMusicState()
{
if (Mix_PausedMusic())
{
return MUSIC_PAUSED;
}
else if (Mix_PlayingMusic())
{
return MUSIC_PLAYING;
}
else
{
return MUSIC_STOPPED;
}
}
// Obté el nom de l'arxiu de música actual
std::string whichMusic()
{
return music_filename;
}
// Carrega un só des d'un arxiu WAV
const sound *loadSound(const std::string filename)
{
return (sound *)Mix_LoadWAV(filename.c_str());
}
// Comença a reproduïr el só especificat
const int playSound(sound *snd, const int loop)
{
return Mix_PlayChannel(-1, (Mix_Chunk *)snd, loop);
}
// Allibera un só
void deleteSound(sound *snd)
{
Mix_FreeChunk((Mix_Chunk *)snd);
}
// Pausa un canal en el que s'estava reproduïnt un só
void pauseChannel(const int channel)
{
Mix_Pause(channel);
}
// Continua un canal pausat
void resumeChannel(const int channel)
{
Mix_Resume(channel);
}
// Para un canal que estava reproduïnt un só
void stopChannel(const int channel)
{
Mix_HaltChannel(channel);
}
// Obté l'estat d'un canal
const channel_state getChannelState(const int channel)
{
if (Mix_Paused(channel))
{
return CHANNEL_PAUSED;
}
else if (Mix_Playing(channel))
{
return CHANNEL_PLAYING;
}
else
{
return CHANNEL_FREE;
}
}
// Estableix el volum general
const int setVolume(int volume)
{
return Mix_Volume(-1, volume);
}
}

View File

@@ -1,98 +0,0 @@
#pragma once
#include <string>
namespace audio
{
// Enumeració per a representar el estat de un canal de sò
enum channel_state
{
CHANNEL_INVALID,
CHANNEL_FREE,
CHANNEL_PLAYING,
CHANNEL_PAUSED
};
// Enumeració per a representar el estat de la música
enum music_state
{
MUSIC_INVALID,
MUSIC_PLAYING,
MUSIC_PAUSED,
MUSIC_STOPPED
};
// Estructures per a gestionar música i só
struct sound;
/// @brief Inicialitza el sistema de só
void init();
/// @brief Tanca el sistema de só
void quit();
/// @brief Carrega un arxiu de música en format OGG
/// @param filename nom de l'arxiu
/// @return true si tot be, false si ha fallat
const bool loadMusic(const std::string filename);
/// @brief Comença a reproduïr la música en questió
/// @param loop quants bucles farà (-1=infinit, 0=no repeteix, 1=repeteix 1 vegada...)
void playMusic(const int loop = -1);
/// @brief Pausa la música que està sonant ara
void pauseMusic();
/// @brief Continua la música pausada
void resumeMusic();
/// @brief Para la música que estava sonant
void stopMusic();
/// @brief Para la música que estava sonant fent un fade
void fadeoutMusic();
/// @brief Obté el estat actual de la música
/// @return estat actual de la música (MUSIC_INVALID, MUSIC_PLAYING, MUSIC_PAUSED o MUSIC_STOPPED)
const music_state getMusicState();
/// @brief Obté el nom de l'arxiu de música actual
/// @return el nom de l'arxiu
std::string whichMusic();
/// @brief Carrega un só des d'un arxiu WAV
/// @param filename nom de l'arxiu
/// @return un punter al só
const sound *loadSound(const std::string filename);
/// @brief Comença a reproduïr el só especificat
/// @param snd punter al só a reproduïr
/// @param loop si es fa bucle (-1=infinit, 0=no repeteix, 1=repeteix 1 vegada...)
/// @return número del canal en que està sonant el só
const int playSound(sound *snd, const int loop = 0);
/// @brief Pausa un canal en el que s'estava reproduïnt un só
/// @param channel número del canal a pausar
void pauseChannel(const int channel);
/// @brief Continua un canal pausat
/// @param channel número del canal pausat
void resumeChannel(const int channel);
/// @brief Para un canal que estava reproduïnt un só
/// @param channel número del canal a parar
void stopChannel(const int channel);
/// @brief Obté l'estat d'un canal
/// @param channel canal del que es vol obtindre l'estat
/// @return estat del canal (CHANNEL_INVALID, CHANNEL_FREE, CHANNEL_PLAYING o CHANNEL_PAUSED)
const channel_state getChannelState(const int channel);
/// @brief Allibera un só
/// @param snd punter al só
void deleteSound(sound *snd);
/// @brief Estableix el volum general
/// @param volume valor a establir com a volum (128 màxim)
/// @return el volum anterior
const int setVolume(int volume);
}

View File

@@ -1,6 +1,6 @@
#include "jdraw.h" #include "jdraw.h"
#include "SDL2/SDL.h" #include "SDL3/SDL.h"
#include "gif.c" #include "gif.h"
#include "jfile.h" #include "jfile.h"
namespace draw namespace draw
@@ -42,12 +42,16 @@ namespace draw
// [TODO] Incloure gestió de pantalla completa // [TODO] Incloure gestió de pantalla completa
// Inicialització de les estructures de SDL // Inicialització de les estructures de SDL
sdl_window = SDL_CreateWindow(titol.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width * zoom, height * zoom, SDL_WINDOW_SHOWN); sdl_window = SDL_CreateWindow(titol.c_str(), width * zoom, height * zoom, 0);
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0); if (!sdl_window) { SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR: Failed to initialize window!\n"); exit(1); }
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
if (!sdl_renderer) { SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR: Failed to initialize renderer!\n"); exit(1); }
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST);
if (!sdl_texture) { SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR: Failed to initialize texture!\n"); exit(1); }
// Establim el tamany "logic", indepndent del tamany de finestra // Establim el tamany "logic", indepndent del tamany de finestra
SDL_RenderSetLogicalSize(sdl_renderer, width, height); //SDL_SetRenderLogicalPresentation(sdl_renderer, width, height,);
// Creem la superficie "screen" i la establim com a superficie destinació // Creem la superficie "screen" i la establim com a superficie destinació
screen = createSurface(width, height); screen = createSurface(width, height);
@@ -58,7 +62,7 @@ namespace draw
sel_color = transparent = 0; sel_color = transparent = 0;
for (int i=0;i<256;++i) color_indices[i] = i; for (int i=0;i<256;++i) color_indices[i] = i;
SDL_ShowCursor(SDL_DISABLE); SDL_HideCursor();
//textsurf = loadSurface("font.gif"); //textsurf = loadSurface("font.gif");
} }
@@ -114,7 +118,7 @@ namespace draw
// Agafem un buffer de bytes de l'arxiu especificat // Agafem un buffer de bytes de l'arxiu especificat
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char // getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
int size; int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, &size); uint8_t *buffer = (uint8_t *)file_getfilebuffer(filename.c_str(), size);
// Si ens ha tornat nullptr, es que no l'ha trobat, tornem nosaltres també nullptr ja que no s'ha pogut crear la superficie // Si ens ha tornat nullptr, es que no l'ha trobat, tornem nosaltres també nullptr ja que no s'ha pogut crear la superficie
if (buffer == nullptr) if (buffer == nullptr)
@@ -230,7 +234,7 @@ namespace draw
// Agafem un buffer de bytes de l'arxiu especificat // Agafem un buffer de bytes de l'arxiu especificat
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char // getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
int size; int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, &size); uint8_t *buffer = (uint8_t *)file_getfilebuffer(filename.c_str(), size);
// Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta // Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta
// Van a ser 256 entrades de 32 bits, cada entrada es un color, amb el format 0xAARRGGBB // Van a ser 256 entrades de 32 bits, cada entrada es un color, amb el format 0xAARRGGBB
@@ -497,14 +501,14 @@ namespace draw
// i el enviem a la textura SDL // i el enviem a la textura SDL
for (uint32_t i = 0; i < size; ++i) for (uint32_t i = 0; i < size; ++i)
{ {
sdl_pixels[i] = palette[screen->pixels[i]]; sdl_pixels[i] = palette[screen->pixels[i]] | 0xff000000;
} }
// Desbloquejem la textura // Desbloquejem la textura
SDL_UnlockTexture(sdl_texture); SDL_UnlockTexture(sdl_texture);
// Pintem la textura a pantalla // Pintem la textura a pantalla
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL); SDL_RenderTexture(sdl_renderer, sdl_texture, NULL, NULL);
// I ho presentem // I ho presentem
SDL_RenderPresent(sdl_renderer); SDL_RenderPresent(sdl_renderer);

View File

@@ -15,486 +15,215 @@
#include <pwd.h> #include <pwd.h>
#endif #endif
#define DEFAULT_FILENAME "data.jf2" #define DEFAULT_FILENAME "data.jf2"
#define DEFAULT_FOLDER "data/" #define DEFAULT_FOLDER "data/"
#define CONFIG_FILENAME "config.txt" #define CONFIG_FILENAME "config.txt"
/* FORMAT DEL ARXIU .JF2 struct file_t
4 bytes header capçalera "PK2" (caracter 0 de final de cadena al final) (en realitat passe de ella, pero be...)
4 bytes num_files nombre d'arxius inclosos
4 bytes toc_offset en quina posició de l'arxiu està la taula de continguts
... Ara venen tots els arxius inclosos, uno darrere de l'altre.
... Quan acaben ve la taula de continguts. La taula te tantes entrades com arxius inclosos. Cada entrada te el següent format:
(per cert, toc_offset apunta ací)
4 bytes offset en quina posició del arxiu de recursos comença este arxiu
4 bytes size tamany d'este arxiu
1 byte path_size tamany de la cadena amb la ruta de l'arxiu
path_size bytes path ruta de l'arxiu original. La usem per a trobar el arxiu que ens demanen.
EXEMPLE SIMPLE:
- Imaginem que volem incloure un arxiu "data/hola.txt" amb el contingut "HOLA", i un arxiu "data/adios.txt" amb el contingut "ADIOS":
OFFSET CONTINGUT TAMANY DESCRIPCIÓ
0 "PK2"+0 4 La capçalera
4 2 4 nombre d'arxius
8 21 4 offset a la taula de continguts
--- COMENCEN ELS ARXIUS INCLOSOS ---
12 HOLA 4 el contingut del primer arxiu
16 ADIOS 5 el contingut del segon arxiu
--- COMENÇA LA TAULA DE CONTINGUTS ---
21 12 4 offset al primer arxiu
25 4 4 tamany del primer axiu
29 13 1 tamany de la ruta al primer arxiu
30 "data/hola.txt" 13 la ruta al primer arxiu
43 16 4 offset al primer arxiu
47 4 4 tamany del primer axiu
51 13 1 tamany de la ruta al primer arxiu
52 "data/adios.txt" 14 la ruta al primer arxiu
- Es un exemple raro, perque ocupa mes la ruta al arxiu que l'arxiu en si, pero espere que la idea quede clara!
Al principi se carrega la tabla de continguts en memòria, així el acces als arxius es ràpid.
Y com funciona tot açò? pos per defecte va a intentar llegir tots els arxius de "data.jf2". Si no troba e l'arxiu, automaticament passa a
buscar-ho tot en la carpeta "data" en arxius independents. En principi, si no se tenen requeriments diferents, no fa falta configurar res.
Respecte al tema de l'arxiu de configuració, està montat de forma que se pot escriure i llegir valors asociats a una clau sense calfar-se el cap.
En l'arxiu de configuració els valor se guarden de la forma:
CLAU=VALOR
Cada un en una linea. Si llegim i no existeix, torna cadena buida. Si escrivim i ja exisita, se reemplaça.
Tot son valors de cadena. Si en realitat son números, tindràs que encarregar-te tu de la caonversió de cadena a numero o al reves.
*/
namespace file
{ {
// Estructures std::string path;
// =============================================================================================================================== uint32_t size;
uint32_t offset;
};
// Estructura que representa un arxiu en la tabla de continguts del arxiu de recursos std::vector<file_t> toc;
struct file_t
{
std::string path; // Ruta relativa de l'arxiu
uint32_t size; // Tamany de l'arxiu
uint32_t offset; // Offset a l'arxiu dins de l'arxiu de recursos
};
std::vector<file_t> toc; // vector que conté la taula de continguts de l'arxiu de recursos /* 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;
};
/* 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 */ char *resource_filename = NULL;
struct keyvalue_t char *resource_folder = NULL;
{ int file_source = SOURCE_FILE;
std::string key, value; char scratch[255];
}; static std::string config_folder;
std::vector<keyvalue_t> config;
// Variables void file_setresourcefilename(const char *str) {
// =============================================================================================================================== if (resource_filename != NULL) free(resource_filename);
static std::string resource_filename = DEFAULT_FILENAME; // Nom de l'arxiu de recursos resource_filename = (char*)malloc(strlen(str)+1);
static std::string resource_folder = DEFAULT_FOLDER; // Nom de la carpeta de recursos strcpy(resource_filename, str);
static int file_source = SOURCE_FILE; // D'on anem a pillar els recursos, arxiu o carpeta }
static std::string config_folder; // Nom de la carpeta on guardar la configuració
std::vector<keyvalue_t> config; // Vector amb els valors guardats a l'arxiu de configuració
// Funcions void file_setresourcefolder(const char *str) {
// =============================================================================================================================== if (resource_folder != NULL) free(resource_folder);
resource_folder = (char*)malloc(strlen(str)+1);
strcpy(resource_folder, str);
}
// Estableix el nom de l'arxiu on estàn guardats els recursos (es "data.jf2" per defecte) void file_setsource(const int src) {
void setResourceFilename(const std::string str) 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) file_setresourcefolder(DEFAULT_FOLDER);
resource_filename = str; }
bool file_getdictionary() {
if (resource_filename == NULL) file_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 (int 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[path_size+1];
fi.read( file_name, path_size );
file_name[path_size] = 0;
std::string filename = file_name;
toc.push_back({filename, file_size, file_offset});
}
fi.close();
}
char *file_getfilenamewithfolder(const char* filename) {
strcpy(scratch, resource_folder);
strcat(scratch, filename);
return scratch;
}
FILE *file_getfilepointer(const char *resourcename, int& filesize, const bool binary) {
if (file_source==SOURCE_FILE and toc.size()==0) {
if (not file_getdictionary()) file_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(file_getfilenamewithfolder(resourcename), binary?"rb":"r");
fseek(f, 0, SEEK_END);
filesize = ftell(f);
fseek(f, 0, SEEK_SET);
} }
return f;
}
// Estableix el nom de la carpeta on estàn guardats els recursos (es "data" per defecte) char *file_getfilebuffer(const char *resourcename, int& filesize) {
void setResourceFolder(const std::string str) FILE *f = file_getfilepointer(resourcename, filesize, true);
{ char* buffer = (char*)malloc(filesize);
resource_folder = str; fread(buffer, filesize, 1, f);
} fclose(f);
return buffer;
}
// Estableix d'on s'han de obtenir els recursos (arxius individuals dins d'una carpeta o arxiu de recursos) // Crea la carpeta del sistema donde guardar datos
void setSource(const int src) void file_setconfigfolder(const char *foldername)
{ {
file_source = src % 2; // mod 2 de forma que sempre es un valor vàlid, 0 (arxiu) o 1 (carpeta)
// Si volem que busque en carpeta i encara no haviem especificat una carpeta, usem la per defecte
if (/*src == SOURCE_FOLDER && */resource_folder == "")
{
setResourceFolder(DEFAULT_FOLDER);
}
}
// Carreguem la taula de continguts de l'arxiu de recursos
bool getTableOfContents()
{
// Si encara no haviem especificat un arxiu de recursos, usem el arxiu de recursos per defecte
if (resource_filename == "")
{
setResourceFilename(DEFAULT_FILENAME);
}
// Si l'arxiu de recursos existeix...
std::ifstream fi(resource_filename, std::ios::binary);
if (fi.is_open())
{
// Llegim la capçalera (controlar que siga correcta?)
char header[4];
fi.read(header, 4);
// LLegim el nombre d'arxius i la posició de la taula de continguts
uint32_t num_files, toc_offset;
fi.read((char *)&num_files, 4);
fi.read((char *)&toc_offset, 4);
// Anem a la taula de continguts
fi.seekg(toc_offset);
// Per a cada arxiu inclos en l'arxiu de recursos...
for (unsigned int i = 0; i < num_files; ++i)
{
// Llegim en quina posició està i quant ocupa
uint32_t file_offset, file_size;
fi.read((char *)&file_offset, 4);
fi.read((char *)&file_size, 4);
// Llegim la seua ruta
uint8_t path_size;
fi.read((char *)&path_size, 1);
char file_name[path_size + 1];
fi.read(file_name, path_size);
file_name[path_size] = 0;
std::string filename = file_name;
// Y afegim les dades a la taula de continguts en memòria
toc.push_back({filename, file_size, file_offset});
}
// Tanquem la paradeta i tornem true
fi.close();
return true;
}
else // Si no s'ha pogut llegir el arxiu de recursos, tornem false
{
return false;
}
}
// Obté un "std::ifstream" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
std::ifstream getFileStream(const std::string resourcename, int *filesize, const bool binary)
{
// Si tenim configurat agafar els recursos de arxiu, pero encara no tenim la taula de continguts carregada...
if (file_source == SOURCE_FILE and toc.size() == 0)
{
// Si fallem al intentar carregar la taula de continguts de l'arxiu de recursos, canviem a pillar els recursos de carpeta
if (not getTableOfContents())
{
setSource(SOURCE_FOLDER);
}
}
std::ifstream f;
// Si estem pillant els recursos de un arxiu de recursos...
if (file_source == SOURCE_FILE)
{
// Busquem el recurs en la taula de continguts usant la ruta
bool found = false;
uint32_t count = 0;
while (!found && count < toc.size())
{
found = (resource_folder + resourcename == toc[count].path);
if (!found)
{
count++;
}
}
// Si no trobem el recurs, petem el mame
if (!found)
{
printf("ERROR FATAL: No s'ha trobat el recurs '%s' a l'arxiu de recursos '%s'\n", resourcename.c_str(), resource_filename.c_str());
exit(1);
}
// Agafem el tamany del recurs de la taula de continguts
if (filesize) *filesize = toc[count].size;
// obrim l'arxiu de recursos
f.open(resource_filename.c_str(), binary ? std::ios::binary : std::ios::in);
if (!f.is_open()) // En el raruno cas de que a este altures pete al obrir el arxiu de recursos, petem el mame
{
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu de recursos '%s'\n", resource_filename.c_str());
exit(1);
}
// Anem a la posició on està el recurs que volem. Amb açò "f" ja està preparat per a ser tornar.
// Ojo, realment estic tornant un FILE* al arxiu de recursos, pero ja apuntant al moment en que comença el recurs que volem.
// Ho dic perque si fem fseek(f, 0, SEEK_SET) tornarà al principi de l'arxiu de recursos, no del recurs. Tindre-ho en compte.
f.seekg(toc[count].offset);
}
else
{
// Si estem pillant els recursos de carpeta, simplement obrim el arxiu en questió i tornem el FILE* associat.
f.open((resource_folder + resourcename), binary ? std::ios::binary : std::ios::in);
if (f.rdstate() & std::ios_base::failbit)
{
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu '%s/%s'\n", resource_folder.c_str(), resourcename.c_str());
exit(1);
}
f.seekg(0, std::ios_base::end);
if (filesize) *filesize = f.tellg();
f.seekg(0, std::ios_base::beg);
}
// Tornar el punter FILE* al arxiu. OJO! Tenim que tancar-lo quan acabem amb ell
return f;
}
// Obté un "FILE*" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
FILE *getFilePointer(const std::string resourcename, int *filesize, const bool binary)
{
// Si tenim configurat agafar els recursos de arxiu, pero encara no tenim la taula de continguts carregada...
if (file_source == SOURCE_FILE and toc.size() == 0)
{
// Si fallem al intentar carregar la taula de continguts de l'arxiu de recursos, canviem a pillar els recursos de carpeta
if (not getTableOfContents())
{
setSource(SOURCE_FOLDER);
}
}
FILE *f;
// Si estem pillant els recursos de un arxiu de recursos...
if (file_source == SOURCE_FILE)
{
// Busquem el recurs en la taula de continguts usant la ruta
bool found = false;
uint32_t count = 0;
while (!found && count < toc.size())
{
found = (resource_folder + resourcename == toc[count].path);
if (!found)
{
count++;
}
}
// Si no trobem el recurs, petem el mame
if (!found)
{
printf("ERROR FATAL: No s'ha trobat el recurs '%s' a l'arxiu de recursos '%s'\n", resourcename.c_str(), resource_filename.c_str());
exit(1);
}
// Agafem el tamany del recurs de la taula de continguts
if (filesize) *filesize = toc[count].size;
// obrim l'arxiu de recursos
f = fopen(resource_filename.c_str(), binary ? "rb" : "r");
if (!f) // En el raruno cas de que a este altures pete al obrir el arxiu de recursos, petem el mame
{
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu de recursos '%s'\n", resource_filename.c_str());
exit(1);
}
// Anem a la posició on està el recurs que volem. Amb açò "f" ja està preparat per a ser tornar.
// Ojo, realment estic tornant un FILE* al arxiu de recursos, pero ja apuntant al moment en que comença el recurs que volem.
// Ho dic perque si fem fseek(f, 0, SEEK_SET) tornarà al principi de l'arxiu de recursos, no del recurs. Tindre-ho en compte.
fseek(f, toc[count].offset, SEEK_SET);
}
else
{
// Si estem pillant els recursos de carpeta, simplement obrim el arxiu en questió i tornem el FILE* associat.
f = fopen((resource_folder + resourcename).c_str(), binary ? "rb" : "r");
if(!f)
{
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu '%s/%s'\n", resource_folder.c_str(), resourcename.c_str());
exit(1);
}
fseek(f, 0, SEEK_END);
if (filesize) *filesize = ftell(f);
fseek(f, 0, SEEK_SET);
}
// Tornar el punter FILE* al arxiu. OJO! Tenim que tancar-lo quan acabem amb ell
return f;
}
// Obté un buffer de memòria en format "char*" del arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
char *getFileBuffer(const std::string resourcename, int *filesize)
{
int size;
// Usem la funció anterior per a obtinde un FILE*, independentment de on pillem els recursos
FILE *f = getFilePointer(resourcename, &size, true);
// Reservem memòria per al buffer
char *buffer = (char *)malloc(size);
// llegim el contingut del arxiu i el fiquem en el buffer
fread(buffer, size, 1, f);
// Tanquem l'arxiu
fclose(f);
if (filesize) *filesize = size;
// Tornem el buffer. OJO! recordar alliberar-lo amb free(buffer) quan acabem amb ell.
return buffer;
}
// Estableix el nom de la carpeta on es guardarà la configuració
// Adaptat del codi que va escriure JailDesigner en el JailDoctor's Dilemma
// Vull revisar-la tranquilament algun dia
void setConfigFolder(const std::string foldername)
{
#ifdef _WIN32 #ifdef _WIN32
config_folder = std::string(getenv("APPDATA")) + "/" + foldername; config_folder = std::string(getenv("APPDATA")) + "/" + foldername;
#elif __APPLE__ #elif __APPLE__
struct passwd *pw = getpwuid(getuid()); struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir; const char *homedir = pw->pw_dir;
config_folder = std::string(homedir) + "/Library/Application Support/" + foldername; config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
#elif __linux__ #elif __linux__
struct passwd *pw = getpwuid(getuid()); struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir; const char *homedir = pw->pw_dir;
config_folder = std::string(homedir) + "/." + foldername; config_folder = std::string(homedir) + "/." + foldername;
#endif #endif
struct stat st = {0}; struct stat st = {0};
if (stat(config_folder.c_str(), &st) == -1) if (stat(config_folder.c_str(), &st) == -1)
{ {
#ifdef _WIN32 #ifdef _WIN32
int ret = mkdir(config_folder.c_str()); int ret = mkdir(config_folder.c_str());
#else #else
int ret = mkdir(config_folder.c_str(), S_IRWXU); int ret = mkdir(config_folder.c_str(), S_IRWXU);
#endif #endif
if (ret == -1) if (ret == -1)
{
printf("ERROR CREATING CONFIG FOLDER.");
exit(EXIT_FAILURE);
}
}
}
// Obté el nom de la carpeta on es guardarà la configuració
const std::string getConfigFolder()
{
return config_folder + "/";
}
// Carrega tots els valors guardats a l'arxiu de recursos
void loadConfigValues()
{
// Buidem la taula de claus-valors en memòria
config.clear();
// Obrim l'arxiu de configuració
std::string config_file = config_folder + "/config.txt";
FILE *f = fopen(config_file.c_str(), "r");
if (f)
{ {
// Agafem linea a linea printf("ERROR CREATING CONFIG FOLDER.");
char line[1024]; exit(EXIT_FAILURE);
while (fgets(line, sizeof(line), f))
{
// Separem la clau del valor
char *value = strchr(line, '=');
if (value)
{
*value = '\0';
value++;
value[strlen(value) - 1] = '\0';
// i els afegim a la taula de claus-valors en memòria
config.push_back({line, value});
}
}
// tanquem la paradeta
fclose(f);
} }
} }
}
// Guardem tots els valors en la taula de claus-valors a l'arxiu de configuració
void saveConfigValues() const char *file_getconfigfolder() {
{ std::string folder = config_folder + "/";
// Obrim l'arxiu de configuració return folder.c_str();
std::string config_file = config_folder + "/config.txt"; }
FILE *f = fopen(config_file.c_str(), "w");
if (f) void file_loadconfigvalues() {
{ config.clear();
// Guardem cada parella clau/valor de la taula en memòria en una linea en format "clau=valor\n" en l'arxiu de configuració std::string config_file = config_folder + "/config.txt";
for (auto pair : config) FILE *f = fopen(config_file.c_str(), "r");
{ if (!f) return;
fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str());
} char line[1024];
while (fgets(line, sizeof(line), f)) {
// tanquem la paradeta char *value = strchr(line, '=');
fclose(f); if (value) {
} *value='\0'; value++;
} value[strlen(value)-1] = '\0';
config.push_back({line, value});
// Obté un valor de l'arxiu de configuració per a la clau donada (o cadena buida si no existeix) }
const std::string getConfigValue(const std::string key) }
{ fclose(f);
// Si la taula de claus-valors esta buida, la carreguem de l'arxiu de configuració }
if (config.empty())
{ void file_saveconfigvalues() {
loadConfigValues(); std::string config_file = config_folder + "/config.txt";
} FILE *f = fopen(config_file.c_str(), "w");
if (f) {
// busquem la clau en la taula for (auto pair : config) {
for (auto pair : config) fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str());
{ }
if (pair.key == std::string(key)) fclose(f);
{ }
// Si la trobem, tornem el seu valor }
return pair.value;
} const char* file_getconfigvalue(const char *key) {
} if (config.empty()) file_loadconfigvalues();
for (auto pair : config) {
// Si no la trobem, tornem cadena buida if (pair.key == std::string(key)) {
return ""; strcpy(scratch, pair.value.c_str());
} return scratch;
}
// Estableix un valor en l'arxiu de configuració per a la clau donada }
void setConfigValue(const std::string key, const std::string value) return NULL;
{ }
// Si la taula de claus-valors esta buida, la carreguem de l'arxiu de configuració
if (config.empty()) void file_setconfigvalue(const char* key, const char* value) {
{ if (config.empty()) file_loadconfigvalues();
loadConfigValues(); for (auto &pair : config) {
} if (pair.key == std::string(key)) {
pair.value = value;
// busquem la clau en la taula file_saveconfigvalues();
for (auto &pair : config) return;
{ }
if (pair.key == std::string(key)) }
{ config.push_back({key, value});
// Si la trobem, actualitzem el seu valor i guardem els canvis a l'arxiu de configuració file_saveconfigvalues();
pair.value = value; return;
saveConfigValues();
return;
}
}
// Si NO la trobem, afegim la nova clau i el seu valor, i guardem els canvis a l'arxiu de configuració
config.push_back({key, value});
saveConfigValues();
}
} }

View File

@@ -1,68 +1,18 @@
#pragma once #pragma once
#include <stdio.h> #include <stdio.h>
#include <fstream>
#include <string>
#define SOURCE_FILE 0 #define SOURCE_FILE 0
#define SOURCE_FOLDER 1 #define SOURCE_FOLDER 1
// Unitat per a la gestió de l'acces a arxius void file_setconfigfolder(const char *foldername);
namespace file const char *file_getconfigfolder();
{
// Funcions d'acces als recursos (gràfics, musiques...)
// -----------------------------------------------------------------------------------------------------------
/// @brief Estableix el nom de l'arxiu on estàn guardats els recursos (es "data.jf2" per defecte) void file_setresourcefilename(const char *str);
/// @param str nom de l'arxiu de recursos void file_setresourcefolder(const char *str);
void setResourceFilename(const std::string str); void file_setsource(const int src);
/// @brief Estableix el nom de la carpeta on estàn guardats els recursos (es "data" per defecte) FILE *file_getfilepointer(const char *resourcename, int& filesize, const bool binary=false);
/// @param str nom de la carpeta de recursos char *file_getfilebuffer(const char *resourcename, int& filesize);
void setResourceFolder(const std::string str);
/// @brief Estableix d'on s'han de obtenir els recursos (arxius individuals dins d'una carpeta o arxiu de recursos) const char* file_getconfigvalue(const char *key);
/// @param src SOURCE_FILE o SOURCE_FOLDER, si es vol que se pillen recursos de arxiu o de carpeta void file_setconfigvalue(const char* key, const char* value);
void setSource(const int src);
/// @brief Obté un "std::ifstream" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos). Recordar tancar-lo al acabar amb ell.
/// @param resourcename el nom de l'arxiu que volem obrir
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
/// @param binary si volem obrir el arxiu en format binary
/// @return un std::ifstream al arxiu
std::ifstream getFileStream(const std::string resourcename, int *filesize = nullptr, const bool binary = false);
/// @brief Obté un "FILE*" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos). Recordar tancar-lo al acabar amb ell.
/// @param resourcename el nom de l'arxiu que volem obrir
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
/// @param binary si volem obrir el arxiu en format binary
/// @return un punter FILE* al arxiu
FILE *getFilePointer(const std::string resourcename, int *filesize = nullptr, const bool binary = false);
/// @brief Obté un buffer de memòria en format "char*" del arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos).
/// @brief Recordar alliberar la memoria del buffer amb free(buffer) al acabar amb ell.
/// @param resourcename el nom de l'arxiu del que volem obrindre el buffer
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
/// @return un array de "filesize" bytes amb el contingut del arxiu
char *getFileBuffer(const std::string resourcename, int *filesize = nullptr);
// Funcions d'access a la configuració (clau = valor)
// -----------------------------------------------------------------------------------------------------------
/// @brief Estableix el nom de la carpeta on es guardarà la configuració
/// @param foldername nom de la carpeta
void setConfigFolder(const std::string foldername);
/// @brief Obté el nom de la carpeta on es guardarà la configuració
/// @return nom de la carpeta
const std::string getConfigFolder();
/// @brief Obté un valor de l'arxiu de configuració per a la clau donada (o cadena buida si no existeix)
/// @param key clau de la que obtindre el valor
/// @return el valor de la clau especificada
const std::string getConfigValue(const std::string key);
/// @brief Estableix un valor en l'arxiu de configuració per a la clau donada
/// @param key clau a la que establir un valor
/// @param value valor a establir per a la clau
void setConfigValue(const std::string key, const std::string value);
}

View File

@@ -1,7 +1,7 @@
#include "jgame.h" #include "jgame.h"
#include "jdraw.h" #include "jdraw.h"
#include "jinput.h" #include "jinput.h"
#include <SDL2/SDL.h> #include <SDL3/SDL.h>
#include <map> #include <map>
#ifdef MACOS_BUNDLE #ifdef MACOS_BUNDLE
@@ -49,9 +49,11 @@ int main(int argc, char *argv[])
char res_file[255] = ""; char res_file[255] = "";
strcpy(res_file, dirname(argv[0])); strcpy(res_file, dirname(argv[0]));
strcat(res_file, "/../Resources/data.jf2"); strcat(res_file, "/../Resources/data.jf2");
file::setResourceFilename(res_file); file_setresourcefilename(res_file);
#endif #endif
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
game::windowHasFocus = true; game::windowHasFocus = true;
game::init(); game::init();
input::init(draw::getZoom()); input::init(draw::getZoom());
@@ -64,26 +66,34 @@ int main(int argc, char *argv[])
{ {
while(SDL_PollEvent(&e)) while(SDL_PollEvent(&e))
{ {
if (e.type==SDL_QUIT) { should_exit = true; break; } if (e.type==SDL_EVENT_QUIT)
if (e.type==SDL_KEYDOWN)
{ {
input::updateKey(e.key.keysym.scancode); should_exit = true;
break;
} }
if (e.type==SDL_KEYUP) if (e.type==SDL_EVENT_KEY_DOWN)
{ {
input::updateKeypressed(e.key.keysym.scancode); input::updateKey(e.key.scancode);
} }
if (e.type==SDL_MOUSEBUTTONUP) if (e.type==SDL_EVENT_KEY_UP)
{
input::updateKeypressed(e.key.scancode);
}
if (e.type==SDL_EVENT_MOUSE_BUTTON_UP)
{ {
input::updateClk(e.button.button); input::updateClk(e.button.button);
} }
if (e.type==SDL_MOUSEWHEEL) if (e.type==SDL_EVENT_MOUSE_WHEEL)
{ {
input::updateWheel(e.wheel.y); input::updateWheel(e.wheel.y);
} }
if ( e.type == SDL_WINDOWEVENT ) { if ( e.type == SDL_EVENT_WINDOW_FOCUS_GAINED )
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) game::windowHasFocus = true; {
if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST) game::windowHasFocus = false; game::windowHasFocus = true;
}
if ( e.type == SDL_EVENT_WINDOW_FOCUS_LOST )
{
game::windowHasFocus = false;
} }
} }

View File

@@ -2,7 +2,7 @@
#include "jdraw.h" #include "jdraw.h"
#include "jinput.h" #include "jinput.h"
#include "jaudio.h" #include "jail_audio.h"
#include "jfile.h" #include "jfile.h"
namespace game namespace game
{ {

View File

@@ -1,10 +1,10 @@
#include "jinput.h" #include "jinput.h"
#include <SDL2/SDL.h> #include <SDL3/SDL.h>
#include "jdraw.h" #include "jdraw.h"
namespace input namespace input
{ {
static const uint8_t *keys = nullptr; static const bool *keys = nullptr;
static uint8_t keypressed = 0; static uint8_t keypressed = 0;
static uint8_t keydown = 0; static uint8_t keydown = 0;
static uint8_t btnClicked = 0; static uint8_t btnClicked = 0;
@@ -79,7 +79,7 @@ namespace input
// Torna la posició X actual del ratolí // Torna la posició X actual del ratolí
const int mouseX() const int mouseX()
{ {
int x; float x;
SDL_GetMouseState(&x, NULL); SDL_GetMouseState(&x, NULL);
return x/screen_zoom; return x/screen_zoom;
} }
@@ -87,7 +87,7 @@ namespace input
// Torna la posició Y actual del ratolí // Torna la posició Y actual del ratolí
const int mouseY() const int mouseY()
{ {
int y; float y;
SDL_GetMouseState(NULL, &y); SDL_GetMouseState(NULL, &y);
return y/screen_zoom; return y/screen_zoom;
} }
@@ -95,7 +95,7 @@ namespace input
// Determina si el botó del ratolí especificat està sent polsada ara mateix // Determina si el botó del ratolí especificat està sent polsada ara mateix
const bool mouseBtn(const int btn) const bool mouseBtn(const int btn)
{ {
return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(btn)); return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MASK(btn));
} }
// Determina si el botó especificat ha sigut polsat, pero no tornarà a ser true fins // Determina si el botó especificat ha sigut polsat, pero no tornarà a ser true fins

View File

@@ -8,7 +8,7 @@ void game::init()
draw::setTrans(0); draw::setTrans(0);
input::init(3); input::init(3);
audio::init(); JA_Init(48000, SDL_AUDIO_S16, 2);
font::init(); font::init();

5584
source/stb_vorbis.h Normal file

File diff suppressed because it is too large Load Diff