- [FIX] Llevat tot el relacionat amb codi en C/C++

- [NEW] Fulla d'sprites de Batman
- [NEW] Codi base de mini en Lua
- [WIP] Treballant en el editor
- [NEW] Fulla bàsica de tiles
- [NEW] Afegida paleta actual
This commit is contained in:
2025-11-05 13:11:41 +01:00
parent 72b92ca644
commit 47fb840d80
21 changed files with 240 additions and 8454 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
*.exe *.exe
tramussos tramussos
tramussos_debug tramussos_debug
mini_debug
./*.dll ./*.dll
release/* release/*
*.dSYM/* *.dSYM/*

BIN
data/batman.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

120
data/editor.lua Normal file
View File

@@ -0,0 +1,120 @@
editor = {
num = 1,
map_surf = {nil,nil},
current_layer = 1,
current_tile = 1,
ox = 0,
oy = 0,
init = function(level_name)
game_update = editor.update
editor.new()
--if level_name then
-- editor.current_layer = 2
--end
end,
new = function()
editor.num = 1
editor.map_surf[1] = surf.new(256,256)
editor.map_surf[2] = surf.new(256,256)
editor.current_layer = 1
editor.current_tile = 1
editor.ox = 0
editor.oy = 0
end,
save = function()
local base_name = "level" + tostring(editor.num)
end,
update = function()
view.origin(editor.ox,editor.oy)
surf.cls(66)
pal.trans(0)
surf.source(tiles)
map.surf(editor.map_surf[1])
map.draw()
map.surf(editor.map_surf[2])
map.draw()
map.surf(editor.map_surf[editor.current_layer])
local mx,my = mouse.pos()
local tx = (mx//8)*8
local ty = (my//8)*8
draw.rect(tx-1,ty-1,10, 10, 9)
if key.press(key.TAB) then
game_update = editor.tile_picker_update
elseif key.press(key.RIGHT) then
editor.ox = editor.ox - 8
elseif key.press(key.LEFT) then
editor.ox = editor.ox + 8
elseif key.press(key.DOWN) then
editor.oy = editor.oy - 8
elseif key.press(key.UP) then
editor.oy = editor.oy + 8
elseif key.press(key.N1) then
editor.current_layer=1
elseif key.press(key.N2) then
editor.current_layer=2
elseif key.press(key.N2) then
editor.current_layer=2
elseif key.press(key.F) then
editor.fill(tx//8, ty//8, map.tile(mx/8, my/8))
end
if mouse.down(mouse.LEFT) then
map.tile(mx/8, my/8, editor.current_tile)
elseif mouse.press(mouse.RIGHT) then
editor.current_tile = map.tile(mx/8, my/8)
end
view.origin(0,0)
text(tostring(editor.current_layer),0,0,22,42)
end,
fill = function(x, y, tile)
local stack = {}
table.insert(stack, {x = x, y = y})
while #stack > 0 do
local mw, mh = surf.size(editor.map_surf[1])
local pos = table.remove(stack)
local px, py = pos.x, pos.y
map.tile(px, py, editor.current_tile)
if px<mw-1 and map.tile(px+1, py) == tile then table.insert(stack, {x = px + 1, y = py}) end
if px>0 and map.tile(px-1, py) == tile then table.insert(stack, {x = px - 1, y = py}) end
if py<mh-1 and map.tile(px, py+1) == tile then table.insert(stack, {x = px, y = py + 1}) end
if py>0 and map.tile(px, py-1) == tile then table.insert(stack, {x = px, y = py - 1}) end
end
end,
tile_picker_update = function()
view.origin(0,0)
surf.cls(66)
pal.trans(255)
surf.source(tiles)
local sw,sh = surf.size(tiles)
draw.surf(0,0,sw,sh,0,0)
if key.press(key.ESCAPE) then
game_update = editor.update
end
local mx,my = mouse.pos()
local tx = (mx//8)*8
local ty = (my//8)*8
draw.rect(tx-1,ty-1,10, 10, 9)
if mouse.press(mouse.LEFT) then
editor.current_tile = (tx//8) + (ty//8) * (sw//8)
game_update = editor.update
end
end
}

5
data/game.ini Normal file
View File

@@ -0,0 +1,5 @@
title=Tramussos
config=tramussos
width=128
height=96
zoom=5

44
data/main.lua Normal file
View File

@@ -0,0 +1,44 @@
require "editor"
function mini.init()
sprites=surf.load("batman.gif")
tiles=surf.load("tiles01.gif")
surf.source(tiles)
local paleta=pal.load("tiles01.gif")
pal.set(paleta)
editor.init(1);
end
function mini.update()
if key.press(key.F1) then
win.zoom(win.zoom()-1)
elseif key.press(key.F2) then
win.zoom(win.zoom()+1)
elseif key.press(key.F3) then
local fs = win.fullscreen()
win.fullscreen(not fs)
win.cursor(fs)
end
if (game_update) then game_update() end
--text(tostring(sys.fps()),0,0,22,42)
end
function text(str,x,y,col,col2)
draw.text(str,x-1,y-1,66)
draw.text(str,x,y-1,66)
draw.text(str,x+1,y-1,66)
draw.text(str,x-1,y,66)
draw.text(str,x+1,y,66)
draw.text(str,x-1,y+1,66)
draw.text(str,x+1,y+1,66)
draw.text(str,x-1,y+2,66)
draw.text(str,x,y+2,66)
draw.text(str,x+1,y+2,66)
draw.text(str,x,y+1,col2)
draw.text(str,x,y,col)
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 B

After

Width:  |  Height:  |  Size: 884 B

70
data/tramussos.pal Normal file
View File

@@ -0,0 +1,70 @@
JASC-PAL
0100
67
255 6 255
20 16 19
59 23 37
115 23 45
180 32 42
223 62 35
250 106 10
249 163 27
255 213 65
255 252 64
214 242 100
156 219 67
89 193 53
20 160 46
26 122 62
36 82 59
18 32 32
20 52 100
40 92 196
36 159 222
32 214 199
166 252 219
255 255 255
254 243 192
250 214 184
245 160 151
232 106 115
188 74 155
121 58 128
64 51 83
36 34 52
34 28 26
50 43 40
113 65 59
187 117 71
219 164 99
244 210 156
218 224 234
179 185 209
139 147 175
109 117 141
74 84 98
51 57 65
66 36 51
91 49 56
142 82 82
186 117 106
233 181 163
227 230 255
185 191 251
132 155 228
88 141 190
71 125 133
35 103 78
50 132 100
93 175 141
146 220 186
205 247 226
228 210 170
199 176 139
160 134 98
121 103 85
90 78 68
66 57 52
10 25 48
6 10 10
0 0 0

View File

@@ -1,493 +0,0 @@
#include "audio.h"
#include "file.h"
#include "stb_vorbis.h"
#include <SDL3/SDL.h>
#include <stdio.h>
namespace audio {
#define MAX_CHANNELS 5
struct sound
{
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
Uint32 length { 0 };
Uint8 *buffer { NULL };
};
struct channel
{
audio::sound *sound { nullptr };
int pos { 0 };
int times { 0 };
SDL_AudioStream *stream { nullptr };
audio::state state { audio::state::available };
};
struct music
{
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 };
audio::state state { audio::state::invalid };
};
audio::music *current_music { nullptr };
audio::channel channels[MAX_CHANNELS];
SDL_AudioSpec audio_spec { SDL_AUDIO_S16, 2, 48000 };
float music_volume { 1.0f };
float sound_volume { 0.5f };
bool music_enabled { true };
bool sound_enabled { true };
SDL_AudioDeviceID audio_device { 0 };
SDL_TimerID timer_id { 0 };
bool fading { false };
int fade_start_time;
int fade_duration;
int fade_initial_volume;
Uint32 updateCallback(void *userdata, SDL_TimerID timer_id, Uint32 interval)
{
if (music_enabled && current_music && current_music->state == audio::state::playing)
{
if (fading)
{
const int time = SDL_GetTicks();
if (time > (fade_start_time+fade_duration)) {
fading = false;
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, music_volume*(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) stopMusic();
}
}
if (sound_enabled)
{
for (int i=0; i < MAX_CHANNELS; ++i)
if (channels[i].state == audio::state::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) stopChannel(i);
}
}
}
return 30;
}
void 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...");
audio_spec = {format, num_channels, freq };
if (!audio_device) SDL_CloseAudioDevice(audio_device);
audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_spec);
SDL_Log( (audio_device==0) ? "Failed to initialize SDL audio!\n" : "OK!\n");
for (int i=0;i<MAX_CHANNELS;++i) channels[i].state = audio::state::available;
timer_id = SDL_AddTimer(30, updateCallback, nullptr);
}
void quit()
{
if (timer_id) SDL_RemoveTimer(timer_id);
if (!audio_device) SDL_CloseAudioDevice(audio_device);
audio_device = 0;
}
audio::music *loadMusic(Uint8* buffer, Uint32 length)
{
audio::music *music = new audio::music();
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 = audio::state::stopped;
return music;
}
audio::music *loadMusic(const char* filename)
{
int size;
char *buffer = file::getFileBuffer(filename, size);
if (buffer == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadMusic): No s'ha trobat l'arxiu %s\n", filename);
exit(1);
}
audio::music *music = loadMusic((Uint8*)buffer, size);
music->filename = (char*)malloc(strlen(filename)+1);
strcpy(music->filename, filename);
free(buffer);
return music;
}
void playMusic(audio::music *music, const int loop)
{
if (!music_enabled) return;
stopMusic();
current_music = music;
current_music->pos = 0;
current_music->state = audio::state::playing;
current_music->times = loop;
current_music->stream = SDL_CreateAudioStream(&current_music->spec, &audio_spec);
if (!current_music->stream) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_CreateAudioStream: %s\n", SDL_GetError());
exit(1);
}
if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_PutAudioStreamData: %s\n", SDL_GetError());
exit(1);
}
SDL_SetAudioStreamGain(current_music->stream, music_volume);
if (!SDL_BindAudioStream(audio_device, current_music->stream)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_BindAudioStream: %s\n", SDL_GetError());
exit(1);
}
}
void loadAndPlayMusic(const char* filename, const bool force_reinit)
{
if ( (getMusicState() != audio::state::playing) || (strcmp(getMusicFilename(), filename)!=0) || force_reinit )
{
playMusic(loadMusic(filename));
}
}
char *getMusicFilename(audio::music *music)
{
if (!music) music = current_music;
return music->filename;
}
void pauseMusic()
{
if (!music_enabled) return;
if (!current_music || current_music->state == audio::state::invalid) return;
current_music->state = audio::state::paused;
SDL_UnbindAudioStream(current_music->stream);
}
void resumeMusic()
{
if (!music_enabled) return;
if (!current_music || current_music->state == audio::state::invalid) return;
current_music->state = audio::state::playing;
SDL_BindAudioStream(audio_device, current_music->stream);
}
void stopMusic()
{
if (!music_enabled) return;
if (!current_music || current_music->state == audio::state::invalid) return;
current_music->pos = 0;
current_music->state = audio::state::stopped;
SDL_DestroyAudioStream(current_music->stream);
current_music->stream = nullptr;
free(current_music->filename);
current_music->filename = nullptr;
}
void fadeOutMusic(const int milliseconds)
{
if (!music_enabled) return;
if (current_music == NULL || current_music->state == audio::state::invalid) return;
fading = true;
fade_start_time = SDL_GetTicks();
fade_duration = milliseconds;
fade_initial_volume = music_volume;
}
audio::state getMusicState()
{
if (!music_enabled) return audio::state::disabled;
if (!current_music) return audio::state::invalid;
return current_music->state;
}
void deleteMusic(audio::music *music)
{
if (current_music == music) current_music = nullptr;
SDL_free(music->buffer);
if (music->stream) SDL_DestroyAudioStream(music->stream);
delete music;
}
float setMusicVolume(float volume)
{
music_volume = SDL_clamp( volume, 0.0f, 1.0f );
if (current_music) SDL_SetAudioStreamGain(current_music->stream, music_volume);
return music_volume;
}
void setMusicPosition(float value)
{
if (!current_music) return;
current_music->pos = value * current_music->spec.freq;
}
float getMusicPosition()
{
if (!current_music) return 0;
return float(current_music->pos)/float(current_music->spec.freq);
}
void enableMusic(const bool value)
{
if ( !value && current_music && (current_music->state==audio::state::playing) ) stopMusic();
music_enabled = value;
}
audio::sound *newSound(Uint8* buffer, Uint32 length)
{
audio::sound *sound = new audio::sound();
sound->buffer = buffer;
sound->length = length;
return sound;
}
audio::sound *loadSound(uint8_t* buffer, uint32_t size)
{
audio::sound *sound = new audio::sound();
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &sound->spec, &sound->buffer, &sound->length);
return sound;
}
audio::sound *loadSound(const char* filename)
{
int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
if (buffer == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadSound): No s'ha trobat l'arxiu %s\n", filename);
exit(1);
}
audio::sound *sound = loadSound(buffer, size);
free(buffer);
return sound;
}
int playSound(audio::sound *sound, const int loop)
{
if (!sound_enabled) return -1;
int channel = 0;
while (channel < MAX_CHANNELS && channels[channel].state != audio::state::available) { channel++; }
if (channel == MAX_CHANNELS) channel = 0;
stopChannel(channel);
channels[channel].sound = sound;
channels[channel].times = loop;
channels[channel].pos = 0;
channels[channel].state = audio::state::playing;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &audio_spec);
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
SDL_SetAudioStreamGain(channels[channel].stream, sound_volume);
SDL_BindAudioStream(audio_device, channels[channel].stream);
return channel;
}
int playSoundOnChannel(audio::sound *sound, const int channel, const int loop)
{
if (!sound_enabled) return -1;
if (channel < 0 || channel >= MAX_CHANNELS) return -1;
stopChannel(channel);
channels[channel].sound = sound;
channels[channel].times = loop;
channels[channel].pos = 0;
channels[channel].state = audio::state::playing;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &audio_spec);
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
SDL_SetAudioStreamGain(channels[channel].stream, sound_volume);
SDL_BindAudioStream(audio_device, channels[channel].stream);
return channel;
}
void deleteSound(audio::sound *sound)
{
for (int i = 0; i < MAX_CHANNELS; i++) {
if (channels[i].sound == sound) stopChannel(i);
}
SDL_free(sound->buffer);
delete sound;
}
void pauseChannel(const int channel)
{
if (!sound_enabled) return;
if (channel == -1)
{
for (int i = 0; i < MAX_CHANNELS; i++)
if (channels[i].state == audio::state::playing)
{
channels[i].state = audio::state::paused;
SDL_UnbindAudioStream(channels[i].stream);
}
}
else if (channel >= 0 && channel < MAX_CHANNELS)
{
if (channels[channel].state == audio::state::playing)
{
channels[channel].state = audio::state::paused;
SDL_UnbindAudioStream(channels[channel].stream);
}
}
}
void resumeChannel(const int channel)
{
if (!sound_enabled) return;
if (channel == -1)
{
for (int i = 0; i < MAX_CHANNELS; i++)
if (channels[i].state == audio::state::paused)
{
channels[i].state = audio::state::playing;
SDL_BindAudioStream(audio_device, channels[i].stream);
}
}
else if (channel >= 0 && channel < MAX_CHANNELS)
{
if (channels[channel].state == audio::state::paused)
{
channels[channel].state = audio::state::playing;
SDL_BindAudioStream(audio_device, channels[channel].stream);
}
}
}
void stopChannel(const int channel)
{
if (!sound_enabled) return;
if (channel == -1)
{
for (int i = 0; i < MAX_CHANNELS; i++) {
if (channels[i].state != audio::state::available) SDL_DestroyAudioStream(channels[i].stream);
channels[i].stream = nullptr;
channels[i].state = audio::state::available;
channels[i].pos = 0;
channels[i].sound = NULL;
}
}
else if (channel >= 0 && channel < MAX_CHANNELS)
{
if (channels[channel].state != audio::state::available) SDL_DestroyAudioStream(channels[channel].stream);
channels[channel].stream = nullptr;
channels[channel].state = audio::state::available;
channels[channel].pos = 0;
channels[channel].sound = NULL;
}
}
audio::state getChannelState(const int channel)
{
if (!sound_enabled) return audio::state::disabled;
if (channel < 0 || channel >= MAX_CHANNELS) return audio::state::invalid;
return channels[channel].state;
}
float setSoundVolume(float volume)
{
sound_volume = SDL_clamp( volume, 0.0f, 1.0f );
for (int i = 0; i < MAX_CHANNELS; i++)
if ( (channels[i].state == audio::state::playing) || (channels[i].state == audio::state::paused) )
SDL_SetAudioStreamGain(channels[i].stream, sound_volume);
return sound_volume;
}
void enableSound(const bool value)
{
for (int i = 0; i < MAX_CHANNELS; i++)
{
if (channels[i].state == audio::state::playing) stopChannel(i);
}
sound_enabled = value;
}
float setVolume(float volume)
{
setSoundVolume(setMusicVolume(volume) / 2.0f);
return music_volume;
}
#undef MAX_CHANNELS
}

View File

@@ -1,44 +0,0 @@
#pragma once
#include <SDL3/SDL.h>
namespace audio
{
enum state { invalid, available, playing, paused, stopped, disabled };
struct sound;
struct music;
void init(const int freq, const SDL_AudioFormat format, const int num_channels);
void quit();
audio::music *loadMusic(const char* filename);
audio::music *loadMusic(Uint8* buffer, Uint32 length);
void playMusic(audio::music *music, const int loop = -1);
void loadAndPlayMusic(const char* filename, const bool force_reinit=false);
char *getMusicFilename(audio::music *music = nullptr);
void pauseMusic();
void resumeMusic();
void stopMusic();
void fadeOutMusic(const int milliseconds=250);
audio::state getMusicState();
void deleteMusic(audio::music *music);
float setMusicVolume(float volume);
void setMusicPosition(float value);
float getMusicPosition();
void enableMusic(const bool value);
audio::sound *newSound(Uint8* buffer, Uint32 length);
audio::sound *loadSound(Uint8* buffer, Uint32 length);
audio::sound *loadSound(const char* filename);
int playSound(audio::sound *sound, const int loop = 0);
int playSoundOnChannel(audio::sound *sound, const int channel, const int loop = 0);
void pauseChannel(const int channel);
void resumeChannel(const int channel);
void stopChannel(const int channel);
audio::state getChannelState(const int channel);
void deleteSound(audio::sound *sound);
float setSoundVolume(float volume);
void enableSound(const bool value);
float setVolume(float volume);
}

View File

@@ -1,715 +0,0 @@
#include "draw.h"
#include <SDL3/SDL.h>
#include "gif.h"
#include "file.h"
#include "shader.h"
namespace draw
{
// La idea de esta unitat es usar "superficies", que no son mes que arrays de bytes, per a anar pintant.
// El resultat final s'ha de pintar en algun moment a la superficie "screen" (o siga, especificar nullptr en setDestination)
// Aleshores, en "render" el contingut de screen se volca a la textura SDL que crearem,
// i eixa textura se pintarà a pantalla com se sol fer amb SDL. Ho anirem veient en el codi.
SDL_Window *sdl_window {nullptr}; // La finestra de SDL
SDL_Renderer *sdl_renderer {nullptr}; // El renderer de SDL
SDL_Texture *sdl_texture {nullptr}; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla
SDL_Texture *sdl_shadertex {nullptr}; // La textura de SDL per al shader
static int screen_zoom = 1;
static bool screen_fullscreen = false;
static bool screen_cursor = true;
static char* screen_shader = nullptr;
static bool shader_enabled = false;
static float window_ratio = 1;
static int canvas_width;
static int canvas_height;
static int desktop_width;
static int desktop_height;
static int window_width;
static int window_height;
static int offset_x = 0;
static int offset_y = 0;
char window_title[256];
surface *screen {nullptr}; // La superficie screen, que representa la pantalla. Se crea i destrueix internament
surface *destination {nullptr}; // Punter a la actual superficie de destí
surface *source {nullptr}; // Punter a la actual superficie d'oritge
surface *pushed_source {nullptr}; // Punter a la superficie d'oritge que s'ha pushat
uint32_t palette[256]; // La paleta de colors
uint32_t aux_palette[256]; // La paleta de colors, para els fadein
uint8_t color_indices[256]; // Indices dels colors per defecte
uint8_t sel_color {0}; // Color seleccionat per defecte
uint8_t transparent {0}; // El color transparent
//surface *textsurf = nullptr; // Surface on guardar el gif amb la font
SDL_Rect viewport;
bool fading_out = false;
bool fading_in = false;
void createDisplay()
{
// Ajustem el zoom i el ratio, per si tenen valors locs
if (screen_zoom <= 0) screen_zoom = 1;
if (window_ratio <= 0) window_ratio = 1;
// Ajustem el tamany de la finestra, segons el zoom i el ratio
window_width = canvas_width*screen_zoom;
window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom;
// Mentres no càpiga en la pantalla, reduïm el zoom
while (window_width > desktop_width || window_height > desktop_height) {
screen_zoom--;
window_width = canvas_width*screen_zoom;
window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom;
}
if (screen_fullscreen) {
if (desktop_width * window_ratio > desktop_height) {
offset_y = 0;
window_height = desktop_height;
window_width = desktop_height/window_ratio;
offset_x = (desktop_width - window_width)/2;
} else {
offset_x = 0;
window_width = desktop_width;
window_height = desktop_width*window_ratio;
offset_y = (desktop_height - window_height)/2;
}
} else {
offset_x = offset_y = 0;
}
sdl_window = SDL_CreateWindow(window_title, window_width, window_height, SDL_WINDOW_OPENGL|(screen_fullscreen?SDL_WINDOW_FULLSCREEN:0));
if (!sdl_window) {
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
exit(1);
}
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
if (!sdl_renderer) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
exit(1);
}
if (screen_cursor) SDL_ShowCursor(); else SDL_HideCursor();
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, canvas_width, canvas_height);
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST);
if (!sdl_texture) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n");
exit(1);
}
sdl_shadertex = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, window_width, window_height);
SDL_SetTextureScaleMode(sdl_shadertex, SDL_SCALEMODE_NEAREST);
loadShader();
}
void destroyDisplay()
{
SDL_DestroyTexture(sdl_texture);
SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(sdl_window);
}
// Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla
void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen, const float ratio)
{
screen_zoom = file::getConfigValueInteger("zoom", zoom);
screen_fullscreen = file::getConfigValueBool("fullscreen", fullscreen);
window_ratio = ratio;
canvas_width = width;
canvas_height = height;
strcpy(window_title, titol);
const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay());
if (!dm)
{
SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError());
exit(1);
}
desktop_width = dm->w;
desktop_height = dm->h;
createDisplay();
// Inicialització de les estructures de SDL
/*
sdl_window = SDL_CreateWindow(titol, width * zoom, height * zoom, 0);
if (!sdl_window) {
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
exit(1);
}
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
if (!sdl_renderer) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
exit(1);
}
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 (draw::init): Failed to initialize texture!\n");
exit(1);
}
*/
// Creem la superficie "screen" i la establim com a superficie destinació
screen = createSurface(width, height);
destination = screen;
viewport.x = viewport.y = 0;
viewport.w = width;
viewport.h = height;
sel_color = transparent = 0;
for (int i=0;i<256;++i) color_indices[i] = i;
//SDL_HideCursor();
//textsurf = loadSurface("font.gif");
}
// Finalització del sistema
void quit()
{
//if (textsurf) freeSurface(textsurf);
// Si la superficie "screen" existia, alliberem la seua memòria
if (screen != nullptr)
{
freeSurface(screen);
}
// Destruim tot el relacionat amb SDL
destroyDisplay();
// Fiquem tots els punters a nullptr, per si de cas no estem eixint del programa
// i anem a tornar a inicialitzar el sistema
sdl_window = nullptr;
sdl_renderer = nullptr;
sdl_texture = nullptr;
screen = destination = source = nullptr;
}
void setZoom(const int value) {
screen_zoom = value;
destroyDisplay();
createDisplay();
file::setConfigValueInteger("zoom", screen_zoom);
}
const int getZoom()
{
return screen_zoom;
}
const float getScaleX()
{
return float(window_width) / float(canvas_width);
}
const float getScaleY()
{
return float(window_height) / float(canvas_height);
}
const int getOffsetX()
{
return offset_x;
}
const int getOffsetY()
{
return offset_y;
}
bool getFullscreen() {
return screen_fullscreen;
}
void setFullscreen(const bool value) {
screen_fullscreen=value;
destroyDisplay();
createDisplay();
file::setConfigValueBool("fullscreen", screen_fullscreen);
}
void loadShader()
{
char *buffer = nullptr;
if (screen_shader) {
int size;
buffer = file::getFileBuffer(screen_shader, size, true);
}
shader::setAspectRatio(3.0f/4.0f);
shader::init(sdl_window, sdl_shadertex, buffer);
if (buffer) free(buffer);
}
void setShader(const char* shader_file)
{
if (screen_shader) free(screen_shader);
screen_shader = (char*)malloc(strlen(shader_file)+1);
strcpy(screen_shader, shader_file);
loadShader();
if (file::getConfigValueBool("shader_enabled", false)) enableShader();
}
void enableShader()
{
shader_enabled = true;
shader::enable();
//destroyDisplay();
//createDisplay();
file::setConfigValueBool("shader_enabled", shader_enabled);
}
void disableShader()
{
shader_enabled = false;
shader::disable();
//destroyDisplay();
//createDisplay();
file::setConfigValueBool("shader_enabled", shader_enabled);
}
void toggleShader()
{
shader_enabled ? disableShader() : enableShader();
}
void hideCursor()
{
screen_cursor = false;
SDL_HideCursor();
}
void showCursor()
{
screen_cursor = true;
SDL_ShowCursor();
}
// Crea una superficie i torna un punter a ella
surface *createSurface(const uint16_t w, const uint16_t h)
{
// Primer reservem memòria per a la estructura "surface"
surface *surf = (surface *)malloc(sizeof(surface));
// Després reservem memòria per als pixels
surf->pixels = (uint8_t *)malloc(w * h);
// I apuntem el ample i alt de la superficie
surf->w = w;
surf->h = h;
// ...i tornem la superficie creada, clar
return surf;
}
// Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
surface *loadSurface(const char *filename, const bool loadPalette)
{
// Agafem un buffer de bytes de l'arxiu especificat
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, 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
if (buffer == nullptr)
{
return nullptr;
}
// Primer reservem memòria per a la estructura "surface"
surface *surf = (surface *)malloc(sizeof(surface));
// Després li passem el buffer de bytes a la funció de carregar un GIF.
// El resultat es un array de bytes, els pixels en sí. Ja havem reservat
// la memòria necessaria en "LoadGif", així que no tenim que fer-ho ara,
// però, ojo, sí que tindrem que alliberar-la.
surf->pixels = LoadGif(buffer, &surf->w, &surf->h);
// Si li havem dit que carregue també la paleta...
if (loadPalette)
{
// Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta
// Van a ser com a molt 256 entrades de 32 bits (pero no sempre), cada entrada es un color, amb el format 0xAARRGGBB
int paletteSize;
uint32_t *pal = LoadPalette(buffer, &paletteSize);
// Copiem eixe array al nostre array de la paleta de sistema. Ara ja tenim la paleta carregada.
for (int i=0;i<256;++i) {
palette[i] = i<paletteSize ? pal[i] : 0;
}
//memset(palette, 0, 1024); // Fiquem tot a 0, que la paleta potser no es de 256 i quedaria basura
//memcpy(palette, pal, paletteSize*4); // 32 bits per entrada == 4 bytes x 'paletteSize' entrades
// Alliberem el array que ens habia tornat LoadPalette()
free(pal);
}
// Com ja no ens fa falta, alliberem la memòria del buffer del arxiu
free(buffer);
// I finalment tornem la superficie
return surf;
}
// Allibera la memòria d'una superficie, els seus pixels inclosos
void freeSurface(surface *surf)
{
// Si la superficie existeix...
if (surf != nullptr)
{
// Si el array de pixels existeix, l'alliberem
if (surf->pixels != nullptr)
{
free(surf->pixels);
}
// ... alliberem la superficie
free(surf);
}
}
// Estableix una superficie com a superficie que rebrà les funcions de pintat (especificar nullptr per a pintar a pantalla)
void setDestination(surface *surf)
{
// Si han especificat nullptr, fiquem "screen" com a destinació
destination = surf == nullptr ? screen : surf;
resetViewport();
}
// Estableix una superficie com a superficie de la que s'agafaràn els gràfics per a pintar
void setSource(surface *surf)
{
// Si han especificat nullptr, fiquem "screen" com a font
source = surf == nullptr ? screen : surf;
}
void pushSource()
{
pushed_source = source;
}
void popSource()
{
source = pushed_source;
}
void setViewport(const int x, const int y, const int w, const int h)
{
viewport.x = x>0?x:0;
viewport.y = y>0?y:0;
viewport.w = w+x<destination->w?w:destination->w;
viewport.h = h+y<destination->h?h:destination->h;
}
void resetViewport()
{
viewport.x = viewport.y = 0;
viewport.w = destination->w;
viewport.h = destination->h;
}
const int getLocalX(const int x)
{
return x - viewport.x;
}
const int getLocalY(const int y)
{
return y - viewport.y;
}
// Carrega la paleta d'un GIF i la torna en un array de uint32_t
uint32_t *loadPalette(const char *filename, int *paletteSize)
{
// Agafem un buffer de bytes de l'arxiu especificat
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
int size;
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
// 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
uint32_t *pal = LoadPalette(buffer);
// I també el buffer del arxiu
free(buffer);
if (paletteSize) *paletteSize = size;
return pal;
}
// Estableix la paleta del sistema, o part de ella, des d'un array especificat
void setPalette(const uint32_t *pal, const int len, const int pos)
{
for (int i=0; i<len; ++i)
{
palette[i+pos] = pal[i];
}
}
//Recupera la paleta del sistema, o part de ella, a un array
uint32_t *getPalette()
{
uint32_t *p = (uint32_t*)malloc(256*sizeof(uint32_t));
for (int i=0; i<256; ++i) p[i] = palette[i];
return p;
}
// Estableix una entrada de la paleta del sistema
void setPaletteEntry(const uint8_t index, const uint8_t r, const uint8_t g, const uint8_t b)
{
palette[index] = (r << 16) + (g << 8) + b;
}
// Esborra la superficie "destination" amb el color especificat
void cls(const uint8_t color)
{
// El tamany es width x height bytes
const int size = destination->w * destination->h;
// Omplim la memòria dels pixels de la superficie de destinació amb "color"
memset(destination->pixels, color, size);
}
//Estableix el color especificat com a transparent
void setTrans(const uint8_t color)
{
transparent = color;
}
// Funció interna per a pintar un pixel d'una superficie sense eixir-se'n de la memòria i petar el mame
void pset(surface *surface, const int x, const int y, const uint8_t color)
{
// Si el color es transparent, eixim, ni ens molestem en mirar res més
if (color == transparent) return;
// Si pintem a "destination", mirem que estiga dins del "viewport" i sinó fora
if (surface == destination) {
if (x+viewport.x >= 0 && y+viewport.y >= 0 && x < viewport.w && y < viewport.h)
surface->pixels[(viewport.x+x) + (y+viewport.y) * surface->w] = color_indices[color];
} else {
// Si no es destinations, mirem que estiga dins de la surface, i sinó fora!
if (x >= 0 && y >= 0 && x < surface->w && y < surface->h)
surface->pixels[x + y * surface->w] = color_indices[color];
}
}
// Funció interna per a llegir un pixel d'una superficie eixir-se'n de la memòria i petar el mame
const uint8_t pget(surface *surface, const int x, const int y)
{
// Si estem llegint de "destination", mirar que estigam llegint dins del viewport
if (surface == destination) {
if (x+viewport.x >= 0 && y+viewport.y >= 0 && x < viewport.w && y < viewport.h)
return surface->pixels[(viewport.x + x) + (viewport.y + y) * surface->w];
} else {
// Si no es "destination", si la coordenada està dins del rang que abarca la superficie,
if (x >= 0 && y >= 0 && x < surface->w && y < surface->h)
return surface->pixels[x + y * surface->w];
}
return 0;
}
void putPixel(const int x, const int y, const uint8_t color)
{
pset(screen, x, y, color);
}
// Pinta un troç de la superficie "source" en la superficie "destination".
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const draw::flip flip)
{
// Si no hi ha superficie d'oritge especificada, no fem res, o petarà el mame
if (source == nullptr)
{
return;
}
// En principi, el quadrat de l'oritge començarà en (sx,sy) i avançarem 1 pixel en positiu tant en x com en y
int sdx = 1, sdy = 1, ssx = sx, ssy = sy;
// Però si s'ha especificat que fem flip en horitzontal...
if (flip & draw::flip::horizontal)
{
sdx = -1; // Avançarem 1 pixel en negatiu
ssx = sx + w - 1; // I començarem al final, o siga, sumarem a sx el ample
}
// De igual forma per al flip en vertical, per a la y
if (flip & draw::flip::vertical)
{
sdy = -1;
ssy = sy + h - 1;
}
// guardem la coordenada d'oritge en x per a restablir-la a cada linea
int csx = ssx;
// Anem linea per linea. Les variables dels dos bucles for controlen les coordenades en la destinació, que sempre van avant.
for (int y = dy; y < dy + h; ++y)
{
ssx = csx; // fiquem la coordenada de l'oritge al principi
// en cada linea, anem pixel a pixel
for (int x = dx; x < dx + w; ++x)
{
pset(destination, x, y, pget(source, ssx, ssy)); // Agafem pixel de l'oritge i el fiquem en la destinació
ssx += sdx; // avancem (o retrocedim) la coordenada x de l'oritge
}
ssy += sdy; // avancem (o retrocedim) la coordenada y de l'oritge
}
}
// Pinta tota la superficie "source" en la superficie "destination", posició (x,y).
void draw(const int x, const int y)
{
draw(x, y, source->w, source->h, 0, 0);
}
// Pinta tota la superficie "source" en la superficie "destination", posició (0,0)
void draw()
{
draw(0,0,source->w, source->h, 0, 0);
}
// Carrega la superficie especificada en "source" i la pinta tota en la superficie "destination", posició (0,0).
void draw(draw::surface* surf)
{
setSource(surf);
draw();
}
void swapcol(const Uint8 c1, const Uint8 c2)
{
color_indices[c1] = c2;
}
void restorecol(const Uint8 c)
{
color_indices[c] = c;
}
void color(const Uint8 col)
{
sel_color = col;
}
void hline(const int x, const int y, const int w)
{
for (int i=x;i<x+w;++i) pset(destination, i, y, sel_color);
}
void vline(const int x, const int y, const int h)
{
for (int i=y;i<y+h;++i) pset(destination, x, i, sel_color);
}
void fillrect(const int x, const int y, const int w, const int h)
{
for (int j=y;j<y+h;++j) for (int i=x;i<x+w;++i) pset(destination, i, j, sel_color);
}
void rect(const int x, const int y, const int w, const int h)
{
hline(x,y,w);
hline(x,y+h-1,w);
vline(x,y,h);
vline(x+w-1,y,h);
}
bool decPalEntry(const uint8_t index, const uint8_t val)
{
const uint32_t entry = palette[index];
uint8_t r = (entry >> 16) & 0xff;
uint8_t g = (entry >> 8) & 0xff;
uint8_t b = entry & 0xff;
r = r>=val ? r-val : 0;
g = g>=val ? g-val : 0;
b = b>=val ? b-val : 0;
palette[index] = (r << 16) + (g << 8) + b;
return palette[index] != 0;
}
bool incPalEntry(const uint8_t index, const uint8_t val)
{
const uint32_t entry = palette[index];
uint8_t r = (entry >> 16) & 0xff;
uint8_t g = (entry >> 8) & 0xff;
uint8_t b = entry & 0xff;
const uint32_t dest_entry = aux_palette[index];
const uint8_t dr = (dest_entry >> 16) & 0xff;
const uint8_t dg = (dest_entry >> 8) & 0xff;
const uint8_t db = dest_entry & 0xff;
r = (r+val > dr) ? dr : r+val;
g = (g+val > dg) ? dg : g+val;
b = (b+val > db) ? db : b+val;
palette[index] = (r << 16) + (g << 8) + b;
return palette[index] != aux_palette[index];
}
void fadein()
{
if (!fading_in) {
for (int i=0;i<256;++i) {
aux_palette[i] = palette[i];
palette[i] = 0;
}
}
fading_in = false;
for (int i=0; i<256; ++i) if (incPalEntry(i, 8)) fading_in = true;
}
void fadeout()
{
fading_out = false;
for (int i=0; i<256; ++i) if (decPalEntry(i, 8)) fading_out = true;
}
bool isfading()
{
return fading_in || fading_out;
}
// Refresca la pantalla
void render()
{
Uint32 *sdl_pixels; // Punter al array de pixels que enstornarà SDL_LockTexture
int sdl_pitch; // Ací estarà guardat el pitch de la textura, com es de 32 bits, no m'afecta
const uint32_t size = screen->w * screen->h; // tamany de la superficie
if (fading_in) fadein();
if (fading_out) fadeout();
// Bloquejem la textura SDL i agafem els seus pixels (son enters de 32 bits amb format 0xAARRGGBB)
SDL_LockTexture(sdl_texture, NULL, (void **)&sdl_pixels, &sdl_pitch);
// Cada pixel de la superficie "screen" es un enter de 8 bits que representa un index en la paleta de colors
// Per tant, per a pintar en la textura SDL, pillem el color de la paleta que correspon al index en "screen"
// i el enviem a la textura SDL
for (uint32_t i = 0; i < size; ++i)
{
sdl_pixels[i] = palette[screen->pixels[i]] | 0xff000000;
}
// Desbloquejem la textura
SDL_UnlockTexture(sdl_texture);
SDL_SetRenderTarget(sdl_renderer, sdl_shadertex);
// Pintem la textura a pantalla
SDL_RenderTexture(sdl_renderer, sdl_texture, NULL, NULL);
// I ho presentem
shader::render();
//SDL_RenderPresent(sdl_renderer);
}
}

