49 Commits

Author SHA1 Message Date
d117cd3b8b - [FIX] Canvi de tipus del timer 2026-05-01 19:49:54 +02:00
b51ef4ba64 - [FIX] Arreglats paths a version.h en el scripts
- [TEST] Tornat a SDL_GetTicks per a probar si te que vore en el mode 'Benny Hill' de Joup
2026-05-01 15:42:13 +02:00
d1e8425b09 - [FIX] Els scripts de compilació han de esborrar tots els .o antics abans de començar 2026-05-01 12:41:11 +02:00
770971142f - [FIX] Crida a Lua es diferent si deshabilitem el debug 2026-05-01 12:26:17 +02:00
90f057fc5e - [FIX] deshabilitar el debugger si no estem en DEBUG ni en Linux 2026-05-01 12:10:11 +02:00
948870215d - [FIX] Inclos el json.hpp de nlohmann en el projecte 2026-05-01 11:43:44 +02:00
a125e799ad - [FIX] Windows no admet directoris que es diguen "aux", el molt membrillo
. [NEW] Compilació migrada a lagueirto
2026-05-01 11:38:45 +02:00
e6d4833e8b - [NEW] Adaptació de àudio en progrés 2026-04-17 13:46:22 +02:00
9c4c94093c - [NEW] Afegit clipboard al backend
- [NEW] surfaces ara usa un vector dinàmic
- [FIX] Ajustades dereferenciacions per a arreglar la caiguda de rendiment
2026-04-17 06:52:31 +02:00
52d2fcf0d3 - [WIP] Fase 3 en process 2026-04-16 13:49:03 +02:00
884df104bd - [FIX] Al pintar una surface el reemplaç de color es feia doble 2026-04-15 17:33:59 +02:00
c0d1b1fecf - [WIP] Acabant la fase 2
- [NEW] Preparant codi per a la fase 3
2026-04-14 21:45:25 +02:00
380295aed0 - [WIP] Fase 2 quasi acabada
- [FIX] Arreglos per al debugger
- [FIX] Calcul de la posició del mouse en coordenades tenint en compte view.origin
2026-04-13 20:09:57 +02:00
0142d79d91 - [WIP] Puta que susto, una regla mal feta en el .gitignore i ja no comitaba res
- Canvi de comp
2026-04-11 16:39:47 +02:00
14a7fda8b7 - [WIP] Reestructuració: Fase 2 a meitant, pero serà questió de commitar, no tingam un disgust 2026-04-11 14:18:57 +02:00
6cfddadf43 - [NEW] Separat els wrappers de lua en la seua propia unitat
- [FIX] Netejades capçaleres basura
2026-04-03 21:29:00 +02:00
bc006b8f72 - [WIP] Reestructuració: Fase 1 acabada 2026-04-03 11:11:53 +02:00
569221d047 - [WIP] Está tot patas arriba ara... 2026-04-02 13:26:52 +02:00
4a1627835f - [NEW] Neteja de estats interns i reimplementació del sistema de pset i primitives 2026-04-01 23:57:48 +02:00
f4eac55989 BFR 2 (Big Fucking Restructureixon) (dos)
Este es el primer commit de la reestructuració. En caso de pánico, tornar al commit anterior.
- [FIX] Llevada basura varia (pos no en queda...)
- [NEW] mogut el codi font a ./source/
- [NEW] lagueirtofile nou per a compilar en windows y linux, release i debug. mac res, que no se ni si funciona lagueirto.
- [NEW] WARNING!!! make ja no funciona. Mantinc encara el Makefile per a referència.
2026-04-01 22:17:42 +02:00
5c0b046ad8 - [WIP] Mode debug activat per defecte
- [WIP] Canvis visuals al rebre una excepció de Lua
- [WIP] MessageBox al rebre excepcions
- [WIP] Finestra resizable
2026-04-01 07:16:10 +02:00
7739b563f3 - [NEW] [debugger] Quan hi ha una excepció, ho notifica al adapter i li dona la info necessaria 2026-03-31 18:21:05 +02:00
6e5f9fb1a8 - [NEW] [debugger] Soport per a expressions d'assignació en vscode
- [NEW] [debugger] Soport per a Logpoints en vscode
- [NEW] Al tornar del debugger, torna a pillar el foco la finestra de mini
2026-03-31 14:04:17 +02:00
6f5bdd274a - [NEW] [debugger] Soport per a multiples nivells (frames) de stack
- [NEW] [debugger] Soport per a breakpoints condicionals
- [NEW] [debugger] Soport per a modificar variables
2026-03-31 13:03:53 +02:00
6a24086556 - [NEW] [debugger] Soport per a expressions evaluables desde consola de vscode 2026-03-31 09:20:47 +02:00
b78fbe4378 - [NEW] Lo mateix que he dit pa mini-debugger, pero la part del motor ^__^ 2026-03-30 23:06:22 +02:00
9f8533f62b - [NEW] (del debugger) Ja funciona: start, pause, stop, breakpoints, step into, step over, step out. Intentant que funcione el enviament del stackTrace 2026-03-30 14:06:09 +02:00
8a4110e821 - Segueix el treball en el debugger 2026-03-30 06:41:13 +02:00
0547378331 - [FIX] El nom dels .zip estaba mal 2026-03-25 10:35:28 +01:00
92b4e4472f - [FIX] Ficar dlls on fan falta
- [FIX] Llevades les dlls que ja no fan falta
2026-03-25 10:23:09 +01:00
d362eef8bf - [NEW] Ajustada la compilació en Windows
- [FIX] Ficades les dll en l'arrel del zip
2026-03-25 10:14:22 +01:00
57852bd3ae VERSIÓ 1.4.10:
- [NEW] surface_t.flags
- [NEW] F12 per a recàrrega de surfaces en calent
2026-03-24 08:23:11 +01:00
6fb31a3ae5 - [FIX] Fallaba al carregar fonts amb format de final de linea de Windows 2026-03-22 17:39:21 +01:00
89496fb8fb - [FIX] El nom dels arxius a pujar a gitea estaba mal 2026-03-21 11:53:41 +01:00
4774a1a806 - [FIX] Scripts de publicació 2026-03-21 11:46:47 +01:00
74cb8cb0f8 VERSIÓ 1.4.8:
- [NEW] draw.surfrot
2026-03-21 10:15:23 +01:00
d573c159fa - [FIX] Arreglat el make de macos
- [NEW] Publicació automàtica de releases
2026-03-20 11:46:11 +01:00
446f588cfe - [WIP] Treballant en que publique a gitea amb la API 2026-03-20 09:17:21 +01:00
8c9c1f2b47 - [WIP] Afegides DLLs de windows per a quan empaquete 2026-03-20 08:37:01 +01:00
6cccd44743 - [FIX] Eliminat un warning en macOS en jfile
- [WIP] Sistema de release automatic
2026-03-20 08:35:28 +01:00
4a3450e116 VERSIÓ 1.4.7
- [NEW] map.cell() per a establir o llegir el tamany dels tiles del mapa
2026-03-19 11:29:08 +01:00
779ef7acb6 VERSIÓ 1.4.6
-[NEW] font.load()
-[NEW] font.current()
-[NEW] font.spacing()
-[NEW] font.DEFAULT
-[NEW] surf.SCREEN
2026-03-18 20:36:14 +01:00
bf34c92e09 VERSIÓ 1.4.5
- [NEW] Afegida funció "sub" al modul estandar "utf8"
2026-03-18 10:33:02 +01:00
9c895a518e VERSIÓ 1.4.4
- [NEW] Nou sistema de fonts funcionant ja internament
2026-03-18 08:58:42 +01:00
e89d596ea4 (Canvi d'ordenador)
- [WIP] Treballant en les fonts
2026-03-17 18:25:17 +01:00
560d67ca3d VERSIÓ 1.4.3
- [NEW] key.text()
- [NEW] key.utf8char()
- [NEW] pal.trans() ara torna el color transparent
- [NEW] pal.subpal(index) i pal.subpal(index,color) ara tornen el color al que estaba abans asignat el index
2026-03-17 13:47:25 +01:00
6f5d90ee49 - [FIX] typo en un #ifdef 2026-03-16 16:27:04 +01:00
d92a903c61 VERSIÓ 1.4.2
- [NEW] excutar "mini --version" torna la versió
- [NEW] executar "mini dura/a/un/data.jf2" usará eixe arxiu jf2
- [NEW] sys.version()
2026-03-16 14:25:47 +01:00
afa022d838 - [FIX] Arerglat mkdir() en Windows
- [FIX] Arreglat glActiveTexture en Windows
2026-03-15 13:10:00 +01:00
140 changed files with 33472 additions and 4617 deletions

7
.gitignore vendored
View File

@@ -1,8 +1,9 @@
mini.exe /mini.exe
mini /mini
mini_debug.exe mini_debug.exe
mini_debug mini_debug
.vscode/* .vscode/*
info.plist info.plist
*.dll
build/* build/*
*.zip
*.tar.gz

View File

@@ -3,7 +3,7 @@ source = *.cpp ./lua/*.c
windows: windows:
@echo off @echo off
g++ $(source) icon.res -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL3 -lopengl32 -mwindows -o "$(executable).exe" g++ $(source) icon.res -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL3 -lopengl32 -static-libstdc++ -static-libgcc -lpthread -mwindows -o "$(executable).exe"
strip -s -R .comment -R .gnu.version --strip-unneeded "$(executable).exe" strip -s -R .comment -R .gnu.version --strip-unneeded "$(executable).exe"
windows_debug: windows_debug:
@@ -11,13 +11,13 @@ windows_debug:
g++ $(source) -D DEBUG -g -Wall -Os -lmingw32 -lSDL3 -lopengl32 -o "$(executable)_debug.exe" g++ $(source) -D DEBUG -g -Wall -Os -lmingw32 -lSDL3 -lopengl32 -o "$(executable)_debug.exe"
macos: macos:
clang++ $(source) -Wall -Os -std=c++11 -ffunction-sections -fdata-sections -lSDL3 -o "$(executable)" clang++ $(source) -Wall -Os -std=c++17 -Wno-deprecated -ffunction-sections -fdata-sections -lSDL3 -framework OpenGL -o "$(executable)"
macos_debug: macos_debug:
clang++ $(source) -D DEBUG -g -Wall -Os -std=c++11 -ffunction-sections -fdata-sections -lSDL3 -o "$(executable)_debug" clang++ $(source) -D DEBUG -g -Wall -Os -std=c++17 -Wno-deprecated -ffunction-sections -fdata-sections -lSDL3 -framework OpenGL -o "$(executable)_debug"
macos_bundle: macos_bundle:
clang++ $(source) -D MACOS_BUNDLE -Wall -Os -std=c++11 -framework SDL3 -F /Library/Frameworks -ffunction-sections -fdata-sections -o mini_bundle -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.12 clang++ $(source) -D MACOS_BUNDLE -Wall -Os -std=c++17 -Wno-deprecated -framework SDL3 -framework OpenGL -F /Library/Frameworks -ffunction-sections -fdata-sections -o mini_bundle -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.12
linux: linux:
g++ $(source) -D LUA_USE_LINUX -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL3 -lGL -o "$(executable)" g++ $(source) -D LUA_USE_LINUX -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL3 -lGL -o "$(executable)"

BIN
bin/SDL3.dll Normal file

Binary file not shown.

BIN
bin/libwinpthread-1.dll Normal file

Binary file not shown.

9
data/font.fnt Normal file
View File

@@ -0,0 +1,9 @@
bitmap=font.gif
48: 0 0 7 7 0 # 0
49: 8 0 4 7 0 # 1
50: 16 0 7 7 0 # 2
51: 24 0 7 7 0 # 3
52: 32 0 7 7 0 # 4
53: 40 0 7 7 0 # 5
54: 48 0 7 7 0 # 6
55: 56 0 7 7 0 # 7

BIN
data/font.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,6 +1,7 @@
require "ia.other" --require "ia.other"
x=0 x=0
rot=0
function mini.init() function mini.init()
s = surf.load("gfx/logo.gif") s = surf.load("gfx/logo.gif")
@@ -16,11 +17,12 @@ function mini.init()
end end
print("========================") print("========================")
f = font.load("font.fnt")
end end
function mini.update() function mini.update()
surf.cls(0) surf.cls(0)
draw.surf(0, 0, 160, 144, 0, 0) draw.surfrot(0, 0, 160, 144, 120, 78, rot)
draw.text("PRESS START", 60, 110, 28) draw.text("PRESS START", 60, 110, 28)
if key.press(key.ESCAPE) then sys.quit() end if key.press(key.ESCAPE) then sys.quit() end
@@ -41,5 +43,11 @@ function mini.update()
local mx, my = mouse.pos() local mx, my = mouse.pos()
draw.rectf(mx, my, 4, 4, 8) draw.rectf(mx, my, 4, 4, 8)
draw.text(mx .. " " .. my, 1, 8, 8) draw.text(mx .. " " .. my, 1, 8, 8)
draw.text(other.peiv(),1,100,4) --draw.text(other.peiv(),1,100,4)
font.current(f)
font.spacing(0)
draw.text("0146",100,50,28)
font.current(font.DEFAULT)
rot=rot+1
end end

130
default_font.fnt Normal file
View File

@@ -0,0 +1,130 @@
bitmap=default_font
32: 0 0 3 5 0 #
33: 4 0 3 5 0 # !
34: 8 0 3 5 0 # "
35: 12 0 3 5 0 # #
36: 16 0 3 5 0 # $
37: 20 0 3 5 0 # %
38: 24 0 3 5 0 # &
39: 28 0 3 5 0 # '
40: 32 0 3 5 0 # (
41: 36 0 3 5 0 # )
42: 40 0 3 5 0 # *
43: 44 0 3 5 0 # +
44: 48 0 3 5 0 # ,
45: 52 0 3 5 0 # -
46: 56 0 3 5 0 # .
47: 60 0 3 5 0 # /
48: 0 6 3 5 0 # 0
49: 4 6 3 5 0 # 1
50: 8 6 3 5 0 # 2
51: 12 6 3 5 0 # 3
52: 16 6 3 5 0 # 4
53: 20 6 3 5 0 # 5
54: 24 6 3 5 0 # 6
55: 28 6 3 5 0 # 7
56: 32 6 3 5 0 # 8
57: 36 6 3 5 0 # 9
58: 40 6 3 5 0 # :
59: 44 6 3 5 0 # ;
60: 48 6 3 5 0 # <
61: 52 6 3 5 0 # =
62: 56 6 3 5 0 # >
63: 60 6 3 5 0 # ?
64: 0 12 3 5 0 # @
65: 4 12 3 5 0 # A
66: 8 12 3 5 0 # B
67: 12 12 3 5 0 # C
68: 16 12 3 5 0 # D
69: 20 12 3 5 0 # E
70: 24 12 3 5 0 # F
71: 28 12 3 5 0 # G
72: 32 12 3 5 0 # H
73: 36 12 3 5 0 # I
74: 40 12 3 5 0 # J
75: 44 12 3 5 0 # K
76: 48 12 3 5 0 # L
77: 52 12 3 5 0 # M
78: 56 12 3 5 0 # N
79: 60 12 3 5 0 # O
80: 0 18 3 5 0 # P
81: 4 18 3 5 0 # Q
82: 8 18 3 5 0 # R
83: 12 18 3 5 0 # S
84: 16 18 3 5 0 # T
85: 20 18 3 5 0 # U
86: 24 18 3 5 0 # V
87: 28 18 3 5 0 # W
88: 32 18 3 5 0 # X
89: 36 18 3 5 0 # Y
90: 40 18 3 5 0 # Z
91: 44 18 3 5 0 # [
92: 48 18 3 5 0 # \
93: 52 18 3 5 0 # ]
94: 56 18 3 5 0 # ^
95: 60 18 3 5 0 # _
96: 0 24 3 5 0 # `
97: 4 24 3 5 0 # a
98: 8 24 3 5 0 # b
99: 12 24 3 5 0 # c
100: 16 24 3 5 0 # d
101: 20 24 3 5 0 # e
102: 24 24 3 5 0 # f
103: 28 24 3 5 0 # g
104: 32 24 3 5 0 # h
105: 36 24 3 5 0 # i
106: 40 24 3 5 0 # j
107: 44 24 3 5 0 # k
108: 48 24 3 5 0 # l
109: 52 24 3 5 0 # m
110: 56 24 3 5 0 # n
111: 60 24 3 5 0 # o
112: 0 30 3 5 0 # p
113: 4 30 3 5 0 # q
114: 8 30 3 5 0 # r
115: 12 30 3 5 0 # s
116: 16 30 3 5 0 # t
117: 20 30 3 5 0 # u
118: 24 30 3 5 0 # v
119: 28 30 3 5 0 # w
120: 32 30 3 5 0 # x
121: 36 30 3 5 0 # y
122: 40 30 3 5 0 # z
123: 44 30 3 5 0 # {
124: 48 30 3 5 0 # |
125: 52 30 3 5 0 # }
126: 56 30 3 5 0 # ~
127: 60 30 3 5 0 # ⌂ (DEL placeholder)
# Extended characters
161: 0 36 3 5 0 # ¡
191: 4 36 3 5 0 # ¿
192: 8 36 3 8 3 # À
193: 12 36 3 8 3 # Á
200: 16 36 3 8 3 # È
201: 20 36 3 8 3 # É
204: 24 36 3 8 3 # Ì
205: 28 36 3 8 3 # Í
210: 32 36 3 8 3 # Ò
211: 36 36 3 8 3 # Ó
217: 40 36 3 8 3 # Ù
218: 44 36 3 8 3 # Ú
209: 48 36 3 7 2 # Ñ
241: 52 36 3 6 1 # ñ
199: 56 36 3 5 0 # Ç
231: 60 36 3 5 0 # ç
224: 0 45 3 7 2 # à
225: 4 45 3 7 2 # á
232: 8 45 3 7 2 # è
233: 12 45 3 7 2 # é
236: 16 45 3 7 2 # ì
237: 20 45 3 7 2 # í
242: 24 45 3 7 2 # ò
243: 28 45 3 7 2 # ó
249: 32 45 3 7 2 # ù
250: 36 45 3 7 2 # ú

BIN
default_font.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

35
do_release.bat Normal file
View File

@@ -0,0 +1,35 @@
@echo off
REM Comprobar parámetro
IF "%1"=="" (
echo Uso: build_windows.bat ^<PARAMETRO^>
exit /b 1
)
set PARAM=%1
echo Compilando windows...
rmdir /S /Q build || exit /b 1
lagueirto windows || exit /b 1
echo Compilando windows_debug...
rmdir /S /Q build || exit /b 1
lagueirto windows_debug || exit /b 1
echo Creando paquetes...
copy bin\SDL3.dll .
copy bin\libwinpthread-1.dll .
REM Crear ZIP release con mini.exe + DLLs
tar -a -c -f mini_%PARAM%_win32-x64_release.zip mini.exe *.dll || exit /b 1
REM Crear ZIP debug solo con mini_debug.exe
tar -a -c -f mini_%PARAM%_win32-x64_debug.zip mini_debug.exe *.dll || exit /b 1
del SDL3.dll
del libwinpthread-1.dll
echo Paquetes generados:
echo mini_%PARAM%_win32-x64_release.zip
echo mini_%PARAM%_win32-x64_debug.zip

43
do_release.sh Executable file
View File

@@ -0,0 +1,43 @@
#!/bin/bash
set -e
#if [ -z "$1" ]; then
# echo "Uso: $0 <PARAMETRO>"
# exit 1
#fi
# Leer versión desde version.h
VERSION=$(grep '#define MINI_VERSION' source/mini/version.h | sed 's/.*"\(.*\)".*/\1/')
echo "Versión detectada: $VERSION"
#PARAM=$1
# Datos Windows
WIN_USER="raimon"
WIN_HOST="192.168.1.53"
WIN_PATH_SSH="C:\Users\raimon\dev\mini"
WIN_PATH_SCP="C:/Users/Raimon/dev/mini"
echo "=== Compilando Linux ==="
rm -rf build
lagueirto linux
rm -rf build
lagueirto linux_debug
echo "=== Empaquetando Linux ==="
tar -czf mini_v${VERSION}_linux_release.tar.gz mini
tar -czf mini_v${VERSION}_linux_debug.tar.gz mini_debug
echo "=== Ejecutando build remoto Windows ==="
ssh ${WIN_USER}@${WIN_HOST} "cd ${WIN_PATH_SSH} && do_release.bat v${VERSION}"
echo "=== Copiando ZIPs desde Windows ==="
scp ${WIN_USER}@${WIN_HOST}:"${WIN_PATH_SCP}/mini_v${VERSION}_win32-x64_release.zip" .
scp ${WIN_USER}@${WIN_HOST}:"${WIN_PATH_SCP}/mini_v${VERSION}_win32-x64_debug.zip" .
echo "=== Build completado correctamente ==="
echo "Generados:"
echo " mini_v${VERSION}_linux_release.tar.gz"
echo " mini_v${VERSION}_linux_debug.tar.gz"
echo " mini_v${VERSION}_win32-x64_release.zip"
echo " mini_v${VERSION}_win32-x64_debug.zip"

View File

@@ -1,507 +0,0 @@
#ifndef JA_USESDLMIXER
#include "jail_audio.h"
#include "stb_vorbis.h"
#include <SDL3/SDL.h>
#include <stdio.h>
#include "log.h"
#define JA_MAX_SIMULTANEOUS_CHANNELS 5
struct JA_Sound_t
{
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
Uint32 length { 0 };
Uint8 *buffer { NULL };
};
struct JA_Channel_t
{
JA_Sound_t *sound { nullptr };
int pos { 0 };
int times { 0 };
SDL_AudioStream *stream { nullptr };
JA_Channel_state state { JA_CHANNEL_FREE };
};
struct JA_Music_t
{
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
Uint32 length { 0 };
Uint8 *buffer { nullptr };
int pos { 0 };
int times { 0 };
SDL_AudioStream *stream { nullptr };
JA_Music_state state { JA_MUSIC_INVALID };
};
JA_Music_t *current_music { nullptr };
JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS];
SDL_AudioSpec JA_audioSpec { SDL_AUDIO_S16, 2, 48000 };
float JA_musicVolume { 1.0f };
float JA_soundVolume { 0.5f };
bool JA_musicEnabled { true };
bool JA_soundEnabled { true };
SDL_AudioDeviceID sdlAudioDevice { 0 };
SDL_TimerID JA_timerID { 0 };
bool fading = false;
int fade_start_time;
int fade_duration;
int fade_initial_volume;
/*
void audioCallback(void * userdata, uint8_t * stream, int len) {
SDL_memset(stream, 0, len);
if (current_music != NULL && current_music->state == JA_MUSIC_PLAYING) {
const int size = SDL_min(len, current_music->samples*2-current_music->pos);
SDL_MixAudioFormat(stream, (Uint8*)(current_music->output+current_music->pos), AUDIO_S16, size, JA_musicVolume);
current_music->pos += size/2;
if (size < len) {
if (current_music->times != 0) {
SDL_MixAudioFormat(stream+size, (Uint8*)current_music->output, AUDIO_S16, len-size, JA_musicVolume);
current_music->pos = (len-size)/2;
if (current_music->times > 0) current_music->times--;
} else {
current_music->pos = 0;
current_music->state = JA_MUSIC_STOPPED;
}
}
}
// Mixar els channels mi amol
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].state == JA_CHANNEL_PLAYING) {
const int size = SDL_min(len, channels[i].sound->length - channels[i].pos);
SDL_MixAudioFormat(stream, channels[i].sound->buffer + channels[i].pos, AUDIO_S16, size, JA_soundVolume);
channels[i].pos += size;
if (size < len) {
if (channels[i].times != 0) {
SDL_MixAudioFormat(stream + size, channels[i].sound->buffer, AUDIO_S16, len-size, JA_soundVolume);
channels[i].pos = len-size;
if (channels[i].times > 0) channels[i].times--;
} else {
JA_StopChannel(i);
}
}
}
}
}
*/
Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval)
{
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING)
{
if (fading) {
int time = SDL_GetTicks();
if (time > (fade_start_time+fade_duration)) {
fading = false;
JA_StopMusic();
return 30;
} else {
const int time_passed = time - fade_start_time;
const float percent = (float)time_passed / (float)fade_duration;
SDL_SetAudioStreamGain(current_music->stream, 1.0 - percent);
}
}
if (current_music->times != 0)
{
if (SDL_GetAudioStreamAvailable(current_music->stream) < int(current_music->length/2)) {
SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length);
}
if (current_music->times>0) current_music->times--;
}
else
{
if (SDL_GetAudioStreamAvailable(current_music->stream) == 0) JA_StopMusic();
}
}
if (JA_soundEnabled)
{
for (int i=0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i)
if (channels[i].state == JA_CHANNEL_PLAYING)
{
if (channels[i].times != 0)
{
if (SDL_GetAudioStreamAvailable(channels[i].stream) < int(channels[i].sound->length/2))
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
if (channels[i].times>0) channels[i].times--;
}
}
else
{
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i);
}
}
return 30;
}
void JA_Init(const int freq, const SDL_AudioFormat format, const int channels)
{
#ifdef DEBUG
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
#endif
JA_audioSpec = {format, channels, freq };
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
if (!sdlAudioDevice) {
log_msg(LOG_FAIL, "Failed to initialize SDL audio: %s\n", SDL_GetError());
} else {
log_msg(LOG_OK, "Audio subsytem initialized\n");
}
//SDL_PauseAudioDevice(sdlAudioDevice);
JA_timerID = SDL_AddTimer(30, JA_UpdateCallback, nullptr);
}
void JA_Quit()
{
if (JA_timerID) SDL_RemoveTimer(JA_timerID);
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
sdlAudioDevice = 0;
}
JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length)
{
JA_Music_t *music = new JA_Music_t();
int chan, samplerate;
short *output;
music->length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2;
music->spec.channels = chan;
music->spec.freq = samplerate;
music->spec.format = SDL_AUDIO_S16;
music->buffer = (Uint8*)SDL_malloc(music->length);
SDL_memcpy(music->buffer, output, music->length);
free(output);
music->pos = 0;
music->state = JA_MUSIC_STOPPED;
return music;
}
JA_Music_t *JA_LoadMusic(const char* filename)
{
// [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid.
FILE *f = fopen(filename, "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
Uint8 *buffer = (Uint8*)malloc(fsize + 1);
if (fread(buffer, fsize, 1, f)!=1) return NULL;
fclose(f);
JA_Music_t *music = JA_LoadMusic(buffer, fsize);
free(buffer);
return music;
}
void JA_PlayMusic(JA_Music_t *music, const int loop)
{
if (!JA_musicEnabled) return;
JA_StopMusic();
current_music = music;
current_music->pos = 0;
current_music->state = JA_MUSIC_PLAYING;
current_music->times = loop;
current_music->stream = SDL_CreateAudioStream(&current_music->spec, &JA_audioSpec);
if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) log_msg(LOG_FAIL, "SDL_PutAudioStreamData failed!\n");
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) log_msg(LOG_FAIL, "SDL_BindAudioStream failed!\n");
//SDL_ResumeAudioStreamDevice(current_music->stream);
}
void JA_PauseMusic()
{
if (!JA_musicEnabled) return;
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
current_music->state = JA_MUSIC_PAUSED;
//SDL_PauseAudioStreamDevice(current_music->stream);
SDL_UnbindAudioStream(current_music->stream);
}
void JA_ResumeMusic()
{
if (!JA_musicEnabled) return;
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
current_music->state = JA_MUSIC_PLAYING;
//SDL_ResumeAudioStreamDevice(current_music->stream);
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
}
void JA_StopMusic()
{
if (!JA_musicEnabled) return;
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
current_music->pos = 0;
current_music->state = JA_MUSIC_STOPPED;
//SDL_PauseAudioStreamDevice(current_music->stream);
SDL_DestroyAudioStream(current_music->stream);
current_music->stream = nullptr;
}
void JA_FadeOutMusic(const int milliseconds)
{
if (!JA_musicEnabled) return;
if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return;
fading = true;
fade_start_time = SDL_GetTicks();
fade_duration = milliseconds;
fade_initial_volume = JA_musicVolume;
}
JA_Music_state JA_GetMusicState()
{
if (!JA_musicEnabled) return JA_MUSIC_DISABLED;
if (!current_music) return JA_MUSIC_INVALID;
return current_music->state;
}
void JA_DeleteMusic(JA_Music_t *music)
{
if (current_music == music) current_music = nullptr;
SDL_free(music->buffer);
if (music->stream) SDL_DestroyAudioStream(music->stream);
delete music;
}
float JA_SetMusicVolume(float volume)
{
JA_musicVolume = SDL_clamp( volume, 0.0f, 1.0f );
if (current_music) SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
return JA_musicVolume;
}
void JA_SetMusicPosition(float value)
{
if (!current_music) return;
current_music->pos = value * current_music->spec.freq;
}
float JA_GetMusicPosition()
{
if (!current_music) return 0;
return float(current_music->pos)/float(current_music->spec.freq);
}
void JA_EnableMusic(const bool value)
{
if ( !value && current_music && (current_music->state==JA_MUSIC_PLAYING) ) JA_StopMusic();
JA_musicEnabled = value;
}
const bool JA_IsMusicEnabled()
{
return JA_musicEnabled;
}
JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length)
{
JA_Sound_t *sound = new JA_Sound_t();
sound->buffer = buffer;
sound->length = length;
return sound;
}
JA_Sound_t *JA_LoadSound(uint8_t* buffer, uint32_t size)
{
JA_Sound_t *sound = new JA_Sound_t();
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &sound->spec, &sound->buffer, &sound->length);
return sound;
}
JA_Sound_t *JA_LoadSound(const char* filename)
{
JA_Sound_t *sound = new JA_Sound_t();
SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length);
return sound;
}
int JA_PlaySound(JA_Sound_t *sound, const int loop)
{
if (!JA_soundEnabled) return -1;
int channel = 0;
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; }
if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) channel = 0;
JA_StopChannel(channel);
channels[channel].sound = sound;
channels[channel].times = loop;
channels[channel].pos = 0;
channels[channel].state = JA_CHANNEL_PLAYING;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume);
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
return channel;
}
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop)
{
if (!JA_soundEnabled) return -1;
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return -1;
JA_StopChannel(channel);
channels[channel].sound = sound;
channels[channel].times = loop;
channels[channel].pos = 0;
channels[channel].state = JA_CHANNEL_PLAYING;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume);
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
return channel;
}
void JA_DeleteSound(JA_Sound_t *sound)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].sound == sound) JA_StopChannel(i);
}
SDL_free(sound->buffer);
delete sound;
}
void JA_PauseChannel(const int channel)
{
if (!JA_soundEnabled) return;
if (channel == -1)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
if (channels[i].state == JA_CHANNEL_PLAYING)
{
channels[i].state = JA_CHANNEL_PAUSED;
//SDL_PauseAudioStreamDevice(channels[i].stream);
SDL_UnbindAudioStream(channels[i].stream);
}
}
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
if (channels[channel].state == JA_CHANNEL_PLAYING)
{
channels[channel].state = JA_CHANNEL_PAUSED;
//SDL_PauseAudioStreamDevice(channels[channel].stream);
SDL_UnbindAudioStream(channels[channel].stream);
}
}
}
void JA_ResumeChannel(const int channel)
{
if (!JA_soundEnabled) return;
if (channel == -1)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
if (channels[i].state == JA_CHANNEL_PAUSED)
{
channels[i].state = JA_CHANNEL_PLAYING;
//SDL_ResumeAudioStreamDevice(channels[i].stream);
SDL_BindAudioStream(sdlAudioDevice, channels[i].stream);
}
}
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
if (channels[channel].state == JA_CHANNEL_PAUSED)
{
channels[channel].state = JA_CHANNEL_PLAYING;
//SDL_ResumeAudioStreamDevice(channels[channel].stream);
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
}
}
}
void JA_StopChannel(const int channel)
{
if (!JA_soundEnabled) return;
if (channel == -1)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[i].stream);
channels[i].stream = nullptr;
channels[i].state = JA_CHANNEL_FREE;
channels[i].pos = 0;
channels[i].sound = NULL;
}
}
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
if (channels[channel].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[channel].stream);
channels[channel].stream = nullptr;
channels[channel].state = JA_CHANNEL_FREE;
channels[channel].pos = 0;
channels[channel].sound = NULL;
}
}
JA_Channel_state JA_GetChannelState(const int channel)
{
if (!JA_soundEnabled) return JA_SOUND_DISABLED;
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return JA_CHANNEL_INVALID;
return channels[channel].state;
}
float JA_SetSoundVolume(float volume)
{
JA_soundVolume = SDL_clamp( volume, 0.0f, 1.0f );
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
if ( (channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED) )
SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume);
return JA_soundVolume;
}
void JA_EnableSound(const bool value)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
{
if (channels[i].state == JA_CHANNEL_PLAYING) JA_StopChannel(i);
}
JA_soundEnabled = value;
}
const bool JA_IsSoundEnabled()
{
return JA_soundEnabled;
}
float JA_SetVolume(float volume)
{
JA_SetSoundVolume(JA_SetMusicVolume(volume) / 2.0f);
return JA_musicVolume;
}
#endif