View File

@@ -1,154 +0,0 @@
#pragma once
#include <cstdint>
//#define DRAW_FLIP_NONE 0
//#define DRAW_FLIP_HORIZONTAL 1
//#define DRAW_FLIP_VERTICAL 2
//#define DRAW_FLIP_BOTH 3
// Unitat per a la gestió dels recursos gràfics i dibuixat en pantalla
namespace draw
{
enum flip { none, horizontal, vertical, both };
// Estructura per a mantindre una superficie de pintat, la "pantalla virtual" de tota la vida
struct surface
{
uint16_t w; // Ample de la superficie
uint16_t h; // Alt de la superficie
uint8_t *pixels; // pixels de la superficie
};
/// @brief Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla.
/// @brief La finestra serà width*zoom x height*zoom de gran.
/// @param titol es el text que apareixerà en la finestra
/// @param width es el ample de la finestra "virtual"
/// @param height es el alt de la finestra "virtual"
/// @param zoom es com de grans son els pixels.
void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen=false, const float ratio=1.0);
/// @brief Finalització del sistema (tancar coses de SDL, superficies fixes, etc...)
void quit();
void setZoom(const int value);
const int getZoom();
const float getScaleX();
const float getScaleY();
const int getOffsetX();
const int getOffsetY();
bool getFullscreen();
void setFullscreen(const bool value);
void loadShader();
void setShader(const char* shader_file);
void enableShader();
void disableShader();
void toggleShader();
void hideCursor();
void showCursor();
/// @brief Crea una superficie i torna un punter a ella
/// @param w ample de la superficie
/// @param h alt de la superficie
/// @return un punter a una nova superficie
surface *createSurface(const uint16_t w, const uint16_t h);
/// @brief Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
/// @param filename nom de l'arxiu GIF d'on carregar la superficie
/// @param loadPalette si es true també se carrega la paleta del GIF
/// @return un punter a una nova superficie
surface *loadSurface(const char* filename, const bool loadPalette = false);
/// @brief Allibera la memòria d'una superficie, els seus pixels inclosos
/// @param surf punter a la superficie a alliberar
void freeSurface(surface *surf);
/// @brief Estableix una superficie com a superficie que rebrà les funcions de pintat (especificar nullptr per a pintar a pantalla)
/// @param surf punter a la superficie a establir com a destinació
void setDestination(surface *surf);
/// @brief Estableix una superficie com a superficie de la que s'agafaràn els gràfics per a pintar
/// @param surf punter a la superficie a establir com a oritge
void setSource(surface *surf);
void pushSource();
void popSource();
void setViewport(const int x, const int y, const int w, const int h);
void resetViewport();
const int getLocalX(const int x);
const int getLocalY(const int y);
/// @brief Carrega la paleta d'un GIF i la torna en un array de uint32_t
/// @param filename nom de l'arxiu GIF d'on carregar la paleta
/// @param paletteSize si no es NULL ens torna el tamany de la paleta carregada
uint32_t *loadPalette(const char *filename, int *paletteSize = nullptr);
/// @brief Estableix la paleta del sistema, o part de ella, des d'un array especificat
/// @param pal un array de uint32_t
/// @param len quantes entrades volem trasladar a la paleta de sistema (no superar el tamany de 'pal'!)
/// @param pos des de quina posició de la paleta de sistema comencem a copiar
void setPalette(const uint32_t *pal, const int len, const int pos=0);
/// @brief Recupera la paleta del sistema, o part de ella, a un array
/// @return un array de uint32_t
uint32_t *getPalette();
/// @brief Estableix una entrada de la paleta del sistema
/// @param index l'index de l'entrada de la paleta
/// @param r la component roja de l'entrada de la paleta
/// @param g la component verda de l'entrada de la paleta
/// @param b la component blava de l'entrada de la paleta
void setPaletteEntry(const uint8_t index, const uint8_t r, const uint8_t g, const uint8_t b);
/// @brief Esborra la superficie "destination" amb el color especificat
/// @param color color a usar per a borrar la superficie de destinació
void cls(const uint8_t color);
/// @brief Estableix el color especificat com a transparent
/// @param color color a usar com a transparent
void setTrans(const uint8_t color);
void putPixel(const int x, const int y, const uint8_t color);
/// @brief Pinta un troç de la superficie "source" en la superficie "destination".
/// @param dx coordenada x de la destinació
/// @param dy coordenada y de la destinació
/// @param w ample del quadrat a pintar
/// @param h alt del quadrat a pintar
/// @param sx coordenada x de l'oritge
/// @param sy coordenada y de l'oritge
/// @param flip si s'ha de fer flip en hortizontal o vertical (o ambdos)
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const draw::flip flip = draw::flip::none);
/// @brief Pinta tota la superficie "source" en la superficie "destination", posició (x,y).
/// @param x coordenada x de la destinació
/// @param y coordenada y de la destinació
void draw(const int x, const int y);
/// @brief Pinta tota la superficie "source" en la superficie "destination", posició (0,0).
void draw();
/// @brief Carrega la superficie especificada en "source" i la pinta tota en la superficie "destination", posició (0,0).
void draw(draw::surface* surf);
void swapcol(const uint8_t c1, const uint8_t c2);
void restorecol(const uint8_t c);
void color(const uint8_t col);
void hline(const int x, const int y, const int w);
void vline(const int x, const int y, const int h);
void fillrect(const int x, const int y, const int w, const int h);
void rect(const int x, const int y, const int w, const int h);
void fadein();
void fadeout();
bool isfading();
//void print(const char* text, const int x, const int y, const uint8_t color, const uint8_t borde);
/// @brief Refresca la pantalla
void render();
}