View File

@@ -1,42 +0,0 @@
#pragma once
#include <SDL3/SDL.h>
enum JA_Channel_state { JA_CHANNEL_INVALID, JA_CHANNEL_FREE, JA_CHANNEL_PLAYING, JA_CHANNEL_PAUSED, JA_SOUND_DISABLED };
enum JA_Music_state { JA_MUSIC_INVALID, JA_MUSIC_PLAYING, JA_MUSIC_PAUSED, JA_MUSIC_STOPPED, JA_MUSIC_DISABLED };
struct JA_Sound_t;
struct JA_Music_t;
void JA_Init(const int freq, const SDL_AudioFormat format, const int channels);
void JA_Quit();
JA_Music_t *JA_LoadMusic(const char* filename);
JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length);
void JA_PlayMusic(JA_Music_t *music, const int loop = -1);
void JA_PauseMusic();
void JA_ResumeMusic();
void JA_StopMusic();
void JA_FadeOutMusic(const int milliseconds);
JA_Music_state JA_GetMusicState();
void JA_DeleteMusic(JA_Music_t *music);
float JA_SetMusicVolume(float volume);
void JA_SetMusicPosition(float value);
float JA_GetMusicPosition();
void JA_EnableMusic(const bool value);
const bool JA_IsMusicEnabled();
JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length);
JA_Sound_t *JA_LoadSound(Uint8* buffer, Uint32 length);
JA_Sound_t *JA_LoadSound(const char* filename);
int JA_PlaySound(JA_Sound_t *sound, const int loop = 0);
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop = 0);
void JA_PauseChannel(const int channel);
void JA_ResumeChannel(const int channel);
void JA_StopChannel(const int channel);
JA_Channel_state JA_GetChannelState(const int channel);
void JA_DeleteSound(JA_Sound_t *sound);
float JA_SetSoundVolume(float volume);
void JA_EnableSound(const bool value);
const bool JA_IsSoundEnabled();
float JA_SetVolume(float volume);