View File

@@ -1,275 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "file.h"
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <string>
#include <vector>
#ifndef _WIN32
#include <pwd.h>
#endif
#define DEFAULT_FILENAME "data.jf2"
#define DEFAULT_FOLDER "data/"
#define CONFIG_FILENAME "config.txt"
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 (uint 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();
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+1:filesize);
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;
#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() {
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* getConfigValueString(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;
}
const int getConfigValueInteger(const char *key, const int default_value)
{
const char* value = getConfigValueString(key);
if (!value) return default_value;
return atoi(value);
}
const float getConfigValueFloat(const char *key, const float default_value)
{
const char* value = getConfigValueString(key);
if (!value) return default_value;
return atof(value);
}
const bool getConfigValueBool(const char *key, const bool default_value)
{
const char* value = getConfigValueString(key);
if (!value) return default_value;
return strcmp(value, "true")==0?true:false;
}
void setConfigValueString(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;
}
void setConfigValueInteger(const char* key, const int value)
{
char tmp[256];
sprintf(tmp, "%i", value);
setConfigValueString(key, tmp);
}
void setConfigValueFloat(const char* key, const float value)
{
char tmp[256];
sprintf(tmp, "%.2f", value);
setConfigValueString(key, tmp);
}
void setConfigValueBool(const char* key, const bool value)
{
setConfigValueString(key, value?"true":"false");
}
}

View File

@@ -1,27 +0,0 @@
#pragma once
#include <stdio.h>
#define SOURCE_FILE 0
#define SOURCE_FOLDER 1
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);
const char* getConfigValueString(const char *key);
const int getConfigValueInteger(const char *key, const int default_value=0);
const float getConfigValueFloat(const char *key, const float default_value=0.0f);
const bool getConfigValueBool(const char *key, const bool default_value=false);
void setConfigValueString(const char* key, const char* value);
void setConfigValueInteger(const char* key, const int value);
void setConfigValueFloat(const char* key, const float value);
void setConfigValueBool(const char* key, const bool value);
}

View File

@@ -1,133 +0,0 @@
#include "game.h"
#include "draw.h"
#include "input.h"
#include <SDL3/SDL.h>
#include <map>
#include <string>
#ifdef MACOS_BUNDLE
#include <libgen.h>
#endif
namespace game
{
bool windowHasFocus = true;
static bool (*loop)() = nullptr;
static unsigned int ticks_per_frame = 1000/60;
static std::map<std::string, int> config;
void setUpdateTicks(const int ticks)
{
ticks_per_frame = ticks;
}
void setState(bool (*loop)())
{
game::loop = loop;
}
void setConfig(const char* key, const int value)
{
config[key] = value;
}
const int getConfig(const char* key)
{
return config[key];
}
const uint32_t getTicks()
{
return SDL_GetTicks();
}
}
int main(int argc, char *argv[])
{
#ifdef MACOS_BUNDLE
char res_file[255] = "";
strcpy(res_file, dirname(argv[0]));
strcat(res_file, "/../Resources/data.jf2");
file_setresourcefilename(res_file);
#endif
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
#ifdef DEBUG
SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
#endif
SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "JAPI v%s\n", JAPI_VERSION);
game::windowHasFocus = true;
game::init();
input::init();
static unsigned int current_ticks = SDL_GetTicks();
bool should_exit=false;
SDL_Event e;
while (!should_exit)
{
while(SDL_PollEvent(&e))
{
if (e.type==SDL_EVENT_QUIT)
{
should_exit = true;
break;
}
if (e.type==SDL_EVENT_KEY_DOWN)
{
input::updateKey(e.key.scancode);
}
if (e.type==SDL_EVENT_KEY_UP)
{
switch (e.key.scancode) {
case SDL_SCANCODE_F1:
draw::setZoom(draw::getZoom()-1);
break;
case SDL_SCANCODE_F2:
draw::setZoom(draw::getZoom()+1);
break;
case SDL_SCANCODE_F3:
draw::setFullscreen(!draw::getFullscreen());
break;
case SDL_SCANCODE_F4:
draw::toggleShader();
break;
default:
input::updateKeypressed(e.key.scancode);
}
}
if (e.type==SDL_EVENT_MOUSE_BUTTON_UP)
{
input::updateClk(e.button.button);
}
if (e.type==SDL_EVENT_MOUSE_WHEEL)
{
input::updateWheel(e.wheel.y);
}
if ( e.type == SDL_EVENT_WINDOW_FOCUS_GAINED )
{
game::windowHasFocus = true;
}
if ( e.type == SDL_EVENT_WINDOW_FOCUS_LOST )
{
game::windowHasFocus = false;
}
}
if (SDL_GetTicks()-current_ticks >= game::ticks_per_frame)
{
if (game::loop) if (!game::loop()) should_exit = true;
input::updateKey(SDL_SCANCODE_UNKNOWN);
input::updateKeypressed(SDL_SCANCODE_UNKNOWN);
input::updateClk(0);
input::updateWheel(0);
current_ticks = SDL_GetTicks();
}
}
return 0;
}