379
jfile.cpp
View File

@@ -1,379 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "jfile.h"
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <string>
#include <algorithm>
#include <dirent.h> // Para opendir/readdir en SOURCE_FOLDER
#ifndef _WIN32
#include <pwd.h>
#endif
#define DEFAULT_FILENAME "data.jf2"
#define DEFAULT_FOLDER "data/"
#define CONFIG_FILENAME "config.txt"
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 file_setresourcefilename(const char *str) {
if (resource_filename != NULL) free(resource_filename);
resource_filename = (char*)malloc(strlen(str)+1);
strcpy(resource_filename, str);
}
void file_setresourcefolder(const char *str) {
if (resource_folder != NULL) free(resource_folder);
resource_folder = (char*)malloc(strlen(str)+1);
strcpy(resource_folder, str);
}
void file_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) file_setresourcefolder(DEFAULT_FOLDER);
}
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 (uint32_t i=0; i<num_files; ++i)
{
uint32_t file_offset, file_size;
fi.read( (char*)&file_offset, 4 );
fi.read( (char*)&file_size, 4 );
uint8_t path_size;
fi.read( (char*)&path_size, 1 );
char file_name[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 *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;
}
char *file_getfilebuffer(const char *resourcename, int& filesize, const bool zero_terminate) {
FILE *f = file_getfilepointer(resourcename, filesize, true);
char* buffer = (char*)malloc(zero_terminate?filesize:filesize+1);
fread(buffer, filesize, 1, f);
if (zero_terminate) buffer[filesize]=0;
fclose(f);
return buffer;
}
FILE *file_getfilepointerex(const char *filename, int& filesize, const bool binary) {
FILE *f;
f = fopen(filename, binary?"rb":"r");
fseek(f, 0, SEEK_END);
filesize = ftell(f);
fseek(f, 0, SEEK_SET);
return f;
}
char *file_getfilebufferex(const char *filename, int& filesize, const bool zero_terminate) {
FILE *f = file_getfilepointerex(filename, filesize, true);
char* buffer = (char*)malloc(zero_terminate?filesize:filesize+1);
fread(buffer, filesize, 1, f);
if (zero_terminate) buffer[filesize]=0;
fclose(f);
return buffer;
}
// Crea la carpeta del sistema donde guardar datos
void file_setconfigfolder(const char *foldername)
{
#ifdef _WIN32
config_folder = std::string(getenv("APPDATA")) + "/" + foldername;
#elif __APPLE__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
#elif __linux__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
config_folder = std::string(homedir) + "/." + foldername;
config_folder = std::string(homedir) + "/.config/jailgames/" + foldername;
{
// Intenta crear ".config", per si no existeix
std::string config_base_folder = std::string(homedir) + "/.config";
int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
if (ret == -1 && errno != EEXIST)
{
printf("ERROR CREATING CONFIG BASE FOLDER.");
exit(EXIT_FAILURE);
}
}
{
// Intenta crear ".config/jailgames", per si no existeix
std::string config_base_folder = std::string(homedir) + "/.config/jailgames";
int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
if (ret == -1 && errno != EEXIST)
{
printf("ERROR CREATING CONFIG BASE FOLDER.");
exit(EXIT_FAILURE);
}
}
#endif
struct stat st = {0};
if (stat(config_folder.c_str(), &st) == -1)
{
#ifdef _WIN32
int ret = mkdir(config_folder.c_str());
#else
int ret = mkdir(config_folder.c_str(), S_IRWXU);
#endif
if (ret == -1)
{
printf("ERROR CREATING CONFIG FOLDER.");
exit(EXIT_FAILURE);
}
}
}
const char *file_getconfigfolder() {
static std::string folder = config_folder + "/";
return folder.c_str();
}
void file_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 file_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* file_getconfigvalue(const char *key) {
if (config.empty()) file_loadconfigvalues();
for (auto pair : config) {
if (pair.key == std::string(key)) {
strcpy(scratch, pair.value.c_str());
return scratch;
}
}
return NULL;
}
void file_setconfigvalue(const char* key, const char* value) {
if (config.empty()) file_loadconfigvalues();
for (auto &pair : config) {
if (pair.key == std::string(key)) {
pair.value = value;
file_saveconfigvalues();
return;
}
}
config.push_back({key, value});
file_saveconfigvalues();
return;
}
bool file_createFolder(const char* name) {
char tmp[256];
strcpy(tmp, "./");
strcat(tmp, name);
return mkdir(tmp, 0755)==0;
}
static bool has_extension(const std::string &name, const char *ext)
{
if (!ext) return true; // sin filtro
std::string e = ext;
std::string suffix = "." + e;
if (name.size() < suffix.size())
return false;
return (name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0);
}
std::vector<std::string> file_listdir(const char *folder, const char *extension)
{
std::vector<std::string> result;
std::string base(folder);
// Normalizar: quitar "/" final si existe
if (!base.empty() && base.back() == '/')
base.pop_back();
// -------------------------------
// 1. MODO: ARCHIVOS SUELTOS
// -------------------------------
if (file_source == SOURCE_FOLDER)
{
std::string fullpath = std::string(resource_folder) + base;
DIR *dir = opendir(fullpath.c_str());
if (!dir)
return result;
struct dirent *entry;
while ((entry = readdir(dir)) != nullptr)
{
std::string name = entry->d_name;
// Ignorar "." y ".."
if (name == "." || name == "..")
continue;
// Ignorar subdirectorios
std::string full = fullpath + "/" + name;
DIR *test = opendir(full.c_str());
if (test)
{
closedir(test);
continue; // es un directorio
}
// Filtrar por extensión
if (!has_extension(name, extension))
continue;
result.push_back(name);
}
closedir(dir);
return result;
}
// -------------------------------
// 2. MODO: ARCHIVO CONTENEDOR
// -------------------------------
if (file_source == SOURCE_FILE)
{
std::string prefix = base + "/";
for (auto &f : toc)
{
const std::string &path = f.path;
// Debe empezar por "folder/"
if (path.compare(0, prefix.size(), prefix) != 0)
continue;
// Extraer la parte después de "folder/"
std::string rest = path.substr(prefix.size());
// Ignorar subdirectorios
if (rest.find('/') != std::string::npos)
continue;
// Filtrar por extensión
if (!has_extension(rest, extension))
continue;
result.push_back(rest);
}
return result;
}
return result;
}

26
jfile.h
View File

@@ -1,26 +0,0 @@
#pragma once
#include <stdio.h>
#include <vector>
#include <string>
#define SOURCE_FILE 0
#define SOURCE_FOLDER 1
void file_setconfigfolder(const char *foldername);
const char *file_getconfigfolder();
void file_setresourcefilename(const char *str);
void file_setresourcefolder(const char *str);
void file_setsource(const int src);
FILE *file_getfilepointer(const char *resourcename, int& filesize, const bool binary=false);
char *file_getfilebuffer(const char *resourcename, int& filesize, const bool zero_terminate=false);
FILE *file_getfilepointerex(const char *filename, int& filesize, const bool binary=false);
char *file_getfilebufferex(const char *filename, int& filesize, const bool zero_terminate=false);
const char* file_getconfigvalue(const char *key);
void file_setconfigvalue(const char* key, const char* value);
bool file_createFolder(const char* name);
std::vector<std::string> file_listdir(const char *folder, const char *extension=NULL);

View File

@@ -1,250 +0,0 @@
#include "jshader.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_Point win_size = {640, 480};
SDL_FPoint tex_size = {320, 240};
bool can_use_opengl = false;
bool using_opengl = false;
GLuint texture_number;
GLuint nose;
#ifndef __APPLE__
// I'm avoiding the use of GLEW or some extensions handler, but that
// doesn't mean you should...
PFNGLCREATESHADERPROC glCreateShader;
PFNGLSHADERSOURCEPROC glShaderSource;
PFNGLCOMPILESHADERPROC glCompileShader;
PFNGLGETSHADERIVPROC glGetShaderiv;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
PFNGLDELETESHADERPROC glDeleteShader;
PFNGLATTACHSHADERPROC glAttachShader;
PFNGLCREATEPROGRAMPROC glCreateProgram;
PFNGLDELETEPROGRAMPROC glDeleteProgram;
PFNGLLINKPROGRAMPROC glLinkProgram;
PFNGLVALIDATEPROGRAMPROC glValidateProgram;
PFNGLGETPROGRAMIVPROC glGetProgramiv;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
PFNGLUSEPROGRAMPROC glUseProgram;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
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");
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glDeleteProgram && glLinkProgram && glValidateProgram && glGetProgramiv &&
glGetProgramInfoLog && glUseProgram && glGetUniformLocation;
}
#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;
SDL_GetWindowSize(win, &win_size.x, &win_size.y);
SDL_GetTextureSize(backBuffer, &tex_size.x, &tex_size.y);
//printf("tex size: %fx%f\n", tex_size.x, tex_size.y);
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
texture_number = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, -1);
//printf("texture number: %i\n", texture_number);
int access = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, -1);
nose = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER, -1);
//printf("texture target number: %i\n", nose);
if (access != SDL_TEXTUREACCESS_TARGET)
{
std::cout << "ERROR FATAL: La textura per al render ha de tindre SDL_TEXTUREACCESS_TARGET definit." << std::endl;
exit(1);
}
const char * renderer_name = SDL_GetRendererName(renderer);
//printf("rendererInfo.name: %s\n", renderer_name);
if(!strncmp(renderer_name, "opengl", 6)) {
#ifndef __APPLE__
static bool gl_extensions_initialized = false;
if (!gl_extensions_initialized) {
if (!initGLExtensions()) {
std::cout << "WARNING: No s'han pogut inicialitzar les extensions d'OpenGL!" << std::endl;
can_use_opengl = false;
return false;
}
gl_extensions_initialized = true;
}
#endif
// Compilar el shader y dejarlo listo para usar.
if (!vertexShader) {
can_use_opengl = false;
return false;
}
programId = compileProgram(vertexShader, fragmentShader);
} else {
std::cout << "WARNING: El driver del renderer no es OpenGL." << std::endl;
can_use_opengl = false;
return false;
}
can_use_opengl = true;
return true;
}
unsigned char pixels[512*240*4];
void enable() { if (can_use_opengl) using_opengl = true; }
void disable() { using_opengl = false; }
void render()
{
SDL_FlushRenderer(renderer);
SDL_SetRenderTarget(renderer, NULL);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_FlushRenderer(renderer);
if (using_opengl)
{
GLint oldProgramId;
if (programId != 0)
{
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
glUseProgram(programId);
}
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 1);
//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(0, 0, win_size.x, win_size.y);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(-1.0f, -1.0f);
glTexCoord2f(tex_size.x, 0.0f);
glVertex2f(1.0f, -1.0f);
glTexCoord2f(0.0f, tex_size.y);
glVertex2f(-1.0f, 1.0f);
glTexCoord2f(tex_size.x, tex_size.y);
glVertex2f(1.0f, 1.0f);
glEnd();
SDL_GL_SwapWindow(win);
if (programId != 0) glUseProgram(oldProgramId);
} else {
SDL_RenderTexture(renderer, backBuffer, NULL, NULL);
SDL_RenderPresent(renderer);
}
if (glGetError()) { printf("GLERROR!\n"); exit(1); }
}
}

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 enable();
void disable();
void render();
}

View File

@@ -1,5 +1,27 @@
libs = -lSDL3 -lGL [linux]
cppflags = -D LUA_USE_LINUX -D DEBUG -g -Wall cppflags = -D LUA_USE_LINUX -Wall -Os -ffunction-sections -fdata-sections -std=c++20 -Isource
executable = mini_debug libs = -Wl,--gc-sections -lSDL3 -lGL
sourcepath = . lua executable = mini
sourcepath = source+
buildpath = build
[linux_debug] default
cppflags = -D LUA_USE_LINUX -D DEBUG -g -Wall -std=c++20 -Isource
libs = -lSDL3 -lGL
executable = mini_debug
sourcepath = source+
buildpath = build
[windows]
cppflags = -Wall -Os -ffunction-sections -fdata-sections -std=c++20 -Isource
libs = icon.res -Wl,--gc-sections -lmingw32 -lSDL3 -lopengl32 -static-libstdc++ -static-libgcc -lpthread -mwindows
executable = mini.exe
sourcepath = source+
buildpath = build
[windows_debug]
cppflags = -D DEBUG -g -Wall -std=c++20 -Isource
libs = -lmingw32 -lSDL3 -lopengl32
executable = mini_debug.exe
sourcepath = source+
buildpath = build buildpath = build

1435
lua.cpp

File diff suppressed because it is too large Load Diff

7
lua.h
View File

@@ -1,7 +0,0 @@
#pragma once
bool lua_is_playing();
void lua_init(const char* main_lua_file = "main.lua");
void lua_call_init();
void lua_call_update();
void lua_quit();

View File

@@ -1,9 +0,0 @@
#include "mini.h"
void loop() {
settrans(255);
setcolor(0,0xff0000);
const int w=scrw();
const int h=scrh();
rect(0,0,w-1,h-1,0);
}

1585
mini.cpp

File diff suppressed because it is too large Load Diff

282
mini.h
View File

@@ -1,282 +0,0 @@
#pragma once
#include <SDL3/SDL.h>
#include "version.h"
#define KEY_UNKNOWN 0
#define KEY_A 4
#define KEY_B 5
#define KEY_C 6
#define KEY_D 7
#define KEY_E 8
#define KEY_F 9
#define KEY_G 10
#define KEY_H 11
#define KEY_I 12
#define KEY_J 13
#define KEY_K 14
#define KEY_L 15
#define KEY_M 16
#define KEY_N 17
#define KEY_O 18
#define KEY_P 19
#define KEY_Q 20
#define KEY_R 21
#define KEY_S 22
#define KEY_T 23
#define KEY_U 24
#define KEY_V 25
#define KEY_W 26
#define KEY_X 27
#define KEY_Y 28
#define KEY_Z 29
#define KEY_1 30
#define KEY_2 31
#define KEY_3 32
#define KEY_4 33
#define KEY_5 34
#define KEY_6 35
#define KEY_7 36
#define KEY_8 37
#define KEY_9 38
#define KEY_0 39
#define KEY_RETURN 40
#define KEY_ESCAPE 41
#define KEY_BACKSPACE 42
#define KEY_TAB 43
#define KEY_SPACE 44
#define KEY_MINUS 45
#define KEY_EQUALS 46
#define KEY_LEFTBRACKET 47
#define KEY_RIGHTBRACKET 48
#define KEY_BACKSLASH 49
#define KEY_NONUSHASH 50
#define KEY_SEMICOLON 51
#define KEY_APOSTROPHE 52
#define KEY_GRAVE 53
#define KEY_COMMA 54
#define KEY_PERIOD 55
#define KEY_SLASH 56
#define KEY_CAPSLOCK 57
#define KEY_F1 58
#define KEY_F2 59
#define KEY_F3 60
#define KEY_F4 61
#define KEY_F5 62
#define KEY_F6 63
#define KEY_F7 64
#define KEY_F8 65
#define KEY_F9 66
#define KEY_F10 67
#define KEY_F11 68
#define KEY_F12 69
#define KEY_PRINTSCREEN 70
#define KEY_SCROLLLOCK 71
#define KEY_PAUSE 72
#define KEY_INSERT 73
#define KEY_HOME 74
#define KEY_PAGEUP 75
#define KEY_DELETE 76
#define KEY_END 77
#define KEY_PAGEDOWN 78
#define KEY_RIGHT 79
#define KEY_LEFT 80
#define KEY_DOWN 81
#define KEY_UP 82
#define KEY_NUMLOCKCLEAR 83
#define KEY_KP_DIVIDE 84
#define KEY_KP_MULTIPLY 85
#define KEY_KP_MINUS 86
#define KEY_KP_PLUS 87
#define KEY_KP_ENTER 88
#define KEY_KP_1 89
#define KEY_KP_2 90
#define KEY_KP_3 91
#define KEY_KP_4 92
#define KEY_KP_5 93
#define KEY_KP_6 94
#define KEY_KP_7 95
#define KEY_KP_8 96
#define KEY_KP_9 97
#define KEY_KP_0 98
#define KEY_KP_PERIOD 99
#define KEY_NONUSBACKSLASH 100
#define KEY_APPLICATION 101
#define KEY_LCTRL 224
#define KEY_LSHIFT 225
#define KEY_LALT 226
#define KEY_LGUI 227
#define KEY_RCTRL 228
#define KEY_RSHIFT 229
#define KEY_RALT 230
#define KEY_RGUI 231
#define DRAWMODE_NORMAL 0
#define DRAWMODE_PATTERN 1
#define DRAWMODE_AND 2
#define DRAWMODE_OR 3
#define DRAWMODE_XOR 4
#define DRAWMODE_NOT 5
void loop();
int scrw();
int scrh();
uint8_t newsurf(int w, int h);
uint8_t loadsurf(const char* filename, const bool external = false);
void savesurf(uint8_t surface, const char* filename, uint8_t *pal, uint8_t colors=0);
void freesurf(uint8_t surface);
int surfw(uint8_t surface);
int surfh(uint8_t surface);
void setdest(uint8_t surface);
void setsource(uint8_t surface);
void setmap(uint8_t surface);
uint8_t getdest();
uint8_t getsource();
uint8_t getmap();
void shader_init(const char* vshader, const char* fshader);
void shader_enable();
void shader_disable();
void cls(uint8_t color=0);
void color(uint8_t color=6);
void bcolor(uint8_t color=0);
uint32_t *loadpal(const char* filename, uint16_t *palsize=NULL);
void setpal(uint32_t *pal);
void setcolor(uint8_t index, uint32_t color);
uint32_t getcolor(uint8_t index);
void settrans(uint8_t index);
uint8_t gettrans();
void subpal(uint8_t index, uint8_t color);
void reset_subpal();
void set_draw_mode(uint8_t mode);
void pset(int x, int y);
void pset(int x, int y, uint8_t color);
uint8_t pget(int x, int y);
void line(int x0, int y0, int x1, int y1);
void line(int x0, int y0, int x1, int y1, uint8_t color);
void hline(int x0, int y, int x1);
void hline(int x0, int y, int x1, uint8_t color);
void vline(int x, int y0, int y1);
void vline(int x, int y0, int y1, uint8_t color);
void rect(int x, int y, int w, int h);
void rect(int x, int y, int w, int h, uint8_t color);
void rectfill(int x, int y, int w, int h);
void rectfill(int x, int y, int w, int h, uint8_t color);
void fillp(uint16_t pat, bool transparent = false);
void print(const char *str, int x, int y);
void print(const char *str, int x, int y, uint8_t color);
void clip(int x, int y, int w, int h);
void clip();
void origin(int x, int y);
int camx();
int camy();
void circ(int x, int y, uint8_t r = 4);
void circ(int x, int y, uint8_t r, uint8_t color);
void circfill(int x, int y, uint8_t r = 4);
void circfill(int x, int y, uint8_t r, uint8_t color);
void roundrect(int x, int y, int w, int h, uint8_t r);
void roundrect(int x, int y, int w, int h, uint8_t r, uint8_t color);
void roundrectfill(int x, int y, int w, int h, uint8_t r);
void roundrectfill(int x, int y, int w, int h, uint8_t r, uint8_t color);
void oval(int x0, int y0, int x1, int y1);
void oval(int x0, int y0, int x1, int y1, uint8_t color);
void ovalfill(int x0, int y0, int x1, int y1);
void ovalfill(int x0, int y0, int x1, int y1, uint8_t color);
uint8_t sget(int x, int y);
void sset(int x, int y);
void sset(int x, int y, uint8_t color);
void spr(uint8_t n, int x, int y, float w = 1.0f, float h = 1.0f, bool flip_x = false, bool flip_y = false);
void blit(int sx, int sy, int sw, int sh, int dx, int dy, int dw=0, int dh=0, bool flip_x = false, bool flip_y = false, bool invert = false);
void blit_r(int sx, int sy, int sw, int sh, int x, int y, float a);
void tline(int x0, int y0, int x1, int y1, float mx, float my, float mdx=0.125f, float mdy=0.0f);
void thline(int x0, int y, int x1, float mx, float my, float mdx=0.125f, float mdy=0.0f);
void tvline(int x, int y0, int y1, float mx, float my, float mdx=0.0f, float mdy=0.125f);
uint8_t mget(int celx, int cely);
void mset(int celx, int cely, uint8_t snum);
void map(); //int celx, int cely, int sx, int sy, uint8_t celw, uint8_t celh, uint8_t layer=0);
bool btn(uint8_t i);
int wbtnp();
bool btnp(uint8_t i);
bool anykey();
bool pad(int8_t i);
bool padp(int8_t i);
int wpad();
int mousex();
int mousey();
int mwheel();
bool mbtn(uint8_t i);
bool mbtnp(uint8_t i);
bool doubleclick();
void mdiscard();
bool minside(int x, int y, int w, int h);
float time();
bool beat(int16_t i);
int rnd(int x);
int getfps();
void playmusic(const char *filename, const int loop=-1);
void pausemusic();
void resumemusic();
void stopmusic(const int t=1000);
void musicpos(float value);
float musicpos();
void enablemusic(const bool value);
const bool ismusicenabled();
int loadsound(const char *filename);
void freesound(int soundfile);
int playsound(int soundfile, const int volume=-1);
void stopsound(int soundchannel);
void enablesound(const bool value);
const bool issoundenabled();
int getzoom();
void setzoom(const int value);
void setres(const int w, const int h);
bool getfullscreen();
void setfullscreen(const bool value);
bool getcursor();
void setcursor(const bool value);
const char *getconfig(const char* key);
void setconfig(const char* key, const char* value);
const char *configfolder();
#define UPDATE_ALWAYS 0
#define UPDATE_WAIT 1
#define UPDATE_TIMEOUT 2
void setupdatemode(const int value, const int t=0);
int getupdatemode();
void exit();