View File

@@ -1,25 +0,0 @@
#pragma once
#include "draw.h"
#include "input.h"
#include "audio.h"
#include "file.h"
#define JAPI_VERSION "0.8"
namespace game
{
extern bool windowHasFocus;
void setUpdateTicks(const int ticks);
void init();
void setState(bool (*loop)());
void setConfig(const char* key, const int value);
const int getConfig(const char* key);
const uint32_t getTicks();
}

View File

@@ -1,481 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdint.h>
#define EXTENSION_INTRODUCER 0x21
#define IMAGE_DESCRIPTOR 0x2C
#define TRAILER 0x3B
#define GRAPHIC_CONTROL 0xF9
#define APPLICATION_EXTENSION 0xFF
#define COMMENT_EXTENSION 0xFE
#define PLAINTEXT_EXTENSION 0x01
#define READ(dst, size) memcpy(dst, buffer, size); buffer += size
typedef struct
{
unsigned short width;
unsigned short height;
unsigned char fields;
unsigned char background_color_index;
unsigned char pixel_aspect_ratio;
}
screen_descriptor_t;
typedef struct
{
unsigned char r;
unsigned char g;
unsigned char b;
}
rgb;
typedef struct
{
unsigned short image_left_position;
unsigned short image_top_position;
unsigned short image_width;
unsigned short image_height;
unsigned char fields;
}
image_descriptor_t;
typedef struct
{
unsigned char byte;
int prev;
int len;
}
dictionary_entry_t;
typedef struct
{
unsigned char extension_code;
unsigned char block_size;
}
extension_t;
typedef struct
{
unsigned char fields;
unsigned short delay_time;
unsigned char transparent_color_index;
}
graphic_control_extension_t;
typedef struct
{
unsigned char application_id[ 8 ];
unsigned char version[ 3 ];
}
application_extension_t;
typedef struct
{
unsigned short left;
unsigned short top;
unsigned short width;
unsigned short height;
unsigned char cell_width;
unsigned char cell_height;
unsigned char foreground_color;
unsigned char background_color;
}
plaintext_extension_t;
//static unsigned short width = 0;
//static unsigned short height = 0;
//static unsigned char* uncompressed_data = NULL;
void uncompress( int code_length,
const unsigned char *input,
int input_length,
unsigned char *out )
{
//int maxbits;
int i, bit;
int code, prev = -1;
dictionary_entry_t *dictionary;
int dictionary_ind;
unsigned int mask = 0x01;
int reset_code_length;
int clear_code; // This varies depending on code_length
int stop_code; // one more than clear code
int match_len;
clear_code = 1 << ( code_length );
stop_code = clear_code + 1;
// To handle clear codes
reset_code_length = code_length;
// Create a dictionary large enough to hold "code_length" entries.
// Once the dictionary overflows, code_length increases
dictionary = ( dictionary_entry_t * )
malloc( sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
// Initialize the first 2^code_len entries of the dictionary with their
// indices. The rest of the entries will be built up dynamically.
// Technically, it shouldn't be necessary to initialize the
// dictionary. The spec says that the encoder "should output a
// clear code as the first code in the image data stream". It doesn't
// say must, though...
for ( dictionary_ind = 0;
dictionary_ind < ( 1 << code_length );
dictionary_ind++ )
{
dictionary[ dictionary_ind ].byte = dictionary_ind;
// XXX this only works because prev is a 32-bit int (> 12 bits)
dictionary[ dictionary_ind ].prev = -1;
dictionary[ dictionary_ind ].len = 1;
}
// 2^code_len + 1 is the special "end" code; don't give it an entry here
dictionary_ind++;
dictionary_ind++;
// TODO verify that the very last byte is clear_code + 1
while ( input_length )
{
code = 0x0;
// Always read one more bit than the code length
for ( i = 0; i < ( code_length + 1 ); i++ )
{
// This is different than in the file read example; that
// was a call to "next_bit"
bit = ( *input & mask ) ? 1 : 0;
mask <<= 1;
if ( mask == 0x100 )
{
mask = 0x01;
input++;
input_length--;
}
code = code | ( bit << i );
}
if ( code == clear_code )
{
code_length = reset_code_length;
dictionary = ( dictionary_entry_t * ) realloc( dictionary,
sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
for ( dictionary_ind = 0;
dictionary_ind < ( 1 << code_length );
dictionary_ind++ )
{
dictionary[ dictionary_ind ].byte = dictionary_ind;
// XXX this only works because prev is a 32-bit int (> 12 bits)
dictionary[ dictionary_ind ].prev = -1;
dictionary[ dictionary_ind ].len = 1;
}
dictionary_ind++;
dictionary_ind++;
prev = -1;
continue;
}
else if ( code == stop_code )
{
/*if ( input_length > 1 )
{
fprintf( stderr, "Malformed GIF (early stop code)\n" );
exit( 0 );
}*/
break;
}
// Update the dictionary with this character plus the _entry_
// (character or string) that came before it
if ( ( prev > -1 ) && ( code_length < 12 ) )
{
if ( code > dictionary_ind )
{
fprintf( stderr, "code = %.02x, but dictionary_ind = %.02x\n",
code, dictionary_ind );
exit( 0 );
}
// Special handling for KwKwK
if ( code == dictionary_ind )
{
int ptr = prev;
while ( dictionary[ ptr ].prev != -1 )
{
ptr = dictionary[ ptr ].prev;
}
dictionary[ dictionary_ind ].byte = dictionary[ ptr ].byte;
}
else
{
int ptr = code;
while ( dictionary[ ptr ].prev != -1 )
{
ptr = dictionary[ ptr ].prev;
}
dictionary[ dictionary_ind ].byte = dictionary[ ptr ].byte;
}
dictionary[ dictionary_ind ].prev = prev;
dictionary[ dictionary_ind ].len = dictionary[ prev ].len + 1;
dictionary_ind++;
// GIF89a mandates that this stops at 12 bits
if ( ( dictionary_ind == ( 1 << ( code_length + 1 ) ) ) &&
( code_length < 11 ) )
{
code_length++;
dictionary = ( dictionary_entry_t * ) realloc( dictionary,
sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
}
}
prev = code;
// Now copy the dictionary entry backwards into "out"
match_len = dictionary[ code ].len;
while ( code != -1 )
{
out[ dictionary[ code ].len - 1 ] = dictionary[ code ].byte;
if ( dictionary[ code ].prev == code )
{
fprintf( stderr, "Internal error; self-reference." );
exit( 0 );
}
code = dictionary[ code ].prev;
}
out += match_len;
}
}
static int read_sub_blocks( unsigned char* buffer, unsigned char **data )
{
int data_length;
int index;
unsigned char block_size;
// Everything following are data sub-blocks, until a 0-sized block is
// encountered.
data_length = 0;
*data = NULL;
index = 0;
while ( 1 )
{
READ(&block_size, 1);
if ( block_size == 0 ) // end of sub-blocks
{
break;
}
data_length += block_size;
*data = (unsigned char*)realloc( *data, data_length );
// TODO this could be split across block size boundaries
READ(*data + index, block_size);
index += block_size;
}
return data_length;
}
unsigned char* process_image_descriptor( unsigned char* buffer,
rgb *gct,
int gct_size,
int resolution_bits )
{
image_descriptor_t image_descriptor;
int compressed_data_length;
unsigned char *compressed_data = NULL;
unsigned char lzw_code_size;
int uncompressed_data_length = 0;
unsigned char *uncompressed_data = NULL;
// TODO there could actually be lots of these
READ(&image_descriptor, 9);
// TODO if LCT = true, read the LCT
READ(&lzw_code_size, 1);
compressed_data_length = read_sub_blocks( buffer, &compressed_data );
// width = image_descriptor.image_width;
// height = image_descriptor.image_height;
uncompressed_data_length = image_descriptor.image_width *
image_descriptor.image_height;
uncompressed_data = (unsigned char*)malloc( uncompressed_data_length );
uncompress( lzw_code_size, compressed_data, compressed_data_length,
uncompressed_data );
if ( compressed_data ) free( compressed_data );
//if ( uncompressed_data )
// free( uncompressed_data );
return uncompressed_data;
}
/**
* @param gif_file the file descriptor of a file containing a
* GIF-encoded file. This should point to the first byte in
* the file when invoked.
*/
#define rb (*(buffer++))
uint32_t* LoadPalette(unsigned char *buffer, int *paletteSize = NULL) {
unsigned char header[7];
screen_descriptor_t screen_descriptor;
//int color_resolution_bits;
int global_color_table_size = 0; // number of entries in global_color_table
uint32_t *global_color_table = NULL;
READ(header, 6);
READ(&screen_descriptor, 7);
//color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
//global_color_table = (uint32_t *)calloc(1, 1024);
if (screen_descriptor.fields & 0x80)
{
global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
if (paletteSize != NULL) *paletteSize = global_color_table_size;
global_color_table = (uint32_t *)malloc(4 * global_color_table_size);
//READ(global_color_table, 3 * global_color_table_size);
for (int i=0; i<global_color_table_size;++i) {
global_color_table[i] = (buffer[0]<<16) + (buffer[1]<<8) + buffer[2];
buffer+=3;
}
}
return global_color_table;
}
static unsigned char* process_gif_stream(unsigned char *buffer, unsigned short* w, unsigned short* h)
{
unsigned char header[ 7 ];
screen_descriptor_t screen_descriptor;
int color_resolution_bits;
int global_color_table_size =0; // number of entries in global_color_table
rgb *global_color_table = NULL;
unsigned char block_type = 0x0;
// A GIF file starts with a Header (section 17)
READ(header, 6);
header[ 6 ] = 0x0;
// XXX there's another format, GIF87a, that you may still find
// floating around.
/*if ( strcmp( "GIF89a", (char*)header ) )
{
fprintf( stderr,
"Invalid GIF file (header is '%s', should be 'GIF89a')\n",
header );
return NULL;
}*/
// Followed by a logical screen descriptor
// Note that this works because GIFs specify little-endian order; on a
// big-endian machine, the height & width would need to be reversed.
// Can't use sizeof here since GCC does byte alignment;
// sizeof( screen_descriptor_t ) = 8!
READ(&screen_descriptor, 7);
*w = screen_descriptor.width;
*h = screen_descriptor.height;
color_resolution_bits = ( ( screen_descriptor.fields & 0x70 ) >> 4 ) + 1;
if ( screen_descriptor.fields & 0x80 )
{
//int i;
// If bit 7 is set, the next block is a global color table; read it
global_color_table_size = 1 <<
( ( ( screen_descriptor.fields & 0x07 ) + 1 ) );
global_color_table = ( rgb * ) malloc( 3 * global_color_table_size );
// XXX this could conceivably return a short count...
READ(global_color_table, 3 * global_color_table_size);
}
while ( block_type != TRAILER )
{
READ(&block_type, 1);
unsigned char size;
switch ( block_type )
{
case IMAGE_DESCRIPTOR:
return process_image_descriptor(buffer,
global_color_table,
global_color_table_size,
color_resolution_bits);
break;
case EXTENSION_INTRODUCER:
buffer++;
size = *(buffer++);
buffer += size;
do {
size = *(buffer++);
buffer += size;
} while (size != 0);
/*if ( !process_extension( buffer ) )
{
return NULL;
}*/
break;
case TRAILER:
break;
default:
fprintf( stderr, "Bailing on unrecognized block type %.02x\n",
block_type );
return NULL;
}
}
return NULL;
}
unsigned char* LoadGif(unsigned char *buffer, unsigned short* w, unsigned short* h) {
return process_gif_stream(buffer, w, h);
}
/*int main( int argc, char *argv[] )
{
FILE* gif_file;
if ( argc < 2 )
{
fprintf( stderr, "Usage: %s <path-to-gif-file>\n", argv[ 0 ] );
exit( 0 );
}
gif_file = fopen( argv[ 1 ], "rb" );
if ( gif_file == NULL )
{
fprintf( stderr, "Unable to open file '%s'", argv[ 1 ] );
perror( ": " );
}
process_gif_stream( gif_file );
fclose( gif_file );
}*/

View File

@@ -1,111 +0,0 @@
#include "input.h"
#include <SDL3/SDL.h>
#include "draw.h"
namespace input
{
static const bool *keys = nullptr;
static uint8_t keypressed = 0;
static uint8_t keydown = 0;
static uint8_t btnClicked = 0;
static int wheel = 0;
void init()
{
keys = SDL_GetKeyboardState(NULL);
}
// Determina si la tecla especificada està sent polsada ara mateix
bool keyDown(const uint8_t key)
{
return keys[key];
}
// Determina si la tecla especificada ha sigut polsada, pero no tornarà a ser true fins
bool keyPressed(const uint8_t key)
{
return key == keypressed;
}
// Determina si hi ha alguna tecla polsada ara mateix
bool anyKey()
{
return keydown != 0;
}
bool anyKeyPressed()
{
return keypressed != 0;
}
// Torna el codi de la tecla que està sent polsada ara mateix
const uint8_t whichKey()
{
return keydown;
}
// Torna el codi de la tecla que està sent polsada ara mateix
const uint8_t getKeyPressed()
{
return keypressed;
}
// (US INTERN) Actualitza la tecla actualment polsada (keydown) desde jgame
void updateKey(const uint8_t key)
{
keydown = key;
}
// (US INTERN) Actualitza la tecla actualment polsada (keypress) desde jgame
void updateKeypressed(const uint8_t key)
{
keypressed = key;
}
// (US INTERN) Actualitza el botó del ratolí actualment polsat desde jgame
void updateClk(const uint8_t btn)
{
btnClicked = btn;
}
// (US INTERN) Actualitza el valor de la roda del ratolí actual desde jgame
void updateWheel(const int dy)
{
wheel = dy;
}
// Torna la posició X actual del ratolí
const int mouseX()
{
float x;
SDL_GetMouseState(&x, NULL);
return (x-draw::getOffsetX())/draw::getScaleX();
}
// Torna la posició Y actual del ratolí
const int mouseY()
{
float y;
SDL_GetMouseState(NULL, &y);
return (y-draw::getOffsetY())/draw::getScaleY();
}
// Determina si el botó del ratolí especificat està sent polsada ara mateix
const bool mouseBtn(const int 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
// que no se solte el botó i se torne a polsar (Equivalent a keypress en tecles).
const bool mouseClk(const int btn)
{
return btnClicked == btn;
}
// Obté el valor actual de la rodeta del ratolí
const int mouseWheel()
{
return wheel;
}
}

View File

@@ -1,87 +0,0 @@
#pragma once
#include <stdint.h>
namespace input
{
namespace mouse
{
namespace button
{
const int left = 1;
const int middle = 2;
const int right = 3;
}
}
/// @brief Inicialitza els sistemes de teclat, ratolí i gamepad
void init();
/// @brief Determina si la tecla especificada està sent polsada ara mateix
/// @param key tecla a consultar
/// @return true si està polsada, false si no
bool keyDown(const uint8_t key);
/// @brief Determina si la tecla especificada ha sigut polsada, pero no tornarà a ser true fins
/// @brief que no se solte la tecla i se torne a polsar.
/// @param key tecla a consultar
/// @return true si està polsada, false si no
bool keyPressed(const uint8_t key);
/// @brief Determina si hi ha alguna tecla polsada ara mateix
/// @return true si hi ha alguna tecla polsada, false si no
bool anyKey();
/// @brief El equivalent a anykey per a keypressed
/// @return true si hi ha alguna tecla polsada, false si no
bool anyKeyPressed();
/// @brief Torna el codi de la tecla que està sent polsada ara mateix
/// @return Quina tecla està sent polsada
const uint8_t whichKey();
/// @brief Torna el codi de la tecla que està sent polsada ara mateix
/// @brief (nomes una vegada, com keypress)
/// @return Quina tecla està sent polsada
const uint8_t getKeyPressed();
/// @brief (US INTERN) Actualitza la tecla actualment polsada (keydown) desde jgame
/// @param key tecla polsada
void updateKey(const uint8_t key);
/// @brief (US INTERN) Actualitza la tecla actualment polsada (keypress) desde jgame
/// @param key tecla polsada
void updateKeypressed(const uint8_t key);
/// @brief (US INTERN) Actualitza el botó del ratolí actualment polsat desde jgame
/// @param btn botó polsat
void updateClk(const uint8_t btn);
/// @brief (US INTERN) Actualitza el valor de la roda del ratolí actual desde jgame
/// @param dy desplaçament de la rodeta
void updateWheel(const int dy);
/// @brief Torna la posició X actual del ratolí
/// @return valor de la coordenada X del ratolí
const int mouseX();
/// @brief Torna la posició Y actual del ratolí
/// @return valor de la coordenada Y del ratolí
const int mouseY();
/// @brief Determina si el botó del ratolí especificat està sent polsada ara mateix
/// @brief (Equivalent a keydown en tecles)
/// @param btn botó a consultar
/// @return true si està polsat, false si no
const bool mouseBtn(const int btn);
/// @brief Determina si el botó especificat ha sigut polsat, pero no tornarà a ser true fins
/// @brief que no se solte el botó i se torne a polsar (Equivalent a keypress en tecles).
/// @param btn botó a consultar
/// @return true si està polsat, false si no
const bool mouseClk(const int btn);
/// @brief Obté el valor actual de la rodeta del ratolí
/// @return 0 si no es mou, positiu si roda cap amunt, negatiu si roda cap avall
const int mouseWheel();
}

View File

@@ -1,277 +0,0 @@
#include "shader.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 shader
{
SDL_Window *win = nullptr;
SDL_Renderer *renderer = nullptr;
GLuint programId = 0;
SDL_Texture* backBuffer = nullptr;
SDL_FRect window = {0, 0, 640, 480};
SDL_FPoint tex_size = {320, 240};
float aspect_ratio = 1;
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;
PFNGLUNIFORM2FPROC glUniform2f;
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");
glUniform2f = (PFNGLUNIFORM2FPROC)SDL_GL_GetProcAddress("glUniform2f");
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glDeleteProgram && glLinkProgram && glValidateProgram && glGetProgramiv &&
glGetProgramInfoLog && glUseProgram && glGetUniformLocation && glUniform2f;
}
#endif
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;
int w, h;
SDL_GetWindowSize(win, &w, &h);
if (w * aspect_ratio > h) {
window.y = 0;
window.h = h;
window.w = h/aspect_ratio;
window.x = (w - window.w)/2;
} else {
window.x = 0;
window.w = w;
window.h = w*aspect_ratio;
window.y = (h - window.h)/2;
}
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;
}
unsigned char pixels[512*240*4];
void enable() { if (can_use_opengl) using_opengl = true; }
void disable() { using_opengl = false; }
void setAspectRatio(const float ratio)
{
aspect_ratio = ratio;
}
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);
}
//GLint loc = glGetUniformLocation(programId, "TextureSize");
//glUniform2f(loc, 320, 256);
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 1);
//glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels);
//if (glGetError()) { printf("GLGETERROR!\n"); exit(1);}
//GLint param;
//glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &param);
//printf("tex width: %i\n", param);
glViewport(window.x, window.y, window.w, window.h);
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, &window);
SDL_RenderPresent(renderer);
}
int glerror = glGetError();
if (glerror) { printf("GLERROR: %i\n", glerror); exit(1); }
}
}