57
publish_gitea.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/bin/bash
set -e
#if [ -z "$1" ]; then
# echo "Uso: $0 <PARAMETRO>"
# exit 1
#fi
#GITEA_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
if [ -z "$GITEA_TOKEN" ]; then
echo "ERROR: Debes exportar GITEA_TOKEN"
exit 1
fi
# Leer versión desde version.h
VERSION=$(grep '#define MINI_VERSION' source/mini/version.h | sed 's/.*"\(.*\)".*/\1/')
echo "Versión detectada: $VERSION"
#PARAM=$1
API="https://gitea.sustancia.synology.me/api/v1"
REPO="JailDoctor/mini"
echo "=== Creando release ${VERSION} en Gitea ==="
RELEASE_ID=$(curl -s -X POST "${API}/repos/${REPO}/releases" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"${VERSION}\",
\"name\": \"Release ${VERSION}\",
\"draft\": false,
\"prerelease\": false
}" | jq -r '.id')
if [ "$RELEASE_ID" = "null" ]; then
echo "ERROR: No se pudo crear el release"
exit 1
fi
echo "Release creado con ID: $RELEASE_ID"
echo "=== Subiendo artefactos ==="
for f in mini_v${VERSION}_linux_release.tar.gz \
mini_v${VERSION}_linux_debug.tar.gz \
mini_v${VERSION}_win32-x64_release.zip \
mini_v${VERSION}_win32-x64_debug.zip
do
echo "Subiendo $f..."
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@${f}" \
"${API}/repos/${REPO}/releases/${RELEASE_ID}/assets" > /dev/null
done
echo "=== Publicación completada ==="

View File

@@ -0,0 +1,96 @@
#if BACKEND == SDL3
#include "backends/backend.h"
#include "state.h"
#include "mini/win/win.h"
#include "mini/surf/surf.h"
namespace backend
{
state_t current_state = running;
void init() {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD);
//video::init();
}
void quit() {
SDL_Quit();
}
void poll_events() {
//[TODO]
//if (update_mode==UPDATE_WAIT)
// SDL_WaitEvent(NULL);
//else if (update_mode==UPDATE_TIMEOUT)
// SDL_WaitEventTimeout(NULL, timeout);
SDL_Event e;
while(SDL_PollEvent(&e)) {
if (e.type == SDL_EVENT_QUIT) {
current_state=quitting;
break;
}
else if (e.type == SDL_EVENT_TEXT_INPUT) {
SDL_strlcpy(input::key::text_input_buffer, e.text.text, sizeof(input::key::text_input_buffer));
input::key::has_text_input = true;
}
else if (e.type == SDL_EVENT_KEY_DOWN) {
#ifdef DEBUG
if (e.key.scancode == SDL_SCANCODE_F12) {
mini::surf::reloadsurfs();
//} else if (e.key.scancode == SDL_SCANCODE_F11) {
// mini::lua::debug::toggle();
} else if (e.key.scancode == SDL_SCANCODE_F5) {
current_state=exiting;
} else {
input::key::just_pressed = e.key.scancode;
}
#else
input::key::just_pressed = e.key.scancode;
#endif
}
else if (e.type == SDL_EVENT_MOUSE_BUTTON_UP) {
if (input::mouse::discard_buttons)
input::mouse::discard_buttons = false;
else
if (e.button.clicks==2 && e.button.button==SDL_BUTTON_LEFT) {
input::mouse::double_click = true;
} else {
input::mouse::just_pressed = e.button.button;
}
}
else if (e.type == SDL_EVENT_MOUSE_WHEEL) {
input::mouse::w = e.wheel.y;
}
else if (e.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
input::pad::just_pressed = e.gbutton.button;
}
}
input::key::keys = SDL_GetKeyboardState(NULL);
// Update mouse
float real_mouse_x, real_mouse_y;
input::mouse::buttons = SDL_GetMouseState(&real_mouse_x, &real_mouse_y);
float mx, my;
SDL_RenderCoordinatesFromWindow(video::renderer, real_mouse_x, real_mouse_y, &mx, &my);
input::mouse::x = int(mx/mini::win::state.zoom);
input::mouse::y = int(my/mini::win::state.zoom);
}
const state_t& state() {
return current_state;
}
char *clipboard() {
return SDL_GetClipboardText();
}
void clipboard(const char* value) {
SDL_SetClipboardText(value);
}
}
#endif

View File

@@ -0,0 +1,153 @@
#if BACKEND == SDL3
#include "backends/backend.h"
#include "state.h"
#include "mini/view/view.h"
namespace backend
{
namespace input
{
void reset() {
key::just_pressed = 0;
key::has_text_input = false;
key::text_input_buffer[0] = '\0';
mouse::just_pressed = 0;
mouse::double_click = false;
mouse::w = 0;
pad::just_pressed = SDL_GAMEPAD_BUTTON_INVALID;
}
namespace key
{
const bool *keys;
uint8_t just_pressed = 0;
char text_input_buffer[10];
bool has_text_input = false;
bool down(uint8_t i) {
return keys[i];
}
bool press(uint8_t i) {
if (just_pressed == i) {
just_pressed=0;
return true;
} else {
return false;
}
}
int press() {
return just_pressed;
}
bool any() {
const bool something_pressed = (just_pressed != 0) || (pad::just_pressed != -1);
just_pressed=0;
pad::just_pressed=-1;
return something_pressed;
}
void text(const bool enable) {
if (enable)
SDL_StartTextInput(backend::video::window);
else
SDL_StopTextInput(backend::video::window);
}
const char *utf8char() {
return has_text_input ? text_input_buffer : nullptr;
}
}
namespace mouse
{
int x, y, w;
uint32_t buttons;
uint8_t just_pressed = 0;
bool double_click = false;
bool discard_buttons = false;
int posx() {
return x - mini::view::state.origin[0];
}
int posy() {
return y - mini::view::state.origin[1];
}
int wheel() {
return w;
}
bool down(uint8_t i) {
if (discard_buttons) return false;
return buttons & SDL_BUTTON_MASK(i);
}
bool press(uint8_t i) {
return just_pressed == i;
}
bool dblclick() {
return double_click;
}
void discard() {
discard_buttons = true;
}
bool inside(int x, int y, int w, int h) {
const int mx = x - mini::view::state.origin[0];
const int my = y - mini::view::state.origin[1];
return (mx>=x) && (my>=y) && (mx<x+w) && (my<y+h);
}
}
namespace pad
{
SDL_Gamepad *gamepad = nullptr;
int8_t just_pressed = -1;
void init() {
int num_joysticks;
SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks);
if (joysticks) {
for (int i=0; i<num_joysticks; ++i) {
if (SDL_IsGamepad(joysticks[i])) {
gamepad = SDL_OpenGamepad(joysticks[i]);
if (SDL_GamepadConnected(gamepad)) {
SDL_SetGamepadEventsEnabled(true);
// [TODO]
//log_msg(LOG_OK, "Gamepad found and initialized");
return;
}
}
}
}
}
bool down(int8_t i) {
if (!gamepad) return false;
return SDL_GetGamepadButton(gamepad, SDL_GamepadButton(i)) == 1;
}
bool press(int8_t i) {
if (just_pressed == i) {
just_pressed = -1;
return true;
} else {
return false;
}
}
int press() {
return just_pressed;
}
}
}
}
#endif

View File

@@ -0,0 +1,43 @@
#if BACKEND == SDL3
#pragma once
#include <SDL3/SDL.h>
namespace backend
{
namespace video
{
extern SDL_Window *window;
extern SDL_Renderer *renderer;
extern SDL_Texture *tex_back;
extern SDL_Texture *tex_shader;
}
namespace input
{
namespace key
{
extern const bool *keys;
extern uint8_t just_pressed;
extern char text_input_buffer[10];
extern bool has_text_input;
}
namespace mouse
{
extern int x, y, w;
extern uint32_t buttons;
extern uint8_t just_pressed;
extern bool double_click;
extern bool discard_buttons;
}
namespace pad
{
extern SDL_Gamepad *gamepad;
extern int8_t just_pressed;
}
}
}
#endif

View File

@@ -0,0 +1,91 @@
#if BACKEND == SDL3
#include "backends/backend.h"
#include "state.h"
#include "mini/win/win.h"
#include "mini/surf/surf.h"
#include "mini/pal/pal.h"
#include "mini/shader/shader.h"
namespace backend
{
namespace video
{
SDL_Window *window { nullptr };
SDL_Renderer *renderer { nullptr };
SDL_Texture *tex_back { nullptr };
SDL_Texture *tex_shader { nullptr };
void init() {
const auto &title = mini::win::state.title;
const auto &width = mini::win::state.width;
const auto &height = mini::win::state.height;
auto &zoom = mini::win::state.zoom;
// Ajustar el zoom a un valor vàlid
if (zoom <= 0) zoom = 1;
const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay());
while ( width * zoom > dm->w || height * zoom > dm->h) zoom--;
// Crear SDL_Window i SDL_Renderer
window = SDL_CreateWindow(title, width*zoom, height*zoom, SDL_WINDOW_OPENGL|(mini::win::state.fullscreen?SDL_WINDOW_FULLSCREEN:0));
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
renderer = SDL_CreateRenderer(window, NULL);
SDL_SetRenderVSync(renderer, SDL_RENDERER_VSYNC_DISABLED);
// Mostrar o ocultar el cursor
if (mini::win::state.cursor) SDL_ShowCursor(); else SDL_HideCursor();
// Crear textura backbuffer
tex_back = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_SetTextureScaleMode(tex_back, SDL_SCALEMODE_NEAREST);
// Crear textura shaders i inicialitzar shaders
tex_shader = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, width*zoom, height*zoom);
SDL_SetTextureScaleMode(tex_shader, SDL_SCALEMODE_NEAREST);
mini::shader::init(window, tex_shader, nullptr);
// [TODO]
//log_msg(LOG_OK, "Graphics subsystem initialized\n");
}
void quit() {
SDL_DestroyTexture(tex_shader);
SDL_DestroyTexture(tex_back);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
}
void render() {
// Render frame
SDL_SetRenderTarget(renderer, tex_shader);
//SDL_SetRenderDrawColor(win::state.renderer, 0, 0, 0, 255);
//SDL_RenderClear(win::state.renderer);
uint32_t *pixels;
int pitch;
SDL_LockTexture(tex_back, NULL, (void**)&pixels, &pitch);
auto &s = mini::surf::state.surfaces[SCREEN];
uint8_t *p = s.p;
for (uint32_t i=0;i<s.size;++i) pixels[i] = mini::pal::palette[p[i]];
SDL_UnlockTexture(tex_back);
SDL_RenderTexture(renderer, tex_back, NULL, NULL); //NEW
mini::shader::render();
//SDL_RenderTexture(mini_ren, mini_bak, NULL, NULL);
//SDL_RenderPresent(mini_ren);
}
void raise_window() {
SDL_RaiseWindow(window);
}
void cursor(const bool value) {
if (value) SDL_ShowCursor(); else SDL_HideCursor();
}
}
}
#endif

View File

@@ -0,0 +1,16 @@
#include "backend.h"
#include <chrono>
namespace backend
{
uint32_t get_time_ms() {
using namespace std::chrono;
return duration_cast<milliseconds>(
steady_clock::now().time_since_epoch()
).count();
}
void exit() {
current_state = quitting;
}
}

77
source/backends/backend.h Normal file
View File

@@ -0,0 +1,77 @@
#pragma once
#define SDL3 1
#define SFML 2
#define EMSCRIPTEN 3
#ifndef BACKEND
#define BACKEND SDL3
#endif
#include <stdint.h>
namespace backend
{
enum state_t { running=0, exiting=1, quitting=2 };
extern state_t current_state;
void init();
void quit();
void poll_events();
const state_t& state();
void exit();
uint32_t get_time_ms();
char* clipboard();
void clipboard(const char* value);
namespace video
{
void init();
void quit();
void render();
void raise_window();
void cursor(const bool value);
}
namespace audio
{
void init();
void quit();
void render();
}
namespace input
{
void reset();
namespace key
{
bool down(uint8_t i);
bool press(uint8_t i);
int press();
bool any();
void text(const bool enable);
const char *utf8char();
}
namespace mouse
{
int posx();
int posy();
int wheel();
bool down(uint8_t i);
bool press(uint8_t i);
bool dblclick();
void discard();
bool inside(int x, int y, int w, int h);
}
namespace pad
{
void init();
bool down(int8_t i);
bool press(int8_t i);
int press();
}
}
}

107
source/mini/audio/audio.cpp Normal file
View File

@@ -0,0 +1,107 @@
#include "audio.h"
#include "jail_audio.h"
#include "mini/file/file.h"
namespace mini
{
namespace audio
{
int current_music = -1;
//#define MAX_SOUNDS 50
//JA_Sound_t *sounds[MAX_SOUNDS];
void init() {
jail::audio::init();
//for (int i=0;i<MAX_SOUNDS;++i) sounds[i] = NULL;
}
void quit() {
//if (current_music != NULL) JA_DeleteMusic(current_music);
//for (int i=0;i<MAX_SOUNDS;++i) if (sounds[i]!=NULL) JA_DeleteSound(sounds[i]);
jail::audio::quit();
}
namespace music
{
void play(const char *filename, const int loop) {
int size;
char *buffer = file::getfilebuffer(filename, size);
if (current_music != -1) jail::audio::music::destroy(current_music);
current_music = jail::audio::music::load((uint8_t*)buffer, size);
jail::audio::music::play(current_music, loop);
}
void pause() {
jail::audio::music::pause();
}
void resume() {
jail::audio::music::resume();
}
void stop(const int t) {
jail::audio::music::stop();
}
namespace pos {
void set(float value)
{
jail::audio::music::setPosition(value);
}
float get()
{
return jail::audio::music::getPosition();
}
}
namespace enable {
void set(const bool value)
{
jail::audio::music::enable(value);
file::setconfigvalue("music", value?"true":"false");
}
const bool get()
{
return jail::audio::music::isEnabled();
}
}
}
namespace sound
{
int load(const char *filename) {
int size;
char *buffer = file::getfilebuffer(filename, size);
return jail::audio::sound::load((uint8_t*)buffer, size);
}
void free(int soundfile) {
return jail::audio::sound::destroy(soundfile);
}
int play(int soundfile, const int volume) {
// [TODO] Ficar el volumen
return jail::audio::sound::play(soundfile, 0);
}
void stop(int soundchannel) {
return jail::audio::sound::channel::stop(soundchannel);
}
namespace enable {
void set(const bool value)
{
return jail::audio::sound::enable(value);
file::setconfigvalue("sound", value?"true":"false");
}
const bool get()
{
return jail::audio::sound::isEnabled();
}
}
}
}
}

38
source/mini/audio/audio.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
namespace mini
{
namespace audio
{
void init();
void quit();
namespace music
{
void play(const char *filename, const int loop=-1);
void pause();
void resume();
void stop(const int t=1000);
namespace pos {
void set(float value);
float get();
}
namespace enable {
void set(const bool value);
const bool get();
}
}
namespace sound
{
int load(const char *filename);
void free(int soundfile);
int play(int soundfile, const int volume=-1);
void stop(int soundchannel);
namespace enable {
void set(const bool value);
const bool get();
}
}
}
}

View File

@@ -0,0 +1,642 @@
#ifndef JA_USESDLMIXER
#include "jail_audio.h"
#include "external/stb_vorbis.h"
#include "other/log.h"
#include <SDL3/SDL.h>
#include <stdio.h>
#include <vector>
// structs i variables
// =============================
namespace jail
{
namespace audio
{
static SDL_AudioSpec audioSpec { SDL_AUDIO_S16, 2, 48000 };
SDL_AudioDeviceID sdlAudioDevice { 0 };
SDL_TimerID timerID { 0 };
namespace music
{
struct music_t
{
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
Uint32 length { 0 };
Uint8 *buffer { nullptr };
int pos { 0 };
int times { 0 };
SDL_AudioStream *stream { nullptr };
music::state state { music::state::invalid };
};
static int current { -1 };
static std::vector<music_t> musics;
static float volume { 1.0f };
static bool enabled { true };
namespace fade
{
static bool fading = false;
static int start_time;
static int duration;
static int initial_volume;
}
}
namespace sound
{
struct sound_t
{
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
Uint32 length { 0 };
Uint8 *buffer { NULL };
};
static std::vector<sound_t> sounds;
static float volume { 0.5f };
static bool enabled { true };
namespace channel
{
struct channel_t
{
int sound { -1 };
int pos { 0 };
int times { 0 };
SDL_AudioStream *stream { nullptr };
channel::state state { channel::state::free };
};
static std::vector<channel_t> channels;
}
}
}
}
// Funcions
// ==================
namespace jail
{
namespace audio
{
static void updateMusic()
{
if (!music::enabled) return;
if (music::current < 0 || music::current > music::musics.size()) return;
auto &m = music::musics[music::current];
if (m.state != music::state::playing) return;
if (music::fade::fading) {
int time = SDL_GetTicks();
if (time > (music::fade::start_time + music::fade::duration)) {
music::fade::fading = false;
music::stop();
return;
} else {
const int time_passed = time - music::fade::start_time;
const float percent = (float)time_passed / (float)music::fade::duration;
SDL_SetAudioStreamGain(m.stream, 1.0 - percent);
}
}
if (m.times != 0)
{
if (SDL_GetAudioStreamAvailable(m.stream) < int(m.length/2)) {
SDL_PutAudioStreamData(m.stream, m.buffer, m.length);
}
if (m.times>0) m.times--;
}
else
{
if (SDL_GetAudioStreamAvailable(m.stream) == 0) music::stop();
}
}
static void updateSound()
{
if (sound::enabled)
{
for (int i=0; i < sound::channel::channels.size(); ++i) {
auto &c = sound::channel::channels[i];
if (c.state == sound::channel::state::playing)
{
if (c.times != 0)
{
auto &s = sound::sounds[c.sound];
if (SDL_GetAudioStreamAvailable(c.stream) < int(s.length/2))
SDL_PutAudioStreamData(c.stream, s.buffer, s.length);
if (c.times>0) c.times--;
}
}
else
{
if (SDL_GetAudioStreamAvailable(c.stream) == 0) sound::channel::stop(i);
}
}
}
}
Uint32 updateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval)
{
updateMusic();
updateSound();
return 30;
}
void init()
{
#ifdef DEBUG
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
#endif
audioSpec = {SDL_AUDIO_S16, 1, 48000 };
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audioSpec);
if (!sdlAudioDevice) {
log_msg(LOG_FAIL, "Failed to initialize SDL audio: %s\n", SDL_GetError());
} else {
log_msg(LOG_OK, "Audio subsytem initialized\n");
}
//SDL_PauseAudioDevice(sdlAudioDevice);
timerID = SDL_AddTimer(30, updateCallback, nullptr);
}
void quit()
{
if (timerID) SDL_RemoveTimer(timerID);
for (int i=0; i<music::musics.size();++i) music::destroy(i);
music::musics.clear();
for (int i=0; i<sound::channel::channels.size();++i) sound::channel::stop(i);
sound::channel::channels.clear();
for (int i=0; i<sound::sounds.size();++i) sound::destroy(i);
sound::sounds.clear();
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
sdlAudioDevice = 0;
}
float setVolume(float vol)
{
sound::setVolume(music::setVolume(vol) / 2.0f);
return music::volume;
}
namespace music
{
int load(const uint8_t* buffer, uint32_t length)
{
int music = 0;
while (music < musics.size() && musics[music].state != state::invalid) { music++; }
if (music == musics.size()) musics.emplace_back();
auto &m = musics[music];
int chan, samplerate;
short *output;
m.length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2;
m.spec.channels = chan;
m.spec.freq = samplerate;
m.spec.format = SDL_AUDIO_S16;
m.buffer = (uint8_t*)SDL_malloc(m.length);
SDL_memcpy(m.buffer, output, m.length);
free(output);
m.pos = 0;
m.state = state::stopped;
return music;
}
int load(const char* filename)
{
// [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid.
FILE *f = fopen(filename, "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
Uint8 *buffer = (Uint8*)malloc(fsize + 1);
if (fread(buffer, fsize, 1, f)!=1) return NULL;
fclose(f);
int music = load(buffer, fsize);
free(buffer);
return music;
}
void play(int mus, int loop)
{
if (!music::enabled) return;
stop();
if (mus < 0 || mus >= musics.size()) {
log_msg(LOG_FAIL, "music::play: Illegal music handle: %i\n", mus);
return;
}
current = mus;
auto &m = musics[current];
m.pos = 0;
m.state = state::playing;
m.times = loop;
m.stream = SDL_CreateAudioStream(&m.spec, &audioSpec);
if (!SDL_PutAudioStreamData(m.stream, m.buffer, m.length)) log_msg(LOG_FAIL, "SDL_PutAudioStreamData failed!\n");
SDL_SetAudioStreamGain(m.stream, volume);
if (!SDL_BindAudioStream(sdlAudioDevice, m.stream)) log_msg(LOG_FAIL, "SDL_BindAudioStream failed!\n");
//SDL_ResumeAudioStreamDevice(current->stream);
}
void pause()
{
if (!music::enabled) return;
if (current<0 || current>musics.size()) {
log_msg(LOG_FAIL, "music::pause: Illegal music handle: %i\n", current);
return;
}
auto &m = musics[current];
if (m.state == state::invalid) {
log_msg(LOG_FAIL, "music::pause: Invalidated music handle: %i\n", current);
return;
}
m.state = state::paused;
//SDL_PauseAudioStreamDevice(current->stream);
SDL_UnbindAudioStream(m.stream);
}
void resume()
{
if (!music::enabled) return;
if (current<0 || current>musics.size()) {
log_msg(LOG_FAIL, "music::resume: Illegal music handle: %i\n", current);
return;
}
auto &m = musics[current];
if (m.state == state::invalid) {
log_msg(LOG_FAIL, "music::resume: Invalidated music handle: %i\n", current);
return;
}
m.state = state::playing;
//SDL_ResumeAudioStreamDevice(current->stream);
SDL_BindAudioStream(sdlAudioDevice, m.stream);
}
void stop()
{
if (!music::enabled) return;
if (current<0 || current>musics.size()) {
log_msg(LOG_FAIL, "music::stop: Illegal music handle: %i\n", current);
return;
}
auto &m = musics[current];
if (m.state == state::invalid) {
log_msg(LOG_FAIL, "music::stop: Invalidated music handle: %i\n", current);
return;
}
m.pos = 0;
m.state = state::stopped;
//SDL_PauseAudioStreamDevice(current->stream);
SDL_DestroyAudioStream(m.stream);
m.stream = nullptr;
}
void fadeOut(int milliseconds)
{
if (!music::enabled) return;
if (current<0 || current>musics.size()) {
log_msg(LOG_FAIL, "music::fadeOut: Illegal music handle: %i\n", current);
return;
}
auto &m = musics[current];
if (m.state == state::invalid) {
log_msg(LOG_FAIL, "music::fadeOut: Invalidated music handle: %i\n", current);
return;
}
fade::fading = true;
fade::start_time = SDL_GetTicks();
fade::duration = milliseconds;
fade::initial_volume = volume;
}
music::state getState()
{
if (!music::enabled) return music::state::disabled;
if (current<0 || current>musics.size()) return state::invalid;
return musics[current].state;
}
void destroy(int mus)
{
if (current == mus) current = -1;
if (mus<0 || mus>musics.size()) {
log_msg(LOG_FAIL, "music::destroy: Illegal music handle: %i\n", mus);
return;
}
auto &m = musics[mus];
SDL_free(m.buffer);
m.buffer = nullptr;
if (m.stream) SDL_DestroyAudioStream(m.stream);
m.stream = nullptr;
m.state = state::invalid;
}
float setVolume(float vol)
{
volume = SDL_clamp( vol, 0.0f, 1.0f );
if (current<0 || current>musics.size()) return vol;
auto &m = musics[current];
if (m.state == state::invalid) return vol;
SDL_SetAudioStreamGain(m.stream, volume);
return volume;
}
void setPosition(float value)
{
if (!music::enabled) return;
if (current<0 || current>musics.size()) {
log_msg(LOG_FAIL, "music::setPosition: Illegal music handle: %i\n", current);
return;
}
auto &m = musics[current];
if (m.state == state::invalid) {
log_msg(LOG_FAIL, "music::setPosition: Invalidated music handle: %i\n", current);
return;
}
m.pos = value * m.spec.freq;
}
float getPosition()
{
if (!music::enabled) return 0;
if (current<0 || current>musics.size()) {
log_msg(LOG_FAIL, "music::getPosition: Illegal music handle: %i\n", current);
return 0;
}
auto &m = musics[current];
if (m.state == state::invalid) {
log_msg(LOG_FAIL, "music::getPosition: Invalidated music handle: %i\n", current);
return 0;
}
return float(m.pos)/float(m.spec.freq);
}
void enable(bool value)
{
if (!value && music::enabled && current>=0 && current<musics.size() && musics[current].state==state::playing) stop();
music::enabled = value;
}
bool isEnabled()
{
return enabled;
}
}
namespace sound
{
int create(uint8_t* buffer, uint32_t length)
{
int snd = 0;
while (snd < sounds.size() && sounds[snd].buffer) { snd++; }
if (snd == sounds.size()) sounds.emplace_back();
auto &s = sounds[snd];
s.buffer = buffer;
s.length = length;
return snd;
}
int load(uint8_t* buffer, uint32_t size)
{
int snd = 0;
while (snd < sounds.size() && sounds[snd].buffer) { snd++; }
if (snd == sounds.size()) sounds.emplace_back();
auto &s = sounds[snd];
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &s.spec, &s.buffer, &s.length);
return snd;
}
int load(const char* filename)
{
int snd = 0;
while (snd < sounds.size() && sounds[snd].buffer) { snd++; }
if (snd == sounds.size()) sounds.emplace_back();
auto &s = sounds[snd];
SDL_LoadWAV(filename, &s.spec, &s.buffer, &s.length);
return snd;
}
int play(int snd, int loop)
{
if (!sound::enabled) return -1;
if (snd<0 || snd>sounds.size()) {
log_msg(LOG_FAIL, "sound::play: Illegal sound handle: %i\n", snd);
return -1;
}
auto &s = sounds[snd];
if (!s.buffer) {
log_msg(LOG_FAIL, "sound::play: Invalid sound: %i\n", snd);
return -1;
}
int chan = 0;
while (chan < channel::channels.size() && channel::channels[chan].state != channel::state::free) { chan++; }
if (chan == channel::channels.size()) channel::channels.emplace_back();
channel::stop(chan);
auto &c = channel::channels[chan];
c.sound = snd;
c.times = loop;
c.pos = 0;
c.state = channel::state::playing;
c.stream = SDL_CreateAudioStream(&s.spec, &audioSpec);
SDL_PutAudioStreamData(c.stream, s.buffer, s.length);
SDL_SetAudioStreamGain(c.stream, volume);
SDL_BindAudioStream(sdlAudioDevice, c.stream);
return chan;
}
int playOnChannel(int snd, int chan, int loop)
{
if (!sound::enabled) return -1;
if (snd<0 || snd>sounds.size()) {
log_msg(LOG_FAIL, "sound::playOnChannel: Illegal sound handle: %i\n", snd);
return -1;
}
auto &s = sounds[snd];
if (!s.buffer) {
log_msg(LOG_FAIL, "sound::playOnChannel: Invalid sound: %i\n", snd);
return -1;
}
if (chan<0 || chan>channel::channels.size()) {
log_msg(LOG_FAIL, "sound::playOnChannel: Illegal channel handle: %i\n", chan);
return -1;
}
channel::stop(chan);
auto &c = channel::channels[chan];
c.sound = snd;
c.times = loop;
c.pos = 0;
c.state = channel::state::playing;
c.stream = SDL_CreateAudioStream(&s.spec, &audioSpec);
SDL_PutAudioStreamData(c.stream, s.buffer, s.length);
SDL_SetAudioStreamGain(c.stream, volume);
SDL_BindAudioStream(sdlAudioDevice, c.stream);
return chan;
}
void destroy(int snd)
{
for (int i = 0; i < channel::channels.size(); i++) {
if (channel::channels[i].sound == snd) channel::stop(i);
}
if (snd<0 || snd>sounds.size()) {
log_msg(LOG_FAIL, "sound::destroy: Illegal sound handle: %i\n", snd);
return;
}
auto &s = sounds[snd];
SDL_free(s.buffer);
}
float setVolume(float vol)
{
volume = SDL_clamp( vol, 0.0f, 1.0f );
for (int i = 0; i < channel::channels.size(); i++) {
auto &c = channel::channels[i];
if ( (c.state == channel::state::playing) || (c.state == channel::state::paused) )
SDL_SetAudioStreamGain(c.stream, volume);
}
return volume;
}
void enable(bool value)
{
if (!value && sound::enabled) {
for (int i = 0; i < channel::channels.size(); i++) {
if (channel::channels[i].state == channel::state::playing) channel::stop(i);
}
}
enabled = value;
}
bool isEnabled()
{
return enabled;
}
namespace channel
{
void pause(const int chan)
{
if (!sound::enabled) return;
if (chan == -1)
{
for (int i = 0; i < channels.size(); i++)
if (channels[i].state == state::playing)
{
channels[i].state = state::paused;
//SDL_PauseAudioStreamDevice(channels[i].stream);
SDL_UnbindAudioStream(channels[i].stream);
}
}
else if (chan >= 0 && chan < channels.size())
{
if (channels[chan].state == state::playing)
{
channels[chan].state = state::paused;
//SDL_PauseAudioStreamDevice(channels[channel].stream);
SDL_UnbindAudioStream(channels[chan].stream);
}
}
}
void resume(int chan)
{
if (!sound::enabled) return;
if (chan == -1)
{
for (int i = 0; i < channels.size(); i++)
if (channels[i].state == state::paused)
{
channels[i].state = state::playing;
//SDL_ResumeAudioStreamDevice(channels[i].stream);
SDL_BindAudioStream(sdlAudioDevice, channels[i].stream);
}
}
else if (chan >= 0 && chan < channels.size())
{
if (channels[chan].state == state::paused)
{
channels[chan].state = state::playing;
//SDL_ResumeAudioStreamDevice(channels[channel].stream);
SDL_BindAudioStream(sdlAudioDevice, channels[chan].stream);
}
}
}
void stop(int chan)
{
if (!sound::enabled) return;
if (chan == -1)
{
for (int i = 0; i < channels.size(); i++) {
if (channels[i].state != state::free) SDL_DestroyAudioStream(channels[i].stream);
channels[i].stream = nullptr;
channels[i].state = state::free;
channels[i].pos = 0;
channels[i].sound = -1;
}
}
else if (chan >= 0 && chan < channels.size())
{
if (channels[chan].state != state::free) SDL_DestroyAudioStream(channels[chan].stream);
channels[chan].stream = nullptr;
channels[chan].state = state::free;
channels[chan].pos = 0;
channels[chan].sound = -1;
}
}
channel::state getState(int chan)
{
if (!sound::enabled) return state::disabled;
if (chan < 0 || chan >= channels.size()) return state::invalid;
return channels[chan].state;
}
}
}
}
}
#endif