View File

@@ -1,48 +0,0 @@
#pragma once
#include <SDL3/SDL.h>
// TIPS:
// =======================================================================
// Abans de crear el renderer, cridar a la següent funció:
//
// SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
//
// Aixó li diu que volem un renderer que use especificament opengl. A més,
// al crear el renderer li tenim que dir que el volem que use acceeració
// per hardware, i que soporte render a textura. Per exemple:
//
// SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED |
// SDL_RENDERER_TARGETTEXTURE);
//
// Per altra part, al crear la textura tenim que definir que puga ser target
// de renderitzat (SDL_TEXTUREACCESS_TARGET), per exemple:
//
// SDL_Texture *tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
// SDL_TEXTUREACCESS_TARGET, 320, 240);
//
// Els shaders li'ls passem com una cadena, som nosaltres els que s'encarreguem
// de carregarlos de disc, amb fopen, ifstream, jfile o el que vullgues.
// Si els tens en un std::string, passa-li-la com "cadena.c_str()".
//
// Poden ser els dos el mateix arxiu, com fa libRetro, jo desde dins ja fique
// els defines necessaris. Si es el mateix arxiu, pots no ficar el quart paràmetre.
//
// Els shaders de libRetro no funcionen directament, hi ha que fer algunes modificacions.
//
// El pintat final de la teua escena l'has de fer com si "backBuffer" fora la pantalla.
//
// Ah! una cosa mes: al compilar, en Linux afegir "-lGL", en Windows afegir "-lopengl32".
// En Mac ni idea
namespace shader
{
const bool init(SDL_Window* win, SDL_Texture* backBuffer,
const char* vertexShader, const char* fragmentShader=nullptr);
void setAspectRatio(const float ratio);
void enable();
void disable();
void render();
}

File diff suppressed because it is too large Load Diff