View File

@@ -0,0 +1,57 @@
#pragma once
#include <stdint.h>
namespace jail
{
namespace audio
{
void init(/*const int freq, const SDL_AudioFormat format, const int channels*/);
void quit();
namespace music
{
//struct JA_Music_t;
enum state { invalid, playing, paused, stopped, disabled };
int load(const char* filename);
int load(const uint8_t* buffer, uint32_t length);
void play(int mus, int loop = -1);
void pause();
void resume();
void stop();
void fadeOut(int milliseconds);
state getState();
void destroy(int mus);
float setVolume(float vol);
void setPosition(float value);
float getPosition();
void enable(bool value);
bool isEnabled();
}
namespace sound
{
//struct JA_Sound_t;
int create(uint8_t* buffer, uint32_t length);
int load(uint8_t* buffer, uint32_t length);
int load(const char* filename);
int play(int snd, int loop = 0);
int playOnChannel(int snd, int chan, int loop = 0);
void destroy(int snd);
float setVolume(float vol);
void enable(bool value);
bool isEnabled();
namespace channel
{
enum state { invalid, free, playing, paused, disabled };
void pause(int chan);
void resume(int chan);
void stop(int chan);
state getState(int chan);
}
}
}
}

View File

@@ -0,0 +1,21 @@
#include "config.h"
#include "mini/file/file.h"
namespace mini
{
namespace config
{
namespace key {
void set(const char* key, const char* value) {
file::setconfigvalue(key, value);
}
const char* get(const char* key) {
return file::getconfigvalue(key);
}
}
const char *folder() {
return file::getconfigfolder();
}
}
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
namespace mini
{
namespace config
{
namespace key {
void set(const char* key, const char* value);
const char *get(const char* key);
}
const char *folder();
}
}

488
source/mini/draw/draw.cpp Normal file
View File

@@ -0,0 +1,488 @@
#include "draw.h"
#include "mini/surf/surf.h"
#include "mini/view/view.h"
#include "mini/font/font.h"
#include <math.h>
namespace mini
{
namespace draw
{
state_t state;
void init() {
state.mode = DRAWMODE_NORMAL;
state.fill_pattern = 0b1111111111111111;
}
void quit() {
}
namespace pixel {
static inline void pset_fast(int x, int y, uint8_t color) {
auto &s = surf::state.surfaces[surf::state.dest_surface];
uint8_t *p = s.p;
if (state.trans != color) p[x+y*s.w] = color;
}
static inline void pset_bool(int x, int y, uint8_t color) {
if (state.trans != color) {
auto &s = surf::state.surfaces[surf::state.dest_surface];
uint8_t *p = s.p;
switch (state.mode) {
case DRAWMODE_AND:
p[x+y*s.w] &= color;
break;
case DRAWMODE_OR:
p[x+y*s.w] |= color;
break;
case DRAWMODE_XOR:
p[x+y*s.w] ^= color;
break;
case DRAWMODE_NOT:
p[x+y*s.w] = ~p[x+y*s.w];
break;
}
}
}
static inline void pset_pattern(int x, int y, uint8_t color) {
int pbx = x % 4, pby = y % 4;
int pb = pbx+pby*4;
if (state.fill_pattern & (1 << pb))
if (state.trans != color) {
auto &s = surf::state.surfaces[surf::state.dest_surface];
uint8_t *p = s.p;
p[x+y*s.w] = color;
}
}
// Per a les funcions que pinten tot del mateix color
static inline void direct_pset(int x, int y, uint8_t color) {
auto &s = surf::state.surfaces[surf::state.dest_surface];
x += view::state.origin[0]; y += view::state.origin[1];
if (x < s.clip[0] || x > s.clip[2] || y < s.clip[1] || y > s.clip[3]) return;
switch (state.mode) {
case DRAWMODE_NORMAL: pset_fast(x,y,color); break;
case DRAWMODE_PATTERN: pset_pattern(x,y,color); break;
default: pset_bool(x,y,color); break;
}
}
// Per a les funcions que van canviant de color (surf.pixel i draw.surf, bàsicament)
static inline void subst_pset(int x, int y, uint8_t color) {
direct_pset(x, y, state.draw_palette[color]);
}
void set(int x, int y, uint8_t color) {
subst_pset(x,y,color);
}
uint8_t get(int x, int y) {
if (surf::state.source_surface==-1) return 0;
auto &s = surf::state.surfaces[surf::state.source_surface];
if (x < 0 || x > (s.w-1) || y < 0 || y > (s.h-1)) return 0;
return s.p[x+y*s.w];
}
}
// Bresenham Line Algorithm
void line(int x0, int y0, int x1, int y1, uint8_t color) {
int x, y;
int dx, dy;
int incx, incy;
int balance;
color = state.draw_palette[color];
if (x1 >= x0) { dx = x1 - x0; incx = 1; } else { dx = x0 - x1; incx = -1; }
if (y1 >= y0) { dy = y1 - y0; incy = 1; } else { dy = y0 - y1; incy = -1; }
x = x0; y = y0;
if (dx >= dy) {
dy <<= 1;
balance = dy - dx;
dx <<= 1;
while (x != x1) {
pixel::direct_pset(x, y, color);
if (balance >= 0) { y += incy; balance -= dx; }
balance += dy;
x += incx;
}
pixel::direct_pset(x, y, color);
} else {
dx <<= 1;
balance = dx - dy;
dy <<= 1;
while (y != y1) {
pixel::direct_pset(x, y, color);
if (balance >= 0) { x += incx; balance -= dy; }
balance += dx;
y += incy;
}
pixel::direct_pset(x, y, color);
}
}
void hline(int x0, int y, int x1, uint8_t color) {
color = state.draw_palette[color];
if (x0>x1) { const int tmp=x0;x0=x1;x1=tmp; }
for (int x=x0; x<=x1; ++x) pixel::direct_pset(x, y, color);
}
void vline(int x, int y0, int y1, uint8_t color) {
color = state.draw_palette[color];
if (y0>y1) { const int tmp=y0;y0=y1;y1=tmp; }
for (int y=y0; y<=y1; ++y) pixel::direct_pset(x, y, color);
}
void rect(int x, int y, int w, int h, uint8_t color) {
int x1 = w+x-1;
int y1 = h+y-1;
hline(x, y, x1, color);
hline(x, y1, x1, color);
vline(x, y, y1, color);
vline(x1, y, y1, color);
}
void rectf(int x, int y, int w, int h, uint8_t color) {
int x1 = w+x-1;
int y1 = h+y-1;
for (int i=y; i<=y1; ++i) hline(x, i, x1, color);
}
static inline void circ_scanline(int xc, int yc, int x, int y, uint8_t color) {
pixel::direct_pset(xc+x, yc+y, color);
pixel::direct_pset(xc-x, yc+y, color);
pixel::direct_pset(xc+x, yc-y, color);
pixel::direct_pset(xc-x, yc-y, color);
pixel::direct_pset(xc+y, yc+x, color);
pixel::direct_pset(xc-y, yc+x, color);
pixel::direct_pset(xc+y, yc-x, color);
pixel::direct_pset(xc-y, yc-x, color);
}
void circ(int x, int y, uint8_t r, uint8_t color) {
color = state.draw_palette[color];
int xi=0, yi=r;
int d=3-2*r;
circ_scanline(x, y, xi, yi, color);
while (yi>=xi++) {
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
circ_scanline(x, y, xi, yi, color);
}
}
static inline void circf_scanline(int xc, int yc, int x, int y, uint8_t color) {
hline(xc-x, yc+y, xc+x, color);
hline(xc-x, yc-y, xc+x, color);
hline(xc-y, yc+x, xc+y, color);
hline(xc-y, yc-x, xc+y, color);
}
void circf(int x, int y, uint8_t r, uint8_t color) {
int xi=0, yi=r;
int d=3-2*r;
circf_scanline(x, y, xi, yi, color);
while (yi>=xi++) {
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
circf_scanline(x, y, xi, yi, color);
}
}
void roundrect(int x, int y, int w, int h, uint8_t r, uint8_t color) {
int xi=0, yi=r;
int d=3-2*r;
int xf = w+x-1;
int yf = h+y-1;
int x1 = x+r, y1 = y+r;
int x2 = xf-r, y2 = yf-r;
hline(x1, y, x2, color);
hline(x1, yf, x2, color);
vline(x, y1, y2, color);
vline(xf, y1, y2, color);
color = state.draw_palette[color];
while (yi>=xi++) {
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
pixel::direct_pset(x2+xi, y2+yi, color);
pixel::direct_pset(x1-xi, y2+yi, color);
pixel::direct_pset(x2+xi, y1-yi, color);
pixel::direct_pset(x1-xi, y1-yi, color);
pixel::direct_pset(x2+yi, y2+xi, color);
pixel::direct_pset(x1-yi, y2+xi, color);
pixel::direct_pset(x2+yi, y1-xi, color);
pixel::direct_pset(x1-yi, y1-xi, color);
}
}
void roundrectf(int x, int y, int w, int h, uint8_t r, uint8_t color) {
int xi=0, yi=r;
int d=3-2*r;
int xf = w+x-1;
int yf = h+y-1;
int x1 = x+r, y1 = y+r;
int x2 = xf-r, y2 = yf-r;
for (int i=y1; i<=y2; ++i) hline(x, i, xf, color);
while (yi>=xi++) {
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
hline(x1-xi, y2+yi, x2+xi, color);
hline(x1-xi, y1-yi, x2+xi, color);
hline(x1-yi, y2+xi, x2+yi, color);
hline(x1-yi, y1-xi, x2+yi, color);
}
}
void oval_scanline(int xc, int yc, int x, int y, float xf, float yf, uint8_t color) {
pixel::direct_pset((xc+x)*xf, (yc+y)*yf, color);
pixel::direct_pset((xc-x)*xf, (yc+y)*yf, color);
pixel::direct_pset((xc+x)*xf, (yc-y)*yf, color);
pixel::direct_pset((xc-x)*xf, (yc-y)*yf, color);
pixel::direct_pset((xc+y)*xf, (yc+x)*yf, color);
pixel::direct_pset((xc-y)*xf, (yc+x)*yf, color);
pixel::direct_pset((xc+y)*xf, (yc-x)*yf, color);
pixel::direct_pset((xc-y)*xf, (yc-x)*yf, color);
}
void oval(int x0, int y0, int x1, int y1, uint8_t color) {
color = state.draw_palette[color];
int rx = (x1-x0)/2;
int ry = (y1-y0)/2;
int r = rx;
int x = x0 + rx;
int y = y0 + ry;
float xf = 1.0f, yf = 1.0f;
if (rx>=ry) {r=rx;yf=float(ry)/float(rx);} else {r=ry;xf=float(rx)/float(ry);}
int xi=0, yi=r;
int d=3-2*r;
oval_scanline(x, y, xi, yi, xf, yf, color);
while (yi>=xi++) {
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
oval_scanline(x, y, xi, yi, xf, yf, color);
}
}
static inline void ovalf_scanline(int xc, int yc, int x, int y, float xf, float yf, uint8_t color) {
hline((xc-x)*xf, (yc+y)*yf, (xc+x)*xf, color);
hline((xc-x)*xf, (yc-y)*yf, (xc+x)*xf, color);
hline((xc-y)*xf, (yc+x)*yf, (xc+y)*xf, color);
hline((xc-y)*xf, (yc-x)*yf, (xc+y)*xf, color);
}
void ovalf(int x0, int y0, int x1, int y1, uint8_t color) {
int rx = (x1-x0)/2;
int ry = (y1-y0)/2;
int r = rx;
int x = x0 + rx;
int y = y0 + ry;
float xf = 1.0f, yf = 1.0f;
if (rx>=ry) {r=rx;yf=float(ry)/float(rx);} else {r=ry;xf=float(rx)/float(ry);}
int xi=0, yi=r;
int d=3-2*r;
ovalf_scanline(x, y, xi, yi, xf, yf, color);
while (yi>=xi++) {
d += d>0 ? 4*(xi-yi--)+10 : 4*xi+6;
ovalf_scanline(x, y, xi, yi, xf, yf, color);
}
}
namespace pattern {
void set(uint16_t pat, bool transparent) {
state.fill_pattern = pat;
}
}
void surf(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flip_x, bool flip_y, bool invert) {
if (dw == 0) dw = sw;
if (dh == 0) dh = sh;
// 16.16 fixed point
int sdx = (sw << 16) / dw;
int sdy = (sh << 16) / dh;
if (flip_x) sdx = -sdx;
if (flip_y) sdy = -sdy;
int ssx = flip_x ? ((sx + sw - 1) << 16) : (sx << 16);
int ssy = flip_y ? ((sy + sh - 1) << 16) : (sy << 16);
int csy = ssy;
if (!invert) {
for (int y = dy; y < dy + dh; ++y) {
int csx = ssx;
for (int x = dx; x < dx + dw; ++x) {
uint8_t color = pixel::get(csx >> 16, csy >> 16);
pixel::subst_pset(x, y, color);
csx += sdx;
}
csy += sdy;
}
} else {
for (int y = dy; y < dy + dh; ++y) {
int csx = ssx;
for (int x = dx; x < dx + dw; ++x) {
uint8_t color = pixel::get(csy >> 16, csx >> 16);
pixel::subst_pset(x, y, color);
csx += sdx;
}
csy += sdy;
}
}
}
void surfrot(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flip_x, bool flip_y, float angle_deg) {
if (dw == 0) dw = sw;
if (dh == 0) dh = sh;
// Centro del destino (rectángulo sin rotar)
float dcx = dx + dw * 0.5f;
float dcy = dy + dh * 0.5f;
// Centro del subrectángulo origen
float scx = sx + sw * 0.5f;
float scy = sy + sh * 0.5f;
// Escalado destino -> origen
float inv_scale_x = float(sw) / float(dw);
float inv_scale_y = float(sh) / float(dh);
// Flips integrados en la escala
if (flip_x) inv_scale_x = -inv_scale_x;
if (flip_y) inv_scale_y = -inv_scale_y;
// Ángulo en radianes
float a = angle_deg * 3.14159265f / 180.0f;
float ca = cosf(a);
float sa = sinf(a);
// --- 1. Bounding box rotado del rectángulo destino ---
float hx = dw * 0.5f;
float hy = dh * 0.5f;
float vx[4] = { -hx, hx, -hx, hx };
float vy[4] = { -hy, -hy, hy, hy };
float min_x = 1e9f, max_x = -1e9f;
float min_y = 1e9f, max_y = -1e9f;
for (int i = 0; i < 4; ++i) {
float rr_x = vx[i] * ca - vy[i] * sa;
float rr_y = vx[i] * sa + vy[i] * ca;
float dxp = dcx + rr_x;
float dyp = dcy + rr_y;
if (dxp < min_x) min_x = dxp;
if (dxp > max_x) max_x = dxp;
if (dyp < min_y) min_y = dyp;
if (dyp > max_y) max_y = dyp;
}
int bb_x0 = (int)floorf(min_x);
int bb_x1 = (int)ceilf (max_x);
int bb_y0 = (int)floorf(min_y);
int bb_y1 = (int)ceilf (max_y);
// --- 2. Rotación inversa + escalado + clipping estricto ---
for (int y = bb_y0; y <= bb_y1; ++y) {
for (int x = bb_x0; x <= bb_x1; ++x) {
// Coordenadas relativas al centro destino
float rx = x - dcx;
float ry = y - dcy;
// Rotación inversa
float ux = rx * ca + ry * sa;
float uy = -rx * sa + ry * ca;
// Escalado destino -> origen (con flips)
float sxp = scx + ux * inv_scale_x;
float syp = scy + uy * inv_scale_y;
// Clipping estricto al subrectángulo origen
if (sxp < sx || sxp >= sx + sw ||
syp < sy || syp >= sy + sh)
continue; // no pintamos nada
uint8_t color = pixel::get((int)sxp, (int)syp);
pixel::subst_pset(x, y, color);
}
}
}
void tileblit(uint8_t n, int x, int y, int tw, int th) {
//const int tw = tile_width;
//const int th = tile_height;
const int tiles_per_row = mini::surf::state.surfaces[mini::surf::state.source_surface].w / tw;
// Coordenadas del tile dentro del spritesheet
int tx = (n % tiles_per_row) * tw;
int ty = (n / tiles_per_row) * th;
int src_y = ty;
for (int yi = 0; yi < th; ++yi) {
int src_x = tx;
for (int xi = 0; xi < tw; ++xi) {
uint8_t c = mini::draw::pixel::get(src_x, src_y);
mini::draw::pixel::subst_pset(x + xi, y + yi, c);
src_x++;
}
src_y++;
}
}
const uint8_t printchar(uint8_t c, int x, int y) {
font::char_t &chr = font::current_font->chars[c];
draw::surf(chr.x, chr.y, chr.w, chr.h, x, y-chr.base);
return chr.w;
}
void text(const char* str, int x, int y, uint8_t color) {
const unsigned char* p = (const unsigned char*)str;
uint8_t cp;
uint8_t xpos = x;
uint8_t old_source = surf::source::get();
surf::source::set(font::current_font->surface);
uint8_t old_color = state.draw_palette[1];
state.draw_palette[1] = color;
uint8_t old_trans = state.trans;
state.trans = 0;
while (p[0]!=0) {
if (p[0] < 0x80) {
cp = p[0];
p+=1;
} else if ((p[0] & 0xE0) == 0xC0) {
cp = (p[0] << 6) | (p[1] & 0x3F);
p+=2;
}
xpos += printchar(cp, xpos, y) + font::current_font->spacing;
}
state.trans = old_trans;
state.draw_palette[1] = old_color;
surf::source::set(old_source);
}
namespace mode {
void set(uint8_t mode) {
state.mode = mode;
}
}
}
}

62
source/mini/draw/draw.h Normal file
View File

@@ -0,0 +1,62 @@
#pragma once
#include <stdint.h>
#define DRAWMODE_NORMAL 0
#define DRAWMODE_PATTERN 1
#define DRAWMODE_AND 2
#define DRAWMODE_OR 3
#define DRAWMODE_XOR 4
#define DRAWMODE_NOT 5
namespace mini
{
namespace draw
{
struct state_t {
uint8_t trans = 0;
uint16_t fill_pattern = 0b1111111111111111;
uint8_t draw_palette[256];
uint8_t mode = DRAWMODE_NORMAL;
};
extern state_t state;
void init();
void quit();
namespace pixel {
void set(int x, int y, uint8_t color);
uint8_t get(int x, int y);
}
void line(int x0, int y0, int x1, int y1, uint8_t color);
void hline(int x0, int y, int x1, uint8_t color);
void vline(int x, int y0, int y1, uint8_t color);
void rect(int x, int y, int w, int h, uint8_t color);
void rectf(int x, int y, int w, int h, uint8_t color);
void circ(int x, int y, uint8_t r, uint8_t color);
void circf(int x, int y, uint8_t r, uint8_t color);
void roundrect(int x, int y, int w, int h, uint8_t r, uint8_t color);
void roundrectf(int x, int y, int w, int h, uint8_t r, uint8_t color);
void oval(int x0, int y0, int x1, int y1, uint8_t color);
void ovalf(int x0, int y0, int x1, int y1, uint8_t color);
namespace pattern {
void set(uint16_t pat, bool transparent = false);
}
void surf(int sx, int sy, int sw, int sh, int dx, int dy, int dw=0, int dh=0, bool flip_x = false, bool flip_y = false, bool invert = false);
void surfrot(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flip_x, bool flip_y, float angle_deg);
void tileblit(uint8_t n, int x, int y, int tw, int th);
void text(const char *str, int x, int y, uint8_t color);
namespace mode
{
void set(uint8_t mode);
}
}
}

Some files were not shown because too many files have changed in this diff Show More