Compare commits
14 Commits
2631112a4b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| aabf90351c | |||
| 13e0e89aa8 | |||
| eb0f5e24bd | |||
| 81c5011bc7 | |||
| 4670b52378 | |||
| b403dbad52 | |||
| 8cc347f639 | |||
| bc59b74f15 | |||
| 063016cf55 | |||
| 28f4da9b26 | |||
| becc33b88c | |||
| 737e18fd96 | |||
| d5286d8abe | |||
| 7622045e36 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,4 +6,5 @@ arounders_debug
|
||||
./*.dll
|
||||
release/*
|
||||
respak2
|
||||
*.dSYM/*
|
||||
*.dSYM/*
|
||||
build/*
|
||||
|
||||
20
Makefile
20
Makefile
@@ -13,12 +13,12 @@ linuxRelease = $(executable)-$(version)-linux.tar.gz
|
||||
windows:
|
||||
@echo off
|
||||
windres icon.rc -O coff icon.res
|
||||
g++ $(source) icon.res -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL2main -lSDL2 -lSDL2_mixer -mwindows -o "$(executable).exe"
|
||||
g++ $(source) icon.res -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL3 -mwindows -o "$(executable).exe"
|
||||
strip -s -R .comment -R .gnu.version --strip-unneeded "$(executable).exe"
|
||||
|
||||
windows_debug:
|
||||
@echo off
|
||||
g++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -fvar-tracking -lmingw32 -lSDL2main -lSDL2 -lSDL2_mixer -o "$(executable)_debug.exe"
|
||||
g++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -fvar-tracking -lmingw32 -lSDL3 -o "$(executable)_debug.exe"
|
||||
|
||||
windows_release:
|
||||
@echo off
|
||||
@@ -38,7 +38,7 @@ windows_release:
|
||||
|
||||
# Build
|
||||
windres icon.rc -O coff icon.res
|
||||
g++ $(source) icon.res -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL2main -lSDL2 -lSDL2_mixer -mwindows -o "$(releaseFolder)/$(executable).exe"
|
||||
g++ $(source) icon.res -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lmingw32 -lSDL3 -mwindows -o "$(releaseFolder)/$(executable).exe"
|
||||
strip -s -R .comment -R .gnu.version --strip-unneeded "$(releaseFolder)/$(executable).exe"
|
||||
|
||||
# Create ZIP
|
||||
@@ -49,10 +49,10 @@ windows_release:
|
||||
powershell if (Test-Path "$(releaseFolder)") {Remove-Item "$(releaseFolder)" -Recurse -Force}
|
||||
|
||||
macos:
|
||||
clang++ $(source) -D VERSION=\"$(version)\" -Wall -Os -std=c++11 -ffunction-sections -fdata-sections -lSDL2 -lSDL2_mixer -o "$(executable)"
|
||||
clang++ $(source) -D VERSION=\"$(version)\" -Wall -Os -std=c++11 -ffunction-sections -fdata-sections -lSDL3 -o "$(executable)"
|
||||
|
||||
macos_debug:
|
||||
clang++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -std=c++11 -lSDL2 -lSDL2_mixer -o "$(executable)_debug"
|
||||
clang++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -std=c++11 -lSDL3 -o "$(executable)_debug"
|
||||
|
||||
macos_release:
|
||||
# Remove data and possible data from previous builds
|
||||
@@ -86,7 +86,7 @@ macos_release:
|
||||
ln -s /Applications "$(releaseFolder)"/Applications
|
||||
|
||||
# Build INTEL
|
||||
clang++ $(source) -D MACOS_BUNDLE -D VERSION=\"$(version)\" -Wall -Os -std=c++11 -framework SDL2 -framework SDL2_mixer -F ./Frameworks -ffunction-sections -fdata-sections -o "$(releaseFolder)/$(appName).app/Contents/MacOS/$(executable)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.12
|
||||
clang++ $(source) -D MACOS_BUNDLE -D VERSION=\"$(version)\" -Wall -Os -std=c++11 -framework SDL3 -F ./Frameworks -ffunction-sections -fdata-sections -o "$(releaseFolder)/$(appName).app/Contents/MacOS/$(executable)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.12
|
||||
|
||||
# Build INTEL DMG
|
||||
hdiutil create tmp.dmg -ov -volname "$(appName)" -fs HFS+ -srcfolder "$(releaseFolder)"
|
||||
@@ -94,7 +94,7 @@ macos_release:
|
||||
rm -f tmp.dmg
|
||||
|
||||
# Build APPLE SILICON
|
||||
#clang++ $(source) -D MACOS_BUNDLE -D VERSION=\"$(version)\" -std=c++11 -Wall -Os -framework SDL2 -framework SDL2_mixer -F ./Frameworks -ffunction-sections -fdata-sections -o "$(releaseFolder)/$(appName).app/Contents/MacOS/$(executable)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11
|
||||
#clang++ $(source) -D MACOS_BUNDLE -D VERSION=\"$(version)\" -std=c++11 -Wall -Os -framework SDL3 -F ./Frameworks -ffunction-sections -fdata-sections -o "$(releaseFolder)/$(appName).app/Contents/MacOS/$(executable)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11
|
||||
|
||||
# Build APPLE SILICON DMG
|
||||
#hdiutil create tmp.dmg -ov -volname "$(appName)" -fs HFS+ -srcfolder "$(releaseFolder)"
|
||||
@@ -107,11 +107,11 @@ macos_release:
|
||||
|
||||
|
||||
linux:
|
||||
g++ $(source) -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL2 -lSDL2_mixer -o "$(executable)"
|
||||
g++ $(source) -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL3 -o "$(executable)"
|
||||
strip -s -R .comment -R .gnu.version --strip-unneeded "$(executable)"
|
||||
|
||||
linux_debug:
|
||||
g++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -fvar-tracking -lSDL2 -lSDL2_mixer -o "$(executable)_debug"
|
||||
g++ $(source) -D DEBUG -D VERSION=\"$(version)\" -g -Wall -fvar-tracking -lSDL3 -o "$(executable)_debug"
|
||||
|
||||
linux_release:
|
||||
# Remove data
|
||||
@@ -129,7 +129,7 @@ linux_release:
|
||||
cp README.md "$(releaseFolder)"
|
||||
|
||||
# Build
|
||||
g++ $(source) -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL2 -lSDL2_mixer -o "$(releaseFolder)/$(executable)"
|
||||
g++ $(source) -D VERSION=\"$(version)\" -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -lSDL3 -o "$(releaseFolder)/$(executable)"
|
||||
strip -s -R .comment -R .gnu.version --strip-unneeded "$(releaseFolder)/$(executable)"
|
||||
|
||||
# Pack files
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# AROUNDERS
|
||||
versió 1.2
|
||||
versió 1.2.1
|
||||
|
||||

|
||||

|
||||
|
||||
## HISTÒRIA
|
||||
|
||||
@@ -27,7 +27,7 @@ Teòricament, deuria funcionar amb comps molt més xungos, pero no tinc temps ar
|
||||
- Impresora (¿¿??)
|
||||
OPCIONAL: Scanner, scanner!
|
||||
|
||||

|
||||

|
||||
|
||||
## INSTRUCCIONS
|
||||
|
||||
|
||||
141
data/crtpi.glsl
Normal file
141
data/crtpi.glsl
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
crt-pi - A Raspberry Pi friendly CRT shader.
|
||||
|
||||
Copyright (C) 2015-2016 davej
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
This shader is designed to work well on Raspberry Pi GPUs (i.e. 1080P @ 60Hz on a game with a 4:3 aspect ratio). It pushes the Pi's GPU hard and enabling some features will slow it down so that it is no longer able to match 1080P @ 60Hz. You will need to overclock your Pi to the fastest setting in raspi-config to get the best results from this shader: 'Pi2' for Pi2 and 'Turbo' for original Pi and Pi Zero. Note: Pi2s are slower at running the shader than other Pis, this seems to be down to Pi2s lower maximum memory speed. Pi2s don't quite manage 1080P @ 60Hz - they drop about 1 in 1000 frames. You probably won't notice this, but if you do, try enabling FAKE_GAMMA.
|
||||
|
||||
SCANLINES enables scanlines. You'll almost certainly want to use it with MULTISAMPLE to reduce moire effects. SCANLINE_WEIGHT defines how wide scanlines are (it is an inverse value so a higher number = thinner lines). SCANLINE_GAP_BRIGHTNESS defines how dark the gaps between the scan lines are. Darker gaps between scan lines make moire effects more likely.
|
||||
|
||||
GAMMA enables gamma correction using the values in INPUT_GAMMA and OUTPUT_GAMMA. FAKE_GAMMA causes it to ignore the values in INPUT_GAMMA and OUTPUT_GAMMA and approximate gamma correction in a way which is faster than true gamma whilst still looking better than having none. You must have GAMMA defined to enable FAKE_GAMMA.
|
||||
|
||||
CURVATURE distorts the screen by CURVATURE_X and CURVATURE_Y. Curvature slows things down a lot.
|
||||
|
||||
By default the shader uses linear blending horizontally. If you find this too blury, enable SHARPER.
|
||||
|
||||
BLOOM_FACTOR controls the increase in width for bright scanlines.
|
||||
|
||||
MASK_TYPE defines what, if any, shadow mask to use. MASK_BRIGHTNESS defines how much the mask type darkens the screen.
|
||||
|
||||
*/
|
||||
|
||||
// Haven't put these as parameters as it would slow the code down.
|
||||
#define SCANLINES
|
||||
#define MULTISAMPLE
|
||||
#define GAMMA
|
||||
// MASK_TYPE: 0 = none, 1 = green/magenta, 2 = trinitron(ish)
|
||||
#define MASK_TYPE 2
|
||||
|
||||
|
||||
#define COMPAT_PRECISION
|
||||
|
||||
#define MASK_BRIGHTNESS 0.80
|
||||
#define SCANLINE_WEIGHT 6.0
|
||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
||||
#define BLOOM_FACTOR 3.5
|
||||
#define INPUT_GAMMA 2.4
|
||||
#define OUTPUT_GAMMA 2.2
|
||||
|
||||
/* COMPATIBILITY
|
||||
- GLSL compilers
|
||||
*/
|
||||
|
||||
uniform vec2 TextureSize;
|
||||
varying vec2 TEX0;
|
||||
varying float filterWidth;
|
||||
|
||||
#if defined(VERTEX)
|
||||
|
||||
void main()
|
||||
{
|
||||
filterWidth = (768.0 / TextureSize.x) / 3.0;
|
||||
TEX0 = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
|
||||
#elif defined(FRAGMENT)
|
||||
|
||||
uniform sampler2D Texture;
|
||||
|
||||
float CalcScanLineWeight(float dist)
|
||||
{
|
||||
return max(1.0-dist*dist*SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
|
||||
}
|
||||
|
||||
float CalcScanLine(float dy)
|
||||
{
|
||||
float scanLineWeight = CalcScanLineWeight(dy);
|
||||
#if defined(MULTISAMPLE)
|
||||
scanLineWeight += CalcScanLineWeight(dy-filterWidth);
|
||||
scanLineWeight += CalcScanLineWeight(dy+filterWidth);
|
||||
scanLineWeight *= 0.3333333;
|
||||
#endif
|
||||
return scanLineWeight;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
//vec2 TextureSize = vec2(320.0, 256.0);
|
||||
vec2 texcoord = TEX0;
|
||||
{
|
||||
vec2 texcoordInPixels = texcoord * TextureSize;
|
||||
float tempY = floor(texcoordInPixels.y) + 0.5;
|
||||
float yCoord = tempY / TextureSize.y;
|
||||
float dy = texcoordInPixels.y - tempY;
|
||||
float scanLineWeight = CalcScanLine(dy);
|
||||
float signY = sign(dy);
|
||||
dy = dy * dy;
|
||||
dy = dy * dy;
|
||||
dy *= 8.0;
|
||||
dy /= TextureSize.y;
|
||||
dy *= signY;
|
||||
vec2 tc = vec2(texcoord.x, yCoord + dy);
|
||||
|
||||
vec3 colour = texture2D(Texture, tc).rgb;
|
||||
|
||||
#if defined(SCANLINES)
|
||||
#if defined(GAMMA)
|
||||
colour = pow(colour, vec3(INPUT_GAMMA));
|
||||
#endif
|
||||
scanLineWeight *= BLOOM_FACTOR;
|
||||
colour *= scanLineWeight;
|
||||
|
||||
#if defined(GAMMA)
|
||||
colour = pow(colour, vec3(1.0/OUTPUT_GAMMA));
|
||||
#endif
|
||||
#endif
|
||||
#if MASK_TYPE == 0
|
||||
gl_FragColor = vec4(colour, 1.0);
|
||||
#else
|
||||
#if MASK_TYPE == 1
|
||||
float whichMask = fract((gl_FragCoord.x*1.0001) * 0.5);
|
||||
vec3 mask;
|
||||
if (whichMask < 0.5)
|
||||
mask = vec3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS);
|
||||
else
|
||||
mask = vec3(1.0, MASK_BRIGHTNESS, 1.0);
|
||||
#elif MASK_TYPE == 2
|
||||
float whichMask = fract((gl_FragCoord.x*1.0001) * 0.3333333);
|
||||
vec3 mask = vec3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS);
|
||||
if (whichMask < 0.3333333)
|
||||
mask.x = 1.0;
|
||||
else if (whichMask < 0.6666666)
|
||||
mask.y = 1.0;
|
||||
else
|
||||
mask.z = 1.0;
|
||||
#endif
|
||||
|
||||
gl_FragColor = vec4(colour * mask, 1.0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
38
data/gbc.glsl
Normal file
38
data/gbc.glsl
Normal file
@@ -0,0 +1,38 @@
|
||||
varying vec2 tex_coord;
|
||||
varying vec2 pix_coord;
|
||||
|
||||
#if defined(VERTEX)
|
||||
|
||||
void main()
|
||||
{
|
||||
pix_coord = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001;
|
||||
tex_coord = vec2((gl_Vertex.x+1.0)*0.5, (-gl_Vertex.y+1.0)*0.5);
|
||||
vec4 pos = vec4(gl_Vertex.x * 2.0, gl_Vertex.y * 2.0, gl_Vertex.z, gl_Vertex.w);
|
||||
gl_Position = gl_Vertex; //(gl_Vertex*2)-vec3(1.0, 1.0, 1.0);//gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
|
||||
#elif defined(FRAGMENT)
|
||||
|
||||
uniform sampler2D Texture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = sign(pix_coord.x)*floor(abs(pix_coord.x)+0.5);
|
||||
float y = sign(pix_coord.y)*floor(abs(pix_coord.y)+0.5);
|
||||
float column = mod(x,4.0);
|
||||
float row = mod(y,4.0);
|
||||
vec4 color = texture2D(Texture, tex_coord);
|
||||
vec4 newcolor;
|
||||
if ((column == 0.0) || (row == 0.0) ) {
|
||||
newcolor = color * vec4(0.4, 0.4, 0.4, 1.0);
|
||||
} else if ((column == 1.0) || (row == 1.0) ) {
|
||||
newcolor = color * vec4(0.6, 0.7, 0.8, 1.0);
|
||||
} else if ((column == 3.0) || (row == 3.0) ) {
|
||||
newcolor = color * vec4(0.8, 0.7, 0.6, 1.0);
|
||||
} else {
|
||||
newcolor = color;
|
||||
}
|
||||
gl_FragColor = newcolor;
|
||||
}
|
||||
|
||||
#endif
|
||||
41
data/lynx.glsl
Normal file
41
data/lynx.glsl
Normal file
@@ -0,0 +1,41 @@
|
||||
varying vec2 tex_coord;
|
||||
varying vec2 pix_coord;
|
||||
|
||||
#if defined(VERTEX)
|
||||
|
||||
void main()
|
||||
{
|
||||
pix_coord = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001;
|
||||
tex_coord = vec2((gl_Vertex.x+1.0)*0.5, (-gl_Vertex.y+1.0)*0.5);
|
||||
vec4 pos = vec4(gl_Vertex.x * 2.0, gl_Vertex.y * 2.0, gl_Vertex.z, gl_Vertex.w);
|
||||
gl_Position = gl_Vertex; //(gl_Vertex*2)-vec3(1.0, 1.0, 1.0);//gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
|
||||
#elif defined(FRAGMENT)
|
||||
|
||||
uniform sampler2D Texture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = sign(pix_coord.x)*floor(abs(pix_coord.x)+0.5);
|
||||
float column = mod(x,4.0);
|
||||
vec4 color = texture2D(Texture, tex_coord);
|
||||
float xfade = abs((tex_coord.s * 2.0) - 1.0);
|
||||
xfade = xfade * xfade * xfade * xfade * xfade;
|
||||
float yfade = abs((tex_coord.t * 2.0) - 1.0);
|
||||
yfade = yfade * yfade * yfade * yfade * yfade;
|
||||
color = color + vec4(0.7, 0.7, 0.7, 0.0) * (1.0-tex_coord.t) * (1.0-xfade) * (1.0-yfade);
|
||||
vec4 newcolor;
|
||||
if (column == 0.0) {
|
||||
newcolor = color * vec4(1.0, 0.4, 0.6, 1.0);
|
||||
} else if (column == 1.0) {
|
||||
newcolor = color * vec4(0.4, 1.0, 0.4, 1.0);
|
||||
} else if (column == 2.0) {
|
||||
newcolor = color * vec4(0.6, 0.4, 1.0, 1.0);
|
||||
} else {
|
||||
newcolor = color * vec4(0.2, 0.2, 0.2, 1.0);
|
||||
}
|
||||
gl_FragColor = newcolor;
|
||||
}
|
||||
|
||||
#endif
|
||||
5
lagueirtofile
Normal file
5
lagueirtofile
Normal file
@@ -0,0 +1,5 @@
|
||||
libs = -lSDL3 -lGL
|
||||
cppflags = -D DEBUG -D VERBOSE -g -Wall
|
||||
executable = arounders
|
||||
sourcepath = source source/aux source/japi source/gamestates source/entities
|
||||
buildpath = build
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "aux_font.h"
|
||||
#include "jgame.h"
|
||||
#include "font.h"
|
||||
#include "../japi/game.h"
|
||||
|
||||
namespace font
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "aux_textfile.h"
|
||||
#include "textfile.h"
|
||||
|
||||
#include "jgame.h"
|
||||
#include "../japi/game.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace textfile
|
||||
@@ -22,7 +22,7 @@ namespace textfile
|
||||
|
||||
const bool open(std::string filename)
|
||||
{
|
||||
buffer = file::getFileBuffer(filename.c_str(), &fsize);
|
||||
buffer = file::getFileBuffer(filename.c_str(), fsize);
|
||||
p = 0;
|
||||
|
||||
return true;
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "proc_arounders.h"
|
||||
#include "jgame.h"
|
||||
#include "proc_mapa.h"
|
||||
#include "proc_explosio.h"
|
||||
#include "arounders.h"
|
||||
#include "../japi/game.h"
|
||||
#include "mapa.h"
|
||||
#include "explosio.h"
|
||||
|
||||
namespace arounders
|
||||
{
|
||||
@@ -55,9 +55,6 @@ namespace arounders
|
||||
bool pujarEscalo(arounder *a);
|
||||
bool baixarEscalo(arounder *a);
|
||||
|
||||
bool checkArounderAnt(arounder *a, int x, int y, int o);
|
||||
bool checkArounderSig(arounder *a, int x, int y, int o);
|
||||
|
||||
void doCavar(arounder *a);
|
||||
void doPerforar(arounder *a);
|
||||
void doEscalera(arounder *a, int desfase = 0);
|
||||
@@ -681,11 +678,12 @@ namespace arounders
|
||||
|
||||
bool blockArounder(arounder *a)
|
||||
{
|
||||
bool resultado = false;
|
||||
if (a->anterior) resultado = checkArounderAnt(a->anterior, a->x, a->y, a->orientacio);
|
||||
if (a->siguiente) resultado = resultado || checkArounderSig(a->siguiente, a->x, a->y, a->orientacio);
|
||||
|
||||
return resultado;
|
||||
arounder *b = arounders::first;
|
||||
while (b) {
|
||||
if ( (a != b) && ( b->accio == arounders::accions::parar ) && ( a->y >= b->y-8 ) && ( a->y <= b->y+8 ) && ( b->x == a->x+8-(a->orientacio*2) ) ) return true;
|
||||
b = b->siguiente;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool blockParet(arounder *a, int desfase)
|
||||
@@ -835,33 +833,6 @@ namespace arounders
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
bool checkArounderAnt(arounder *a, int x, int y, int o)
|
||||
{
|
||||
if ( a->accio == arounders::accions::parar && (y >= a->y-8) && (y <= a->y+8) && ( (o == arounders::orientacions::dreta && a->x == x+8) || (o == arounders::orientacions::esquerra && a->x == x-8) ) ) {
|
||||
return true;
|
||||
} else {
|
||||
if (a->anterior) {
|
||||
return checkArounderAnt(a->anterior, x, y, o);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool checkArounderSig(arounder *a, int x, int y, int o)
|
||||
{
|
||||
if ( a->accio == arounders::accions::parar && (y >= a->y-8) && (y <= a->y+8) && ( (o == arounders::orientacions::dreta && a->x == x+8) || (o == arounders::orientacions::esquerra && a->x == x-8) ) ) {
|
||||
return true;
|
||||
} else {
|
||||
if (a->siguiente) {
|
||||
return checkArounderSig(a->siguiente, x, y, o);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void doCavar(arounder *a)
|
||||
{
|
||||
if (a->orientacio == arounders::orientacions::dreta) {
|
||||
@@ -36,7 +36,6 @@ namespace arounders
|
||||
arounder *siguiente;
|
||||
};
|
||||
|
||||
extern arounder *first;
|
||||
extern arounder *seleccionat;
|
||||
|
||||
void init();
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "proc_explosio.h"
|
||||
#include "explosio.h"
|
||||
#include <stdlib.h>
|
||||
#include "jgame.h"
|
||||
#include "../japi/game.h"
|
||||
|
||||
namespace explosio
|
||||
{
|
||||
@@ -34,7 +34,7 @@ namespace explosio
|
||||
exp[numexp].pix[i].x = x;
|
||||
exp[numexp].pix[i].y = y;
|
||||
exp[numexp].pix[i].xa = float((rand()%expansio) - (expansio >> 1))/1000.0f;
|
||||
exp[numexp].pix[i].ya = float((rand()%expansio) - (expansio >> 1))/1000.0f;
|
||||
exp[numexp].pix[i].ya = -float((rand()%expansio))/1000.0f;
|
||||
exp[numexp].pix[i].g = 0.05;
|
||||
exp[numexp].pix[i].c = rand()%60;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "proc_mapa.h"
|
||||
#include "jgame.h"
|
||||
#include "aux_textfile.h"
|
||||
#include "aux_font.h"
|
||||
#include "mapa.h"
|
||||
#include "../japi/game.h"
|
||||
#include "../aux/textfile.h"
|
||||
#include "../aux/font.h"
|
||||
namespace mapa
|
||||
{
|
||||
namespace botons
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "jgame.h"
|
||||
#include "../japi/game.h"
|
||||
#include <stdint.h>
|
||||
namespace mapa
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "gamestates.h"
|
||||
#include "jgame.h"
|
||||
#include "aux_font.h"
|
||||
#include "../japi/game.h"
|
||||
#include "../aux/font.h"
|
||||
|
||||
namespace gamestate
|
||||
{
|
||||
@@ -25,9 +25,7 @@ namespace gamestate
|
||||
menu::fondo = draw::loadSurface("menuprin.gif", true);
|
||||
menu::cursor = draw::loadSurface("cursor.gif");
|
||||
|
||||
if (audio::getMusicState() != audio::music_state::MUSIC_PLAYING || audio::whichMusic() != "mus3.ogg") {
|
||||
audio::playMusic(audio::loadMusic("mus3.ogg"));
|
||||
}
|
||||
audio::loadAndPlayMusic("mus3.ogg");
|
||||
|
||||
font::selectFont(font::type::colored);
|
||||
font::setColor(font::color::white);
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "gamestates.h"
|
||||
#include "jgame.h"
|
||||
#include "../japi/game.h"
|
||||
|
||||
namespace gamestate
|
||||
{
|
||||
@@ -24,7 +24,7 @@ namespace gamestate
|
||||
fondo = draw::loadSurface("mort.gif", true);
|
||||
cursor = draw::loadSurface("cursor.gif");
|
||||
|
||||
audio::playMusic(audio::loadMusic("mus5.ogg"));
|
||||
audio::loadAndPlayMusic("mus5.ogg");
|
||||
|
||||
draw::fadein();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "gamestates.h"
|
||||
#include "jgame.h"
|
||||
#include "../japi/game.h"
|
||||
#include <string>
|
||||
#include "aux_font.h"
|
||||
#include "proc_mapa.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "../aux/font.h"
|
||||
#include "../entities/mapa.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace gamestate
|
||||
{
|
||||
@@ -120,7 +120,7 @@ namespace gamestate
|
||||
{
|
||||
password[10] = 0;
|
||||
int filesize = 0;
|
||||
const char *buffer = file::getFileBuffer("offsets.bal", &filesize);
|
||||
const char *buffer = file::getFileBuffer("offsets.bal", filesize);
|
||||
|
||||
int punter = 0;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "gamestates.h"
|
||||
#include "jgame.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "../japi/game.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <string>
|
||||
#include "aux_font.h"
|
||||
#include "proc_mapa.h"
|
||||
#include "proc_arounders.h"
|
||||
#include "../aux/font.h"
|
||||
#include "../entities/mapa.h"
|
||||
#include "../entities/arounders.h"
|
||||
|
||||
namespace gamestate
|
||||
{
|
||||
@@ -15,6 +15,7 @@ namespace gamestate
|
||||
const int no = 0;
|
||||
const int postfase = 1;
|
||||
const int mort = 2;
|
||||
const int cheat = 3;
|
||||
}
|
||||
|
||||
draw::surface *faded = nullptr;
|
||||
@@ -31,7 +32,7 @@ namespace gamestate
|
||||
int aigua_frame1 = 0;
|
||||
int aigua_frame2 = 5;
|
||||
|
||||
int exit = 0;
|
||||
int exit = play::eixir::no;
|
||||
|
||||
bool loop();
|
||||
|
||||
@@ -72,8 +73,7 @@ namespace gamestate
|
||||
// arounder_seleccionat = primerArounders
|
||||
|
||||
// Enxufa el arradio
|
||||
audio::loadMusic((game::getConfig("fase")+1) % 5 == 0 ? "mus6.ogg" : "mus4.ogg");
|
||||
audio::playMusic();
|
||||
audio::loadAndPlayMusic((game::getConfig("fase")+1) % 5 == 0 ? "mus6.ogg" : "mus4.ogg");
|
||||
|
||||
// Fiquem a contar el cronómetre de arounders
|
||||
play::arounderCount = play::startTicks = game::getTicks();
|
||||
@@ -95,6 +95,7 @@ namespace gamestate
|
||||
// I anem on toque
|
||||
if (play::exit==play::eixir::postfase) gamestate::postfase::init();
|
||||
else if (play::exit==play::eixir::mort) gamestate::mort::init();
|
||||
else if (play::exit==play::eixir::cheat) gamestate::prefase::init();
|
||||
}
|
||||
// En qualsevol cas, renderitzem i eixim ja del bucle (fins que acabe el fadeout)
|
||||
draw::render();
|
||||
@@ -183,68 +184,99 @@ namespace gamestate
|
||||
arounders::abortarAccio();
|
||||
}
|
||||
|
||||
// Si pulsem el botó de l'esquerra... pos es més complicat! Anem pas per pas...
|
||||
if (input::mouseClk(input::mouse::button::left))
|
||||
{
|
||||
// Si al pulsar no havem seleccionat cap arounder (o siga, no havem fet click sobre un arounder)...
|
||||
if (!arounders::seleccionar())
|
||||
{
|
||||
// Si no havem pulsat en la zona de baix dels botons, i hi ha un arounder seleccionat, i està caminant...
|
||||
if (input::mouseY()<165 && arounders::seleccionat != nullptr && arounders::seleccionat->accio == arounders::accions::caminar)
|
||||
{
|
||||
// Fem que camine en la direcció en que està el cursor
|
||||
arounders::seleccionat->orientacio = input::mouseX() > arounders::seleccionat->x ? arounders::orientacions::dreta : arounders::orientacions::esquerra;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mirem si havem pulsat un botó... (si el botó està a 0, no conta com a pulsat)
|
||||
const int botoPulsat = mapa::procesar();
|
||||
// Si havem pulsat un botó i tenim un arounder seleccionat...
|
||||
if (botoPulsat != -1 && arounders::seleccionat != nullptr)
|
||||
{
|
||||
// Si el botó pulsat se correspon en la accio encolada
|
||||
if (botoPulsat == arounders::seleccionat->prevista) {
|
||||
// La desencolem
|
||||
arounders::seleccionat->prevista = arounders::seleccionat->accio;
|
||||
} else {
|
||||
// Pero si no, la encolem (atenció, moltes accions comencen instantàneament)
|
||||
arounders::seleccionat->prevista = botoPulsat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si ha passat el temps suficient, actualitzem els arounders (i les ones)
|
||||
// Açò pot ser mes tard o mes prompte depenent de si havem accelerat el temps
|
||||
currentTicks = game::getTicks() - startTicks;
|
||||
if( currentTicks >= mapa::velocitat ) {
|
||||
startTicks = game::getTicks();
|
||||
aigua_frame1 = (aigua_frame1+1)%10;
|
||||
aigua_frame2 = (aigua_frame2+1)%10;
|
||||
|
||||
// Ací passa quasi tot lo interessant!!! No t'ho perdes!!
|
||||
arounders::procesar();
|
||||
}
|
||||
|
||||
// Si ha passat el temps suficient...
|
||||
// (també depen de si havem pulsat el botó de passar rapid el temps)
|
||||
if ( (game::getTicks() - arounderCount) >= mapa::velocitat*58) {
|
||||
// I encara queden arounders per eixir...
|
||||
if (mapa::arounders::eixits < mapa::arounders::totals) {
|
||||
// Traguem un nou arounder per la porta
|
||||
arounders::afegir();
|
||||
mapa::arounders::eixits++;
|
||||
arounderCount = game::getTicks();
|
||||
}
|
||||
}
|
||||
|
||||
// Si tots els arounders ja han arribat o han mort...
|
||||
if (mapa::arounders::arrivats + mapa::arounders::morts == mapa::arounders::totals) {
|
||||
// Si han arribat els necessaris...
|
||||
if (mapa::arounders::arrivats >= mapa::arounders::necessaris) {
|
||||
// Pujem el numero de fase en el config i anem a la postfase
|
||||
game::setConfig("fase", game::getConfig("fase")+1);
|
||||
draw::fadeout();
|
||||
audio::fadeoutMusic();
|
||||
play::exit = 1;
|
||||
audio::fadeOutMusic();
|
||||
play::exit = play::eixir::postfase;
|
||||
} else {
|
||||
// Sino, anem a la pantalla de mort
|
||||
draw::fadeout();
|
||||
audio::fadeoutMusic();
|
||||
play::exit = 2;
|
||||
audio::fadeOutMusic();
|
||||
play::exit = play::eixir::mort;
|
||||
}
|
||||
}
|
||||
|
||||
if (input::keyPressed(SDL_SCANCODE_F12)) {
|
||||
game::setConfig("fase", game::getConfig("fase")+1);
|
||||
draw::fadeout();
|
||||
audio::fadeOutMusic();
|
||||
play::exit = play::eixir::cheat;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mentres estem en pausa, este es el bucle principal
|
||||
bool loop_pause()
|
||||
{
|
||||
// Consisteix en pintar la surface sobre la que havem pintat la foto del que hi havia en pantalla
|
||||
draw::draw(faded);
|
||||
// ...I el cursor...
|
||||
draw::setSource(cursor);
|
||||
draw::draw(input::mouseX(), input::mouseY());
|
||||
|
||||
draw::render();
|
||||
|
||||
// I mirem si s'ha polsat P o ESC, per a tornat al bucle principal del mòdul de joc
|
||||
if (input::keyPressed(SDL_SCANCODE_P) || input::keyPressed(SDL_SCANCODE_ESCAPE))
|
||||
{
|
||||
play::backToLoop();
|
||||
@@ -252,10 +284,14 @@ namespace gamestate
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mentres estem en el menú, este es el bucle principal
|
||||
bool loop_menu()
|
||||
{
|
||||
// Si havem triat eixir...
|
||||
if (play::exit) {
|
||||
// esperem a que acabe el fade...
|
||||
if (!draw::isfading()) {
|
||||
// I quan acabe
|
||||
play::finalize();
|
||||
if (exit==1) {
|
||||
mapa::carregar();
|
||||
@@ -287,13 +323,13 @@ namespace gamestate
|
||||
if (input::mouseY() >= 71 && input::mouseY() <= 76) {
|
||||
free(play::original_palette);
|
||||
draw::fadeout();
|
||||
audio::fadeoutMusic();
|
||||
audio::fadeOutMusic();
|
||||
play::exit = 1;
|
||||
}
|
||||
if (input::mouseY() >= 82 && input::mouseY() <= 87) {
|
||||
free(play::original_palette);
|
||||
draw::fadeout();
|
||||
audio::fadeoutMusic();
|
||||
audio::fadeOutMusic();
|
||||
play::exit = 2;
|
||||
}
|
||||
if (input::mouseY() >= 93 && input::mouseY() <= 98) {
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "gamestates.h"
|
||||
#include "jgame.h"
|
||||
#include "../japi/game.h"
|
||||
#include <string>
|
||||
#include "aux_font.h"
|
||||
#include "../aux/font.h"
|
||||
|
||||
namespace gamestate
|
||||
{
|
||||
@@ -31,8 +31,7 @@ namespace gamestate
|
||||
gamestate::sequence::init();
|
||||
return;
|
||||
} else {
|
||||
audio::loadMusic("mus3.ogg");
|
||||
audio::playMusic();
|
||||
audio::loadAndPlayMusic("mus3.ogg");
|
||||
}
|
||||
|
||||
if (game::getConfig("fase") % 5 == 0) {
|
||||
@@ -104,7 +103,8 @@ namespace gamestate
|
||||
|
||||
std::string getPassword()
|
||||
{
|
||||
char *buffer = file::getFileBuffer("offsets.bal");
|
||||
int size;
|
||||
char *buffer = file::getFileBuffer("offsets.bal", size);
|
||||
|
||||
int punter = (game::getConfig("fase")-1)*10;
|
||||
char passFile[11];
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "gamestates.h"
|
||||
#include "jgame.h"
|
||||
#include "../japi/game.h"
|
||||
#include <string>
|
||||
#include "aux_font.h"
|
||||
#include "proc_mapa.h"
|
||||
#include "../aux/font.h"
|
||||
#include "../entities/mapa.h"
|
||||
|
||||
namespace gamestate
|
||||
{
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "gamestates.h"
|
||||
#include "jgame.h"
|
||||
#include "../japi/game.h"
|
||||
#include <string>
|
||||
#include <SDL2/SDL.h>
|
||||
#include "aux_font.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include "../aux/font.h"
|
||||
|
||||
namespace gamestate
|
||||
{
|
||||
@@ -33,7 +33,8 @@ namespace gamestate
|
||||
default: gamestate::prefase::init(); return; break;
|
||||
}
|
||||
|
||||
sequence_file = file::getFilePointer(filename);
|
||||
int size;
|
||||
sequence_file = file::getFilePointer(filename.c_str(), size);
|
||||
|
||||
game::setState(&gamestate::sequence::loop);
|
||||
}
|
||||
@@ -98,15 +99,14 @@ namespace gamestate
|
||||
|
||||
} else if (command=="PLAYMUSIC") {
|
||||
fscanf(sequence_file, " '%[^']'", text);
|
||||
audio::loadMusic(text);
|
||||
audio::playMusic();
|
||||
audio::loadAndPlayMusic(text);
|
||||
|
||||
} else if (command=="FADEOUT") {
|
||||
draw::fadeout();
|
||||
|
||||
} else if (command=="FADEOUTMUSIC") {
|
||||
draw::fadeout();
|
||||
audio::fadeoutMusic();
|
||||
audio::fadeOutMusic();
|
||||
|
||||
} else if (command=="END") {
|
||||
fclose(sequence_file);
|
||||
@@ -125,7 +125,7 @@ namespace gamestate
|
||||
|
||||
void drawPic(std::string filename)
|
||||
{
|
||||
draw::surface *pic = draw::loadSurface(filename, true);
|
||||
draw::surface *pic = draw::loadSurface(filename.c_str(), true);
|
||||
draw::draw(pic);
|
||||
draw::freeSurface(pic);
|
||||
}
|
||||
493
source/japi/audio.cpp
Normal file
493
source/japi/audio.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
#include "audio.h"
|
||||
#include "file.h"
|
||||
#include "stb_vorbis.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace audio {
|
||||
|
||||
#define MAX_CHANNELS 5
|
||||
|
||||
struct sound
|
||||
{
|
||||
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
|
||||
Uint32 length { 0 };
|
||||
Uint8 *buffer { NULL };
|
||||
};
|
||||
|
||||
struct channel
|
||||
{
|
||||
audio::sound *sound { nullptr };
|
||||
int pos { 0 };
|
||||
int times { 0 };
|
||||
SDL_AudioStream *stream { nullptr };
|
||||
audio::state state { audio::state::available };
|
||||
};
|
||||
|
||||
struct music
|
||||
{
|
||||
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
|
||||
Uint32 length { 0 };
|
||||
Uint8 *buffer { nullptr };
|
||||
char *filename { nullptr };
|
||||
|
||||
int pos { 0 };
|
||||
int times { 0 };
|
||||
SDL_AudioStream *stream { nullptr };
|
||||
audio::state state { audio::state::invalid };
|
||||
};
|
||||
|
||||
audio::music *current_music { nullptr };
|
||||
audio::channel channels[MAX_CHANNELS];
|
||||
|
||||
SDL_AudioSpec audio_spec { SDL_AUDIO_S16, 2, 48000 };
|
||||
float music_volume { 1.0f };
|
||||
float sound_volume { 0.5f };
|
||||
bool music_enabled { true };
|
||||
bool sound_enabled { true };
|
||||
SDL_AudioDeviceID audio_device { 0 };
|
||||
SDL_TimerID timer_id { 0 };
|
||||
|
||||
bool fading { false };
|
||||
int fade_start_time;
|
||||
int fade_duration;
|
||||
int fade_initial_volume;
|
||||
|
||||
Uint32 updateCallback(void *userdata, SDL_TimerID timer_id, Uint32 interval)
|
||||
{
|
||||
if (music_enabled && current_music && current_music->state == audio::state::playing)
|
||||
{
|
||||
if (fading)
|
||||
{
|
||||
const int time = SDL_GetTicks();
|
||||
if (time > (fade_start_time+fade_duration)) {
|
||||
fading = false;
|
||||
stopMusic();
|
||||
return 30;
|
||||
} else {
|
||||
const int time_passed = time - fade_start_time;
|
||||
const float percent = (float)time_passed / (float)fade_duration;
|
||||
SDL_SetAudioStreamGain(current_music->stream, music_volume*(1.0 - percent));
|
||||
}
|
||||
}
|
||||
|
||||
if (current_music->times != 0)
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(current_music->stream) < int(current_music->length/2))
|
||||
{
|
||||
SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length);
|
||||
}
|
||||
if (current_music->times>0) current_music->times--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(current_music->stream) == 0) stopMusic();
|
||||
}
|
||||
}
|
||||
|
||||
if (sound_enabled)
|
||||
{
|
||||
for (int i=0; i < MAX_CHANNELS; ++i)
|
||||
if (channels[i].state == audio::state::playing)
|
||||
{
|
||||
if (channels[i].times != 0)
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(channels[i].stream) < int(channels[i].sound->length/2))
|
||||
{
|
||||
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
|
||||
if (channels[i].times>0) channels[i].times--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) stopChannel(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 30;
|
||||
}
|
||||
|
||||
void init(const int freq, const SDL_AudioFormat format, const int num_channels)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
|
||||
#endif
|
||||
|
||||
SDL_Log("Iniciant JailAudio...");
|
||||
audio_spec = {format, num_channels, freq };
|
||||
if (!audio_device) SDL_CloseAudioDevice(audio_device);
|
||||
audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audio_spec);
|
||||
SDL_Log( (audio_device==0) ? "Failed to initialize SDL audio!\n" : "OK!\n");
|
||||
for (int i=0;i<MAX_CHANNELS;++i) channels[i].state = audio::state::available;
|
||||
timer_id = SDL_AddTimer(30, updateCallback, nullptr);
|
||||
}
|
||||
|
||||
void quit()
|
||||
{
|
||||
if (timer_id) SDL_RemoveTimer(timer_id);
|
||||
|
||||
if (!audio_device) SDL_CloseAudioDevice(audio_device);
|
||||
audio_device = 0;
|
||||
}
|
||||
|
||||
audio::music *loadMusic(Uint8* buffer, Uint32 length)
|
||||
{
|
||||
audio::music *music = new audio::music();
|
||||
|
||||
int chan, samplerate;
|
||||
short *output;
|
||||
music->length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2;
|
||||
|
||||
music->spec.channels = chan;
|
||||
music->spec.freq = samplerate;
|
||||
music->spec.format = SDL_AUDIO_S16;
|
||||
music->buffer = (Uint8*)SDL_malloc(music->length);
|
||||
SDL_memcpy(music->buffer, output, music->length);
|
||||
free(output);
|
||||
music->pos = 0;
|
||||
music->state = audio::state::stopped;
|
||||
|
||||
return music;
|
||||
}
|
||||
|
||||
audio::music *loadMusic(const char* filename)
|
||||
{
|
||||
int size;
|
||||
char *buffer = file::getFileBuffer(filename, size);
|
||||
if (buffer == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadMusic): No s'ha trobat l'arxiu %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
audio::music *music = loadMusic((Uint8*)buffer, size);
|
||||
|
||||
music->filename = (char*)malloc(strlen(filename)+1);
|
||||
strcpy(music->filename, filename);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return music;
|
||||
}
|
||||
|
||||
void playMusic(audio::music *music, const int loop)
|
||||
{
|
||||
if (!music_enabled) return;
|
||||
|
||||
stopMusic();
|
||||
|
||||
current_music = music;
|
||||
current_music->pos = 0;
|
||||
current_music->state = audio::state::playing;
|
||||
current_music->times = loop;
|
||||
|
||||
current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &audio_spec);
|
||||
if (!current_music->stream) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_CreateAudioStream: %s\n", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_PutAudioStreamData: %s\n", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SDL_SetAudioStreamGain(current_music->stream, music_volume);
|
||||
|
||||
if (!SDL_BindAudioStream(audio_device, current_music->stream)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::playMusic): SDL_BindAudioStream: %s\n", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void loadAndPlayMusic(const char* filename, const bool force_reinit)
|
||||
{
|
||||
if ( (getMusicState() != audio::state::playing) || (strcmp(getMusicFilename(), filename)!=0) || force_reinit )
|
||||
{
|
||||
playMusic(loadMusic(filename));
|
||||
}
|
||||
}
|
||||
|
||||
char *getMusicFilename(audio::music *music)
|
||||
{
|
||||
if (!music) music = current_music;
|
||||
return music->filename;
|
||||
}
|
||||
|
||||
void pauseMusic()
|
||||
{
|
||||
if (!music_enabled) return;
|
||||
if (!current_music || current_music->state == audio::state::invalid) return;
|
||||
|
||||
current_music->state = audio::state::paused;
|
||||
SDL_UnbindAudioStream(current_music->stream);
|
||||
}
|
||||
|
||||
void resumeMusic()
|
||||
{
|
||||
if (!music_enabled) return;
|
||||
if (!current_music || current_music->state == audio::state::invalid) return;
|
||||
|
||||
current_music->state = audio::state::playing;
|
||||
SDL_BindAudioStream(audio_device, current_music->stream);
|
||||
}
|
||||
|
||||
void stopMusic()
|
||||
{
|
||||
if (!music_enabled) return;
|
||||
if (!current_music || current_music->state == audio::state::invalid) return;
|
||||
|
||||
current_music->pos = 0;
|
||||
current_music->state = audio::state::stopped;
|
||||
SDL_DestroyAudioStream(current_music->stream);
|
||||
current_music->stream = nullptr;
|
||||
free(current_music->filename);
|
||||
current_music->filename = nullptr;
|
||||
}
|
||||
|
||||
void fadeOutMusic(const int milliseconds)
|
||||
{
|
||||
if (!music_enabled) return;
|
||||
if (current_music == NULL || current_music->state == audio::state::invalid) return;
|
||||
|
||||
fading = true;
|
||||
fade_start_time = SDL_GetTicks();
|
||||
fade_duration = milliseconds;
|
||||
fade_initial_volume = music_volume;
|
||||
}
|
||||
|
||||
audio::state getMusicState()
|
||||
{
|
||||
if (!music_enabled) return audio::state::disabled;
|
||||
if (!current_music) return audio::state::invalid;
|
||||
|
||||
return current_music->state;
|
||||
}
|
||||
|
||||
void deleteMusic(audio::music *music)
|
||||
{
|
||||
if (current_music == music) current_music = nullptr;
|
||||
SDL_free(music->buffer);
|
||||
if (music->stream) SDL_DestroyAudioStream(music->stream);
|
||||
delete music;
|
||||
}
|
||||
|
||||
float setMusicVolume(float volume)
|
||||
{
|
||||
music_volume = SDL_clamp( volume, 0.0f, 1.0f );
|
||||
if (current_music) SDL_SetAudioStreamGain(current_music->stream, music_volume);
|
||||
return music_volume;
|
||||
}
|
||||
|
||||
void setMusicPosition(float value)
|
||||
{
|
||||
if (!current_music) return;
|
||||
current_music->pos = value * current_music->spec.freq;
|
||||
}
|
||||
|
||||
float getMusicPosition()
|
||||
{
|
||||
if (!current_music) return 0;
|
||||
return float(current_music->pos)/float(current_music->spec.freq);
|
||||
}
|
||||
|
||||
void enableMusic(const bool value)
|
||||
{
|
||||
if ( !value && current_music && (current_music->state==audio::state::playing) ) stopMusic();
|
||||
|
||||
music_enabled = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
audio::sound *newSound(Uint8* buffer, Uint32 length)
|
||||
{
|
||||
audio::sound *sound = new audio::sound();
|
||||
sound->buffer = buffer;
|
||||
sound->length = length;
|
||||
return sound;
|
||||
}
|
||||
|
||||
audio::sound *loadSound(uint8_t* buffer, uint32_t size)
|
||||
{
|
||||
audio::sound *sound = new audio::sound();
|
||||
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &sound->spec, &sound->buffer, &sound->length);
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
audio::sound *loadSound(const char* filename)
|
||||
{
|
||||
int size;
|
||||
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
|
||||
|
||||
if (buffer == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR (audio::loadSound): No s'ha trobat l'arxiu %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
audio::sound *sound = loadSound(buffer, size);
|
||||
free(buffer);
|
||||
return sound;
|
||||
}
|
||||
|
||||
int playSound(audio::sound *sound, const int loop)
|
||||
{
|
||||
if (!sound_enabled) return -1;
|
||||
|
||||
int channel = 0;
|
||||
while (channel < MAX_CHANNELS && channels[channel].state != audio::state::available) { channel++; }
|
||||
if (channel == MAX_CHANNELS) channel = 0;
|
||||
stopChannel(channel);
|
||||
|
||||
channels[channel].sound = sound;
|
||||
channels[channel].times = loop;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].state = audio::state::playing;
|
||||
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &audio_spec);
|
||||
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
||||
SDL_SetAudioStreamGain(channels[channel].stream, sound_volume);
|
||||
SDL_BindAudioStream(audio_device, channels[channel].stream);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
int playSoundOnChannel(audio::sound *sound, const int channel, const int loop)
|
||||
{
|
||||
if (!sound_enabled) return -1;
|
||||
|
||||
if (channel < 0 || channel >= MAX_CHANNELS) return -1;
|
||||
stopChannel(channel);
|
||||
|
||||
channels[channel].sound = sound;
|
||||
channels[channel].times = loop;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].state = audio::state::playing;
|
||||
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &audio_spec);
|
||||
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
||||
SDL_SetAudioStreamGain(channels[channel].stream, sound_volume);
|
||||
SDL_BindAudioStream(audio_device, channels[channel].stream);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
void deleteSound(audio::sound *sound)
|
||||
{
|
||||
for (int i = 0; i < MAX_CHANNELS; i++) {
|
||||
if (channels[i].sound == sound) stopChannel(i);
|
||||
}
|
||||
SDL_free(sound->buffer);
|
||||
delete sound;
|
||||
}
|
||||
|
||||
void pauseChannel(const int channel)
|
||||
{
|
||||
if (!sound_enabled) return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
||||
if (channels[i].state == audio::state::playing)
|
||||
{
|
||||
channels[i].state = audio::state::paused;
|
||||
SDL_UnbindAudioStream(channels[i].stream);
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < MAX_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state == audio::state::playing)
|
||||
{
|
||||
channels[channel].state = audio::state::paused;
|
||||
SDL_UnbindAudioStream(channels[channel].stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resumeChannel(const int channel)
|
||||
{
|
||||
if (!sound_enabled) return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
||||
if (channels[i].state == audio::state::paused)
|
||||
{
|
||||
channels[i].state = audio::state::playing;
|
||||
SDL_BindAudioStream(audio_device, channels[i].stream);
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < MAX_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state == audio::state::paused)
|
||||
{
|
||||
channels[channel].state = audio::state::playing;
|
||||
SDL_BindAudioStream(audio_device, channels[channel].stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stopChannel(const int channel)
|
||||
{
|
||||
if (!sound_enabled) return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < MAX_CHANNELS; i++) {
|
||||
if (channels[i].state != audio::state::available) SDL_DestroyAudioStream(channels[i].stream);
|
||||
channels[i].stream = nullptr;
|
||||
channels[i].state = audio::state::available;
|
||||
channels[i].pos = 0;
|
||||
channels[i].sound = NULL;
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < MAX_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state != audio::state::available) SDL_DestroyAudioStream(channels[channel].stream);
|
||||
channels[channel].stream = nullptr;
|
||||
channels[channel].state = audio::state::available;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].sound = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
audio::state getChannelState(const int channel)
|
||||
{
|
||||
if (!sound_enabled) return audio::state::disabled;
|
||||
|
||||
if (channel < 0 || channel >= MAX_CHANNELS) return audio::state::invalid;
|
||||
|
||||
return channels[channel].state;
|
||||
}
|
||||
|
||||
float setSoundVolume(float volume)
|
||||
{
|
||||
sound_volume = SDL_clamp( volume, 0.0f, 1.0f );
|
||||
|
||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
||||
if ( (channels[i].state == audio::state::playing) || (channels[i].state == audio::state::paused) )
|
||||
SDL_SetAudioStreamGain(channels[i].stream, sound_volume);
|
||||
|
||||
return sound_volume;
|
||||
}
|
||||
|
||||
void enableSound(const bool value)
|
||||
{
|
||||
for (int i = 0; i < MAX_CHANNELS; i++)
|
||||
{
|
||||
if (channels[i].state == audio::state::playing) stopChannel(i);
|
||||
}
|
||||
sound_enabled = value;
|
||||
}
|
||||
|
||||
float setVolume(float volume)
|
||||
{
|
||||
setSoundVolume(setMusicVolume(volume) / 2.0f);
|
||||
|
||||
return music_volume;
|
||||
}
|
||||
|
||||
#undef MAX_CHANNELS
|
||||
|
||||
}
|
||||
44
source/japi/audio.h
Normal file
44
source/japi/audio.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace audio
|
||||
{
|
||||
enum state { invalid, available, playing, paused, stopped, disabled };
|
||||
|
||||
struct sound;
|
||||
struct music;
|
||||
|
||||
void init(const int freq, const SDL_AudioFormat format, const int num_channels);
|
||||
void quit();
|
||||
|
||||
audio::music *loadMusic(const char* filename);
|
||||
audio::music *loadMusic(Uint8* buffer, Uint32 length);
|
||||
void playMusic(audio::music *music, const int loop = -1);
|
||||
void loadAndPlayMusic(const char* filename, const bool force_reinit=false);
|
||||
char *getMusicFilename(audio::music *music = nullptr);
|
||||
void pauseMusic();
|
||||
void resumeMusic();
|
||||
void stopMusic();
|
||||
void fadeOutMusic(const int milliseconds=250);
|
||||
audio::state getMusicState();
|
||||
void deleteMusic(audio::music *music);
|
||||
float setMusicVolume(float volume);
|
||||
void setMusicPosition(float value);
|
||||
float getMusicPosition();
|
||||
void enableMusic(const bool value);
|
||||
|
||||
audio::sound *newSound(Uint8* buffer, Uint32 length);
|
||||
audio::sound *loadSound(Uint8* buffer, Uint32 length);
|
||||
audio::sound *loadSound(const char* filename);
|
||||
int playSound(audio::sound *sound, const int loop = 0);
|
||||
int playSoundOnChannel(audio::sound *sound, const int channel, const int loop = 0);
|
||||
void pauseChannel(const int channel);
|
||||
void resumeChannel(const int channel);
|
||||
void stopChannel(const int channel);
|
||||
audio::state getChannelState(const int channel);
|
||||
void deleteSound(audio::sound *sound);
|
||||
float setSoundVolume(float volume);
|
||||
void enableSound(const bool value);
|
||||
|
||||
float setVolume(float volume);
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "jdraw.h"
|
||||
#include "SDL2/SDL.h"
|
||||
#include "gif.c"
|
||||
#include "jfile.h"
|
||||
#include "draw.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include "gif.h"
|
||||
#include "file.h"
|
||||
#include "shader.h"
|
||||
|
||||
namespace draw
|
||||
{
|
||||
@@ -10,22 +11,37 @@ namespace draw
|
||||
// Aleshores, en "render" el contingut de screen se volca a la textura SDL que crearem,
|
||||
// i eixa textura se pintarà a pantalla com se sol fer amb SDL. Ho anirem veient en el codi.
|
||||
|
||||
SDL_Window *sdl_window = nullptr; // La finestra de SDL
|
||||
SDL_Renderer *sdl_renderer = nullptr; // El renderer de SDL
|
||||
SDL_Texture *sdl_texture = nullptr; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla
|
||||
SDL_Window *sdl_window {nullptr}; // La finestra de SDL
|
||||
SDL_Renderer *sdl_renderer {nullptr}; // El renderer de SDL
|
||||
SDL_Texture *sdl_texture {nullptr}; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla
|
||||
SDL_Texture *sdl_shadertex {nullptr}; // La textura de SDL per al shader
|
||||
|
||||
static int screen_zoom = 1;
|
||||
static int screen_zoom = 1;
|
||||
static bool screen_fullscreen = false;
|
||||
static bool screen_cursor = true;
|
||||
static char* screen_shader = nullptr;
|
||||
static bool shader_enabled = false;
|
||||
static float window_ratio = 1;
|
||||
static int canvas_width;
|
||||
static int canvas_height;
|
||||
static int desktop_width;
|
||||
static int desktop_height;
|
||||
static int window_width;
|
||||
static int window_height;
|
||||
static int offset_x = 0;
|
||||
static int offset_y = 0;
|
||||
char window_title[256];
|
||||
|
||||
surface *screen = nullptr; // La superficie screen, que representa la pantalla. Se crea i destrueix internament
|
||||
surface *destination = nullptr; // Punter a la actual superficie de destí
|
||||
surface *source = nullptr; // Punter a la actual superficie d'oritge
|
||||
surface *pushedSource = nullptr; // Punter a la superficie d'oritge que s'ha pushat
|
||||
surface *screen {nullptr}; // La superficie screen, que representa la pantalla. Se crea i destrueix internament
|
||||
surface *destination {nullptr}; // Punter a la actual superficie de destí
|
||||
surface *source {nullptr}; // Punter a la actual superficie d'oritge
|
||||
surface *pushed_source {nullptr}; // Punter a la superficie d'oritge que s'ha pushat
|
||||
|
||||
uint32_t palette[256]; // La paleta de colors
|
||||
uint32_t aux_palette[256]; // La paleta de colors
|
||||
uint8_t color_indices[256]; // Indices dels colors per defecte
|
||||
uint8_t sel_color = 0; // Color seleccionat per defecte
|
||||
uint8_t transparent = 0; // El color transparent
|
||||
uint32_t palette[256]; // La paleta de colors
|
||||
uint32_t aux_palette[256]; // La paleta de colors, para els fadein
|
||||
uint8_t color_indices[256]; // Indices dels colors per defecte
|
||||
uint8_t sel_color {0}; // Color seleccionat per defecte
|
||||
uint8_t transparent {0}; // El color transparent
|
||||
|
||||
//surface *textsurf = nullptr; // Surface on guardar el gif amb la font
|
||||
|
||||
@@ -34,20 +50,112 @@ namespace draw
|
||||
bool fading_out = false;
|
||||
bool fading_in = false;
|
||||
|
||||
// Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla
|
||||
void init(const std::string &titol, const uint16_t width, const uint16_t height, const int zoom)
|
||||
void createDisplay()
|
||||
{
|
||||
screen_zoom = zoom;
|
||||
// Ajustem el zoom i el ratio, per si tenen valors locs
|
||||
if (screen_zoom <= 0) screen_zoom = 1;
|
||||
if (window_ratio <= 0) window_ratio = 1;
|
||||
|
||||
// [TODO] Incloure gestió de pantalla completa
|
||||
// Ajustem el tamany de la finestra, segons el zoom i el ratio
|
||||
window_width = canvas_width*screen_zoom;
|
||||
window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom;
|
||||
|
||||
// Mentres no càpiga en la pantalla, reduïm el zoom
|
||||
while (window_width > desktop_width || window_height > desktop_height) {
|
||||
screen_zoom--;
|
||||
window_width = canvas_width*screen_zoom;
|
||||
window_height = window_ratio != 1 ? int(float(canvas_width)*window_ratio*float(screen_zoom)) : canvas_height*screen_zoom;
|
||||
}
|
||||
|
||||
if (screen_fullscreen) {
|
||||
if (desktop_width * window_ratio > desktop_height) {
|
||||
offset_y = 0;
|
||||
window_height = desktop_height;
|
||||
window_width = desktop_height/window_ratio;
|
||||
offset_x = (desktop_width - window_width)/2;
|
||||
} else {
|
||||
offset_x = 0;
|
||||
window_width = desktop_width;
|
||||
window_height = desktop_width*window_ratio;
|
||||
offset_y = (desktop_height - window_height)/2;
|
||||
}
|
||||
} else {
|
||||
offset_x = offset_y = 0;
|
||||
}
|
||||
|
||||
sdl_window = SDL_CreateWindow(window_title, window_width, window_height, SDL_WINDOW_OPENGL|(screen_fullscreen?SDL_WINDOW_FULLSCREEN:0));
|
||||
if (!sdl_window) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
|
||||
if (!sdl_renderer) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (screen_cursor) SDL_ShowCursor(); else SDL_HideCursor();
|
||||
|
||||
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, canvas_width, canvas_height);
|
||||
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST);
|
||||
if (!sdl_texture) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sdl_shadertex = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, window_width, window_height);
|
||||
SDL_SetTextureScaleMode(sdl_shadertex, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
loadShader();
|
||||
}
|
||||
|
||||
void destroyDisplay()
|
||||
{
|
||||
SDL_DestroyTexture(sdl_texture);
|
||||
SDL_DestroyRenderer(sdl_renderer);
|
||||
SDL_DestroyWindow(sdl_window);
|
||||
}
|
||||
|
||||
// Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla
|
||||
void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen, const float ratio)
|
||||
{
|
||||
screen_zoom = file::getConfigValueInteger("zoom", zoom);
|
||||
screen_fullscreen = file::getConfigValueBool("fullscreen", fullscreen);
|
||||
window_ratio = ratio;
|
||||
canvas_width = width;
|
||||
canvas_height = height;
|
||||
strcpy(window_title, titol);
|
||||
|
||||
const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay());
|
||||
if (!dm)
|
||||
{
|
||||
SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
desktop_width = dm->w;
|
||||
desktop_height = dm->h;
|
||||
|
||||
createDisplay();
|
||||
// Inicialització de les estructures de SDL
|
||||
sdl_window = SDL_CreateWindow(titol.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width * zoom, height * zoom, SDL_WINDOW_SHOWN);
|
||||
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
|
||||
/*
|
||||
sdl_window = SDL_CreateWindow(titol, width * zoom, height * zoom, 0);
|
||||
if (!sdl_window) {
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
|
||||
exit(1);
|
||||
}
|
||||
sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
|
||||
if (!sdl_renderer) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
|
||||
exit(1);
|
||||
}
|
||||
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
|
||||
// Establim el tamany "logic", indepndent del tamany de finestra
|
||||
SDL_RenderSetLogicalSize(sdl_renderer, width, height);
|
||||
SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_NEAREST);
|
||||
if (!sdl_texture) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize texture!\n");
|
||||
exit(1);
|
||||
}
|
||||
*/
|
||||
|
||||
// Creem la superficie "screen" i la establim com a superficie destinació
|
||||
screen = createSurface(width, height);
|
||||
@@ -58,7 +166,7 @@ namespace draw
|
||||
sel_color = transparent = 0;
|
||||
for (int i=0;i<256;++i) color_indices[i] = i;
|
||||
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
//SDL_HideCursor();
|
||||
//textsurf = loadSurface("font.gif");
|
||||
}
|
||||
|
||||
@@ -74,9 +182,7 @@ namespace draw
|
||||
}
|
||||
|
||||
// Destruim tot el relacionat amb SDL
|
||||
SDL_DestroyTexture(sdl_texture);
|
||||
SDL_DestroyRenderer(sdl_renderer);
|
||||
SDL_DestroyWindow(sdl_window);
|
||||
destroyDisplay();
|
||||
|
||||
// Fiquem tots els punters a nullptr, per si de cas no estem eixint del programa
|
||||
// i anem a tornar a inicialitzar el sistema
|
||||
@@ -86,11 +192,105 @@ namespace draw
|
||||
screen = destination = source = nullptr;
|
||||
}
|
||||
|
||||
void setZoom(const int value) {
|
||||
screen_zoom = value;
|
||||
destroyDisplay();
|
||||
createDisplay();
|
||||
file::setConfigValueInteger("zoom", screen_zoom);
|
||||
}
|
||||
|
||||
const int getZoom()
|
||||
{
|
||||
return screen_zoom;
|
||||
}
|
||||
|
||||
const float getScaleX()
|
||||
{
|
||||
return float(window_width) / float(canvas_width);
|
||||
}
|
||||
|
||||
const float getScaleY()
|
||||
{
|
||||
return float(window_height) / float(canvas_height);
|
||||
}
|
||||
|
||||
const int getOffsetX()
|
||||
{
|
||||
return offset_x;
|
||||
}
|
||||
|
||||
const int getOffsetY()
|
||||
{
|
||||
return offset_y;
|
||||
}
|
||||
|
||||
bool getFullscreen() {
|
||||
return screen_fullscreen;
|
||||
}
|
||||
|
||||
void setFullscreen(const bool value) {
|
||||
screen_fullscreen=value;
|
||||
destroyDisplay();
|
||||
createDisplay();
|
||||
file::setConfigValueBool("fullscreen", screen_fullscreen);
|
||||
}
|
||||
|
||||
void loadShader()
|
||||
{
|
||||
char *buffer = nullptr;
|
||||
if (screen_shader) {
|
||||
int size;
|
||||
buffer = file::getFileBuffer(screen_shader, size, true);
|
||||
}
|
||||
shader::setAspectRatio(3.0f/4.0f);
|
||||
shader::init(sdl_window, sdl_shadertex, buffer);
|
||||
if (buffer) free(buffer);
|
||||
}
|
||||
|
||||
void setShader(const char* shader_file)
|
||||
{
|
||||
if (screen_shader) free(screen_shader);
|
||||
screen_shader = (char*)malloc(strlen(shader_file)+1);
|
||||
strcpy(screen_shader, shader_file);
|
||||
loadShader();
|
||||
if (file::getConfigValueBool("shader_enabled", false)) enableShader();
|
||||
}
|
||||
|
||||
void enableShader()
|
||||
{
|
||||
shader_enabled = true;
|
||||
shader::enable();
|
||||
//destroyDisplay();
|
||||
//createDisplay();
|
||||
file::setConfigValueBool("shader_enabled", shader_enabled);
|
||||
}
|
||||
|
||||
void disableShader()
|
||||
{
|
||||
shader_enabled = false;
|
||||
shader::disable();
|
||||
//destroyDisplay();
|
||||
//createDisplay();
|
||||
file::setConfigValueBool("shader_enabled", shader_enabled);
|
||||
}
|
||||
|
||||
void toggleShader()
|
||||
{
|
||||
shader_enabled ? disableShader() : enableShader();
|
||||
}
|
||||
|
||||
void hideCursor()
|
||||
{
|
||||
screen_cursor = false;
|
||||
SDL_HideCursor();
|
||||
}
|
||||
|
||||
void showCursor()
|
||||
{
|
||||
screen_cursor = true;
|
||||
SDL_ShowCursor();
|
||||
}
|
||||
|
||||
// Crea una superficie i torna un punter a ella
|
||||
surface *createSurface(const uint16_t w, const uint16_t h)
|
||||
{
|
||||
@@ -109,12 +309,12 @@ namespace draw
|
||||
}
|
||||
|
||||
// Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
|
||||
surface *loadSurface(const std::string &filename, const bool loadPalette)
|
||||
surface *loadSurface(const char *filename, const bool loadPalette)
|
||||
{
|
||||
// Agafem un buffer de bytes de l'arxiu especificat
|
||||
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
|
||||
int size;
|
||||
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, &size);
|
||||
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
|
||||
|
||||
// Si ens ha tornat nullptr, es que no l'ha trobat, tornem nosaltres també nullptr ja que no s'ha pogut crear la superficie
|
||||
if (buffer == nullptr)
|
||||
@@ -191,12 +391,12 @@ namespace draw
|
||||
|
||||
void pushSource()
|
||||
{
|
||||
pushedSource = source;
|
||||
pushed_source = source;
|
||||
}
|
||||
|
||||
void popSource()
|
||||
{
|
||||
source = pushedSource;
|
||||
source = pushed_source;
|
||||
}
|
||||
|
||||
void setViewport(const int x, const int y, const int w, const int h)
|
||||
@@ -225,12 +425,12 @@ namespace draw
|
||||
}
|
||||
|
||||
// Carrega la paleta d'un GIF i la torna en un array de uint32_t
|
||||
uint32_t *loadPalette(const std::string &filename, int *paletteSize)
|
||||
uint32_t *loadPalette(const char *filename, int *paletteSize)
|
||||
{
|
||||
// Agafem un buffer de bytes de l'arxiu especificat
|
||||
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
|
||||
int size;
|
||||
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, &size);
|
||||
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
|
||||
|
||||
// Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta
|
||||
// Van a ser 256 entrades de 32 bits, cada entrada es un color, amb el format 0xAARRGGBB
|
||||
@@ -321,7 +521,7 @@ namespace draw
|
||||
}
|
||||
|
||||
// Pinta un troç de la superficie "source" en la superficie "destination".
|
||||
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int flip)
|
||||
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const draw::flip flip)
|
||||
{
|
||||
// Si no hi ha superficie d'oritge especificada, no fem res, o petarà el mame
|
||||
if (source == nullptr)
|
||||
@@ -333,14 +533,14 @@ namespace draw
|
||||
int sdx = 1, sdy = 1, ssx = sx, ssy = sy;
|
||||
|
||||
// Però si s'ha especificat que fem flip en horitzontal...
|
||||
if (flip & DRAW_FLIP_HORIZONTAL)
|
||||
if (flip & draw::flip::horizontal)
|
||||
{
|
||||
sdx = -1; // Avançarem 1 pixel en negatiu
|
||||
ssx = sx + w - 1; // I començarem al final, o siga, sumarem a sx el ample
|
||||
}
|
||||
|
||||
// De igual forma per al flip en vertical, per a la y
|
||||
if (flip & DRAW_FLIP_VERTICAL)
|
||||
if (flip & draw::flip::vertical)
|
||||
{
|
||||
sdy = -1;
|
||||
ssy = sy + h - 1;
|
||||
@@ -497,17 +697,19 @@ namespace draw
|
||||
// i el enviem a la textura SDL
|
||||
for (uint32_t i = 0; i < size; ++i)
|
||||
{
|
||||
sdl_pixels[i] = palette[screen->pixels[i]];
|
||||
sdl_pixels[i] = palette[screen->pixels[i]] | 0xff000000;
|
||||
}
|
||||
|
||||
// Desbloquejem la textura
|
||||
SDL_UnlockTexture(sdl_texture);
|
||||
|
||||
SDL_SetRenderTarget(sdl_renderer, sdl_shadertex);
|
||||
// Pintem la textura a pantalla
|
||||
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
||||
SDL_RenderTexture(sdl_renderer, sdl_texture, NULL, NULL);
|
||||
|
||||
// I ho presentem
|
||||
SDL_RenderPresent(sdl_renderer);
|
||||
shader::render();
|
||||
//SDL_RenderPresent(sdl_renderer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#define DRAW_FLIP_NONE 0
|
||||
#define DRAW_FLIP_HORIZONTAL 1
|
||||
#define DRAW_FLIP_VERTICAL 2
|
||||
#define DRAW_FLIP_BOTH 3
|
||||
//#define DRAW_FLIP_NONE 0
|
||||
//#define DRAW_FLIP_HORIZONTAL 1
|
||||
//#define DRAW_FLIP_VERTICAL 2
|
||||
//#define DRAW_FLIP_BOTH 3
|
||||
|
||||
// Unitat per a la gestió dels recursos gràfics i dibuixat en pantalla
|
||||
namespace draw
|
||||
{
|
||||
enum flip { none, horizontal, vertical, both };
|
||||
|
||||
// Estructura per a mantindre una superficie de pintat, la "pantalla virtual" de tota la vida
|
||||
struct surface
|
||||
{
|
||||
@@ -24,12 +25,29 @@ namespace draw
|
||||
/// @param width es el ample de la finestra "virtual"
|
||||
/// @param height es el alt de la finestra "virtual"
|
||||
/// @param zoom es com de grans son els pixels.
|
||||
void init(const std::string &titol, const uint16_t width, const uint16_t height, const int zoom);
|
||||
void init(const char *titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen=false, const float ratio=1.0);
|
||||
|
||||
/// @brief Finalització del sistema (tancar coses de SDL, superficies fixes, etc...)
|
||||
void quit();
|
||||
|
||||
void setZoom(const int value);
|
||||
const int getZoom();
|
||||
const float getScaleX();
|
||||
const float getScaleY();
|
||||
const int getOffsetX();
|
||||
const int getOffsetY();
|
||||
|
||||
bool getFullscreen();
|
||||
void setFullscreen(const bool value);
|
||||
|
||||
void loadShader();
|
||||
void setShader(const char* shader_file);
|
||||
void enableShader();
|
||||
void disableShader();
|
||||
void toggleShader();
|
||||
|
||||
void hideCursor();
|
||||
void showCursor();
|
||||
|
||||
/// @brief Crea una superficie i torna un punter a ella
|
||||
/// @param w ample de la superficie
|
||||
@@ -41,7 +59,7 @@ namespace draw
|
||||
/// @param filename nom de l'arxiu GIF d'on carregar la superficie
|
||||
/// @param loadPalette si es true també se carrega la paleta del GIF
|
||||
/// @return un punter a una nova superficie
|
||||
surface *loadSurface(const std::string &filename, const bool loadPalette = false);
|
||||
surface *loadSurface(const char* filename, const bool loadPalette = false);
|
||||
|
||||
/// @brief Allibera la memòria d'una superficie, els seus pixels inclosos
|
||||
/// @param surf punter a la superficie a alliberar
|
||||
@@ -67,7 +85,7 @@ namespace draw
|
||||
/// @brief Carrega la paleta d'un GIF i la torna en un array de uint32_t
|
||||
/// @param filename nom de l'arxiu GIF d'on carregar la paleta
|
||||
/// @param paletteSize si no es NULL ens torna el tamany de la paleta carregada
|
||||
uint32_t *loadPalette(const std::string &filename, int *paletteSize = nullptr);
|
||||
uint32_t *loadPalette(const char *filename, int *paletteSize = nullptr);
|
||||
|
||||
/// @brief Estableix la paleta del sistema, o part de ella, des d'un array especificat
|
||||
/// @param pal un array de uint32_t
|
||||
@@ -104,7 +122,7 @@ namespace draw
|
||||
/// @param sx coordenada x de l'oritge
|
||||
/// @param sy coordenada y de l'oritge
|
||||
/// @param flip si s'ha de fer flip en hortizontal o vertical (o ambdos)
|
||||
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int flip = DRAW_FLIP_NONE);
|
||||
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const draw::flip flip = draw::flip::none);
|
||||
|
||||
/// @brief Pinta tota la superficie "source" en la superficie "destination", posició (x,y).
|
||||
/// @param x coordenada x de la destinació
|
||||
275
source/japi/file.cpp
Normal file
275
source/japi/file.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "file.h"
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_FILENAME "data.jf2"
|
||||
#define DEFAULT_FOLDER "data/"
|
||||
#define CONFIG_FILENAME "config.txt"
|
||||
|
||||
namespace file
|
||||
{
|
||||
struct file_t
|
||||
{
|
||||
std::string path;
|
||||
uint32_t size;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
std::vector<file_t> toc;
|
||||
|
||||
/* El std::map me fa coses rares, vaig a usar un good old std::vector amb una estructura key,value propia i au, que sempre funciona */
|
||||
struct keyvalue_t {
|
||||
std::string key, value;
|
||||
};
|
||||
|
||||
char *resource_filename = NULL;
|
||||
char *resource_folder = NULL;
|
||||
int file_source = SOURCE_FILE;
|
||||
char scratch[255];
|
||||
static std::string config_folder;
|
||||
std::vector<keyvalue_t> config;
|
||||
|
||||
void setResourceFilename(const char *str) {
|
||||
if (resource_filename != NULL) free(resource_filename);
|
||||
resource_filename = (char*)malloc(strlen(str)+1);
|
||||
strcpy(resource_filename, str);
|
||||
}
|
||||
|
||||
void setResourceFolder(const char *str) {
|
||||
if (resource_folder != NULL) free(resource_folder);
|
||||
resource_folder = (char*)malloc(strlen(str)+1);
|
||||
strcpy(resource_folder, str);
|
||||
}
|
||||
|
||||
void setSource(const int src) {
|
||||
file_source = src%2; // mod 2 so it always is a valid value, 0 (file) or 1 (folder)
|
||||
if (src==SOURCE_FOLDER && resource_folder==NULL) setResourceFolder(DEFAULT_FOLDER);
|
||||
}
|
||||
|
||||
bool getDictionary() {
|
||||
if (resource_filename == NULL) setResourceFilename(DEFAULT_FILENAME);
|
||||
|
||||
std::ifstream fi (resource_filename, std::ios::binary);
|
||||
if (!fi.is_open()) return false;
|
||||
char header[4];
|
||||
fi.read(header, 4);
|
||||
uint32_t num_files, toc_offset;
|
||||
fi.read((char*)&num_files, 4);
|
||||
fi.read((char*)&toc_offset, 4);
|
||||
fi.seekg(toc_offset);
|
||||
|
||||
for (uint i=0; i<num_files; ++i)
|
||||
{
|
||||
uint32_t file_offset, file_size;
|
||||
fi.read( (char*)&file_offset, 4 );
|
||||
fi.read( (char*)&file_size, 4 );
|
||||
uint8_t path_size;
|
||||
fi.read( (char*)&path_size, 1 );
|
||||
char file_name[path_size+1];
|
||||
fi.read( file_name, path_size );
|
||||
file_name[path_size] = 0;
|
||||
std::string filename = file_name;
|
||||
toc.push_back({filename, file_size, file_offset});
|
||||
}
|
||||
fi.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
char *getFilenameWithFolder(const char* filename) {
|
||||
strcpy(scratch, resource_folder);
|
||||
strcat(scratch, filename);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
FILE *getFilePointer(const char *resourcename, int& filesize, const bool binary) {
|
||||
|
||||
if (file_source==SOURCE_FILE and toc.size()==0) {
|
||||
if (not getDictionary()) setSource(SOURCE_FOLDER);
|
||||
}
|
||||
|
||||
FILE *f;
|
||||
|
||||
if (file_source==SOURCE_FILE) {
|
||||
bool found = false;
|
||||
uint32_t count = 0;
|
||||
while( !found && count < toc.size() ) {
|
||||
found = ( std::string(resourcename) == toc[count].path );
|
||||
if( !found ) count++;
|
||||
}
|
||||
|
||||
if( !found ) {
|
||||
perror("El recurs no s'ha trobat en l'arxiu de recursos");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
filesize = toc[count].size;
|
||||
|
||||
f = fopen(resource_filename, binary?"rb":"r");
|
||||
if (not f) {
|
||||
perror("No s'ha pogut obrir l'arxiu de recursos");
|
||||
exit(1);
|
||||
}
|
||||
fseek(f, toc[count].offset, SEEK_SET);
|
||||
} else {
|
||||
f = fopen(getFilenameWithFolder(resourcename), binary?"rb":"r");
|
||||
fseek(f, 0, SEEK_END);
|
||||
filesize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
char *getFileBuffer(const char *resourcename, int& filesize, const bool zero_terminate) {
|
||||
FILE *f = getFilePointer(resourcename, filesize, true);
|
||||
char* buffer = (char*)malloc(zero_terminate?filesize+1:filesize);
|
||||
fread(buffer, filesize, 1, f);
|
||||
if (zero_terminate) buffer[filesize]=0;
|
||||
fclose(f);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Crea la carpeta del sistema donde guardar datos
|
||||
void setConfigFolder(const char *foldername)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
config_folder = std::string(getenv("APPDATA")) + "/" + foldername;
|
||||
#elif __APPLE__
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *homedir = pw->pw_dir;
|
||||
config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
|
||||
#elif __linux__
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *homedir = pw->pw_dir;
|
||||
config_folder = std::string(homedir) + "/." + foldername;
|
||||
#endif
|
||||
|
||||
struct stat st = {0};
|
||||
if (stat(config_folder.c_str(), &st) == -1)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int ret = mkdir(config_folder.c_str());
|
||||
#else
|
||||
int ret = mkdir(config_folder.c_str(), S_IRWXU);
|
||||
#endif
|
||||
|
||||
if (ret == -1)
|
||||
{
|
||||
printf("ERROR CREATING CONFIG FOLDER.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *getConfigFolder() {
|
||||
std::string folder = config_folder + "/";
|
||||
return folder.c_str();
|
||||
}
|
||||
|
||||
void loadConfigValues() {
|
||||
config.clear();
|
||||
std::string config_file = config_folder + "/config.txt";
|
||||
FILE *f = fopen(config_file.c_str(), "r");
|
||||
if (!f) return;
|
||||
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
char *value = strchr(line, '=');
|
||||
if (value) {
|
||||
*value='\0'; value++;
|
||||
value[strlen(value)-1] = '\0';
|
||||
config.push_back({line, value});
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void saveConfigValues() {
|
||||
std::string config_file = config_folder + "/config.txt";
|
||||
FILE *f = fopen(config_file.c_str(), "w");
|
||||
if (f) {
|
||||
for (auto pair : config) {
|
||||
fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str());
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
const char* getConfigValueString(const char *key) {
|
||||
if (config.empty()) loadConfigValues();
|
||||
for (auto pair : config) {
|
||||
if (pair.key == std::string(key)) {
|
||||
strcpy(scratch, pair.value.c_str());
|
||||
return scratch;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int getConfigValueInteger(const char *key, const int default_value)
|
||||
{
|
||||
const char* value = getConfigValueString(key);
|
||||
if (!value) return default_value;
|
||||
return atoi(value);
|
||||
}
|
||||
|
||||
const float getConfigValueFloat(const char *key, const float default_value)
|
||||
{
|
||||
const char* value = getConfigValueString(key);
|
||||
if (!value) return default_value;
|
||||
return atof(value);
|
||||
}
|
||||
|
||||
const bool getConfigValueBool(const char *key, const bool default_value)
|
||||
{
|
||||
const char* value = getConfigValueString(key);
|
||||
if (!value) return default_value;
|
||||
return strcmp(value, "true")==0?true:false;
|
||||
}
|
||||
|
||||
void setConfigValueString(const char* key, const char* value) {
|
||||
if (config.empty()) loadConfigValues();
|
||||
for (auto &pair : config) {
|
||||
if (pair.key == std::string(key)) {
|
||||
pair.value = value;
|
||||
saveConfigValues();
|
||||
return;
|
||||
}
|
||||
}
|
||||
config.push_back({key, value});
|
||||
saveConfigValues();
|
||||
return;
|
||||
}
|
||||
|
||||
void setConfigValueInteger(const char* key, const int value)
|
||||
{
|
||||
char tmp[256];
|
||||
sprintf(tmp, "%i", value);
|
||||
setConfigValueString(key, tmp);
|
||||
}
|
||||
|
||||
void setConfigValueFloat(const char* key, const float value)
|
||||
{
|
||||
char tmp[256];
|
||||
sprintf(tmp, "%.2f", value);
|
||||
setConfigValueString(key, tmp);
|
||||
}
|
||||
|
||||
void setConfigValueBool(const char* key, const bool value)
|
||||
{
|
||||
setConfigValueString(key, value?"true":"false");
|
||||
}
|
||||
|
||||
}
|
||||
27
source/japi/file.h
Normal file
27
source/japi/file.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
|
||||
#define SOURCE_FILE 0
|
||||
#define SOURCE_FOLDER 1
|
||||
|
||||
namespace file
|
||||
{
|
||||
void setConfigFolder(const char *foldername);
|
||||
const char *getConfigFolder();
|
||||
|
||||
void setResourceFilename(const char *str);
|
||||
void setResourceFolder(const char *str);
|
||||
void setSource(const int src);
|
||||
|
||||
FILE *getFilePointer(const char *resourcename, int& filesize, const bool binary=false);
|
||||
char *getFileBuffer(const char *resourcename, int& filesize, const bool zero_terminate=false);
|
||||
|
||||
const char* getConfigValueString(const char *key);
|
||||
const int getConfigValueInteger(const char *key, const int default_value=0);
|
||||
const float getConfigValueFloat(const char *key, const float default_value=0.0f);
|
||||
const bool getConfigValueBool(const char *key, const bool default_value=false);
|
||||
void setConfigValueString(const char* key, const char* value);
|
||||
void setConfigValueInteger(const char* key, const int value);
|
||||
void setConfigValueFloat(const char* key, const float value);
|
||||
void setConfigValueBool(const char* key, const bool value);
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "jgame.h"
|
||||
#include "jdraw.h"
|
||||
#include "jinput.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "game.h"
|
||||
#include "draw.h"
|
||||
#include "input.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#ifdef MACOS_BUNDLE
|
||||
#include <libgen.h>
|
||||
@@ -49,13 +50,21 @@ int main(int argc, char *argv[])
|
||||
char res_file[255] = "";
|
||||
strcpy(res_file, dirname(argv[0]));
|
||||
strcat(res_file, "/../Resources/data.jf2");
|
||||
file::setResourceFilename(res_file);
|
||||
file_setresourcefilename(res_file);
|
||||
#endif
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
||||
|
||||
#ifdef DEBUG
|
||||
SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
|
||||
#endif
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "JAPI v%s\n", JAPI_VERSION);
|
||||
|
||||
game::windowHasFocus = true;
|
||||
game::init();
|
||||
input::init(draw::getZoom());
|
||||
|
||||
input::init();
|
||||
|
||||
static unsigned int current_ticks = SDL_GetTicks();
|
||||
|
||||
bool should_exit=false;
|
||||
@@ -64,26 +73,49 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
while(SDL_PollEvent(&e))
|
||||
{
|
||||
if (e.type==SDL_QUIT) { should_exit = true; break; }
|
||||
if (e.type==SDL_KEYDOWN)
|
||||
if (e.type==SDL_EVENT_QUIT)
|
||||
{
|
||||
input::updateKey(e.key.keysym.scancode);
|
||||
should_exit = true;
|
||||
break;
|
||||
}
|
||||
if (e.type==SDL_KEYUP)
|
||||
if (e.type==SDL_EVENT_KEY_DOWN)
|
||||
{
|
||||
input::updateKeypressed(e.key.keysym.scancode);
|
||||
input::updateKey(e.key.scancode);
|
||||
}
|
||||
if (e.type==SDL_MOUSEBUTTONUP)
|
||||
if (e.type==SDL_EVENT_KEY_UP)
|
||||
{
|
||||
switch (e.key.scancode) {
|
||||
case SDL_SCANCODE_F1:
|
||||
draw::setZoom(draw::getZoom()-1);
|
||||
break;
|
||||
case SDL_SCANCODE_F2:
|
||||
draw::setZoom(draw::getZoom()+1);
|
||||
break;
|
||||
case SDL_SCANCODE_F3:
|
||||
draw::setFullscreen(!draw::getFullscreen());
|
||||
break;
|
||||
case SDL_SCANCODE_F4:
|
||||
draw::toggleShader();
|
||||
break;
|
||||
default:
|
||||
input::updateKeypressed(e.key.scancode);
|
||||
}
|
||||
}
|
||||
if (e.type==SDL_EVENT_MOUSE_BUTTON_UP)
|
||||
{
|
||||
input::updateClk(e.button.button);
|
||||
}
|
||||
if (e.type==SDL_MOUSEWHEEL)
|
||||
if (e.type==SDL_EVENT_MOUSE_WHEEL)
|
||||
{
|
||||
input::updateWheel(e.wheel.y);
|
||||
}
|
||||
if ( e.type == SDL_WINDOWEVENT ) {
|
||||
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) game::windowHasFocus = true;
|
||||
if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST) game::windowHasFocus = false;
|
||||
if ( e.type == SDL_EVENT_WINDOW_FOCUS_GAINED )
|
||||
{
|
||||
game::windowHasFocus = true;
|
||||
}
|
||||
if ( e.type == SDL_EVENT_WINDOW_FOCUS_LOST )
|
||||
{
|
||||
game::windowHasFocus = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "jdraw.h"
|
||||
#include "jinput.h"
|
||||
#include "jaudio.h"
|
||||
#include "jfile.h"
|
||||
#include "draw.h"
|
||||
#include "input.h"
|
||||
#include "audio.h"
|
||||
#include "file.h"
|
||||
|
||||
#define JAPI_VERSION "0.8"
|
||||
|
||||
namespace game
|
||||
{
|
||||
extern bool windowHasFocus;
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define EXTENSION_INTRODUCER 0x21
|
||||
#define IMAGE_DESCRIPTOR 0x2C
|
||||
@@ -1,20 +1,18 @@
|
||||
#include "jinput.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include "jdraw.h"
|
||||
#include "input.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include "draw.h"
|
||||
|
||||
namespace input
|
||||
{
|
||||
static const uint8_t *keys = nullptr;
|
||||
static const bool *keys = nullptr;
|
||||
static uint8_t keypressed = 0;
|
||||
static uint8_t keydown = 0;
|
||||
static uint8_t btnClicked = 0;
|
||||
static int wheel = 0;
|
||||
static int screen_zoom = 1;
|
||||
|
||||
void init(const int zoom)
|
||||
void init()
|
||||
{
|
||||
keys = SDL_GetKeyboardState(NULL);
|
||||
screen_zoom = zoom;
|
||||
}
|
||||
|
||||
// Determina si la tecla especificada està sent polsada ara mateix
|
||||
@@ -79,23 +77,23 @@ namespace input
|
||||
// Torna la posició X actual del ratolí
|
||||
const int mouseX()
|
||||
{
|
||||
int x;
|
||||
float x;
|
||||
SDL_GetMouseState(&x, NULL);
|
||||
return x/screen_zoom;
|
||||
return (x-draw::getOffsetX())/draw::getScaleX();
|
||||
}
|
||||
|
||||
// Torna la posició Y actual del ratolí
|
||||
const int mouseY()
|
||||
{
|
||||
int y;
|
||||
float y;
|
||||
SDL_GetMouseState(NULL, &y);
|
||||
return y/screen_zoom;
|
||||
return (y-draw::getOffsetY())/draw::getScaleY();
|
||||
}
|
||||
|
||||
// Determina si el botó del ratolí especificat està sent polsada ara mateix
|
||||
const bool mouseBtn(const int btn)
|
||||
{
|
||||
return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(btn));
|
||||
return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MASK(btn));
|
||||
}
|
||||
|
||||
// Determina si el botó especificat ha sigut polsat, pero no tornarà a ser true fins
|
||||
@@ -14,7 +14,7 @@ namespace input
|
||||
}
|
||||
|
||||
/// @brief Inicialitza els sistemes de teclat, ratolí i gamepad
|
||||
void init(const int zoom);
|
||||
void init();
|
||||
|
||||
/// @brief Determina si la tecla especificada està sent polsada ara mateix
|
||||
/// @param key tecla a consultar
|
||||
277
source/japi/shader.cpp
Normal file
277
source/japi/shader.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
#include "shader.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "CoreFoundation/CoreFoundation.h"
|
||||
#include <OpenGL/OpenGL.h>
|
||||
|
||||
#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
|
||||
#include <OpenGL/gl3.h>
|
||||
#else
|
||||
#include <OpenGL/gl.h>
|
||||
#endif //!ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
|
||||
#else
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#include <SDL3/SDL_opengl_glext.h>
|
||||
#endif
|
||||
|
||||
namespace shader
|
||||
{
|
||||
SDL_Window *win = nullptr;
|
||||
SDL_Renderer *renderer = nullptr;
|
||||
GLuint programId = 0;
|
||||
SDL_Texture* backBuffer = nullptr;
|
||||
SDL_FRect window = {0, 0, 640, 480};
|
||||
SDL_FPoint tex_size = {320, 240};
|
||||
float aspect_ratio = 1;
|
||||
bool can_use_opengl = false;
|
||||
bool using_opengl = false;
|
||||
GLuint texture_number;
|
||||
GLuint nose;
|
||||
|
||||
#ifndef __APPLE__
|
||||
|
||||
// I'm avoiding the use of GLEW or some extensions handler, but that
|
||||
// doesn't mean you should...
|
||||
PFNGLCREATESHADERPROC glCreateShader;
|
||||
PFNGLSHADERSOURCEPROC glShaderSource;
|
||||
PFNGLCOMPILESHADERPROC glCompileShader;
|
||||
PFNGLGETSHADERIVPROC glGetShaderiv;
|
||||
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
|
||||
PFNGLDELETESHADERPROC glDeleteShader;
|
||||
PFNGLATTACHSHADERPROC glAttachShader;
|
||||
PFNGLCREATEPROGRAMPROC glCreateProgram;
|
||||
PFNGLDELETEPROGRAMPROC glDeleteProgram;
|
||||
PFNGLLINKPROGRAMPROC glLinkProgram;
|
||||
PFNGLVALIDATEPROGRAMPROC glValidateProgram;
|
||||
PFNGLGETPROGRAMIVPROC glGetProgramiv;
|
||||
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
|
||||
PFNGLUSEPROGRAMPROC glUseProgram;
|
||||
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
|
||||
PFNGLUNIFORM2FPROC glUniform2f;
|
||||
|
||||
bool initGLExtensions() {
|
||||
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
|
||||
glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource");
|
||||
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
|
||||
glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv");
|
||||
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog");
|
||||
glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader");
|
||||
glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
|
||||
glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram");
|
||||
glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram");
|
||||
glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram");
|
||||
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram");
|
||||
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv");
|
||||
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog");
|
||||
glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram");
|
||||
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)SDL_GL_GetProcAddress("glGetUniformLocation");
|
||||
glUniform2f = (PFNGLUNIFORM2FPROC)SDL_GL_GetProcAddress("glUniform2f");
|
||||
|
||||
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
|
||||
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
|
||||
glDeleteProgram && glLinkProgram && glValidateProgram && glGetProgramiv &&
|
||||
glGetProgramInfoLog && glUseProgram && glGetUniformLocation && glUniform2f;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
GLuint compileShader(const char* source, GLuint shaderType) {
|
||||
// Create ID for shader
|
||||
GLuint result = glCreateShader(shaderType);
|
||||
// Add define depending on shader type
|
||||
const char *sources[2] = { shaderType==GL_VERTEX_SHADER?"#define VERTEX\n":"#define FRAGMENT\n", source };
|
||||
// Define shader text
|
||||
glShaderSource(result, 2, sources, NULL);
|
||||
// Compile shader
|
||||
glCompileShader(result);
|
||||
|
||||
//Check vertex shader for errors
|
||||
GLint shaderCompiled = GL_FALSE;
|
||||
glGetShaderiv( result, GL_COMPILE_STATUS, &shaderCompiled );
|
||||
if (shaderCompiled != GL_TRUE)
|
||||
{
|
||||
std::cout << "Error en la compilación: " << result << "!" << std::endl;
|
||||
GLint logLength;
|
||||
glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (logLength > 0)
|
||||
{
|
||||
GLchar *log = (GLchar*)malloc(logLength);
|
||||
glGetShaderInfoLog(result, logLength, &logLength, log);
|
||||
std::cout << "Shader compile log:" << log << std::endl;
|
||||
//std::cout << source << std::endl;
|
||||
free(log);
|
||||
}
|
||||
glDeleteShader(result);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GLuint compileProgram(const char* vertexShaderSource, const char* fragmentShaderSource)
|
||||
{
|
||||
GLuint programId = 0;
|
||||
GLuint vtxShaderId, fragShaderId;
|
||||
|
||||
if (programId != 0) glDeleteProgram(programId);
|
||||
programId = glCreateProgram();
|
||||
|
||||
|
||||
vtxShaderId = compileShader(vertexShaderSource, GL_VERTEX_SHADER);
|
||||
fragShaderId = compileShader(fragmentShaderSource?fragmentShaderSource:vertexShaderSource, GL_FRAGMENT_SHADER);
|
||||
|
||||
if(vtxShaderId && fragShaderId)
|
||||
{
|
||||
// Associate shader with program
|
||||
glAttachShader(programId, vtxShaderId);
|
||||
glAttachShader(programId, fragShaderId);
|
||||
glLinkProgram(programId);
|
||||
glValidateProgram(programId);
|
||||
|
||||
// Check the status of the compile/link
|
||||
GLint logLen;
|
||||
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLen);
|
||||
if (logLen > 0)
|
||||
{
|
||||
char* log = (char*) malloc(logLen * sizeof(char));
|
||||
// Show any errors as appropriate
|
||||
glGetProgramInfoLog(programId, logLen, &logLen, log);
|
||||
std::cout << "Prog Info Log: " << std::endl << log << std::endl;
|
||||
free(log);
|
||||
}
|
||||
}
|
||||
if (vtxShaderId) glDeleteShader(vtxShaderId);
|
||||
if (fragShaderId) glDeleteShader(fragShaderId);
|
||||
return programId;
|
||||
}
|
||||
|
||||
const bool init(SDL_Window* win, SDL_Texture* backBuffer, const char* vertexShader, const char* fragmentShader)
|
||||
{
|
||||
shader::win = win;
|
||||
shader::renderer = SDL_GetRenderer(win);
|
||||
shader::backBuffer = backBuffer;
|
||||
int w, h;
|
||||
SDL_GetWindowSize(win, &w, &h);
|
||||
|
||||
if (w * aspect_ratio > h) {
|
||||
window.y = 0;
|
||||
window.h = h;
|
||||
window.w = h/aspect_ratio;
|
||||
window.x = (w - window.w)/2;
|
||||
} else {
|
||||
window.x = 0;
|
||||
window.w = w;
|
||||
window.h = w*aspect_ratio;
|
||||
window.y = (h - window.h)/2;
|
||||
}
|
||||
|
||||
SDL_GetTextureSize(backBuffer, &tex_size.x, &tex_size.y);
|
||||
printf("tex size: %fx%f\n", tex_size.x, tex_size.y);
|
||||
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
|
||||
texture_number = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, -1);
|
||||
printf("texture number: %i\n", texture_number);
|
||||
int access = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, -1);
|
||||
nose = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER, -1);
|
||||
printf("texture target number: %i\n", nose);
|
||||
|
||||
if (access != SDL_TEXTUREACCESS_TARGET)
|
||||
{
|
||||
std::cout << "ERROR FATAL: La textura per al render ha de tindre SDL_TEXTUREACCESS_TARGET definit." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char * renderer_name = SDL_GetRendererName(renderer);
|
||||
printf("rendererInfo.name: %s\n", renderer_name);
|
||||
|
||||
if(!strncmp(renderer_name, "opengl", 6)) {
|
||||
#ifndef __APPLE__
|
||||
static bool gl_extensions_initialized = false;
|
||||
if (!gl_extensions_initialized) {
|
||||
if (!initGLExtensions()) {
|
||||
std::cout << "WARNING: No s'han pogut inicialitzar les extensions d'OpenGL!" << std::endl;
|
||||
can_use_opengl = false;
|
||||
return false;
|
||||
}
|
||||
gl_extensions_initialized = true;
|
||||
}
|
||||
#endif
|
||||
// Compilar el shader y dejarlo listo para usar.
|
||||
if (!vertexShader) {
|
||||
can_use_opengl = false;
|
||||
return false;
|
||||
}
|
||||
programId = compileProgram(vertexShader, fragmentShader);
|
||||
} else {
|
||||
std::cout << "WARNING: El driver del renderer no es OpenGL." << std::endl;
|
||||
can_use_opengl = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
can_use_opengl = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char pixels[512*240*4];
|
||||
|
||||
void enable() { if (can_use_opengl) using_opengl = true; }
|
||||
void disable() { using_opengl = false; }
|
||||
|
||||
void setAspectRatio(const float ratio)
|
||||
{
|
||||
aspect_ratio = ratio;
|
||||
}
|
||||
|
||||
void render()
|
||||
{
|
||||
SDL_FlushRenderer(renderer);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_FlushRenderer(renderer);
|
||||
|
||||
if (using_opengl)
|
||||
{
|
||||
GLint oldProgramId;
|
||||
if (programId != 0)
|
||||
{
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
|
||||
glUseProgram(programId);
|
||||
}
|
||||
|
||||
//GLint loc = glGetUniformLocation(programId, "TextureSize");
|
||||
//glUniform2f(loc, 320, 256);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, 1);
|
||||
//glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels);
|
||||
//if (glGetError()) { printf("GLGETERROR!\n"); exit(1);}
|
||||
//GLint param;
|
||||
//glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, ¶m);
|
||||
//printf("tex width: %i\n", param);
|
||||
glViewport(window.x, window.y, window.w, window.h);
|
||||
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0.0f, 0.0f);
|
||||
glVertex2f(-1.0f, -1.0f);
|
||||
glTexCoord2f(tex_size.x, 0.0f);
|
||||
glVertex2f(1.0f, -1.0f);
|
||||
glTexCoord2f(0.0f, tex_size.y);
|
||||
glVertex2f(-1.0f, 1.0f);
|
||||
glTexCoord2f(tex_size.x, tex_size.y);
|
||||
glVertex2f(1.0f, 1.0f);
|
||||
glEnd();
|
||||
|
||||
SDL_GL_SwapWindow(win);
|
||||
|
||||
if (programId != 0) glUseProgram(oldProgramId);
|
||||
|
||||
} else {
|
||||
SDL_RenderTexture(renderer, backBuffer, NULL, &window);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
int glerror = glGetError();
|
||||
if (glerror) { printf("GLERROR: %i\n", glerror); exit(1); }
|
||||
}
|
||||
}
|
||||
48
source/japi/shader.h
Normal file
48
source/japi/shader.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
// TIPS:
|
||||
// =======================================================================
|
||||
// Abans de crear el renderer, cridar a la següent funció:
|
||||
//
|
||||
// SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||
//
|
||||
// Aixó li diu que volem un renderer que use especificament opengl. A més,
|
||||
// al crear el renderer li tenim que dir que el volem que use acceeració
|
||||
// per hardware, i que soporte render a textura. Per exemple:
|
||||
//
|
||||
// SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED |
|
||||
// SDL_RENDERER_TARGETTEXTURE);
|
||||
//
|
||||
// Per altra part, al crear la textura tenim que definir que puga ser target
|
||||
// de renderitzat (SDL_TEXTUREACCESS_TARGET), per exemple:
|
||||
//
|
||||
// SDL_Texture *tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||
// SDL_TEXTUREACCESS_TARGET, 320, 240);
|
||||
//
|
||||
// Els shaders li'ls passem com una cadena, som nosaltres els que s'encarreguem
|
||||
// de carregarlos de disc, amb fopen, ifstream, jfile o el que vullgues.
|
||||
// Si els tens en un std::string, passa-li-la com "cadena.c_str()".
|
||||
//
|
||||
// Poden ser els dos el mateix arxiu, com fa libRetro, jo desde dins ja fique
|
||||
// els defines necessaris. Si es el mateix arxiu, pots no ficar el quart paràmetre.
|
||||
//
|
||||
// Els shaders de libRetro no funcionen directament, hi ha que fer algunes modificacions.
|
||||
//
|
||||
// El pintat final de la teua escena l'has de fer com si "backBuffer" fora la pantalla.
|
||||
//
|
||||
// Ah! una cosa mes: al compilar, en Linux afegir "-lGL", en Windows afegir "-lopengl32".
|
||||
// En Mac ni idea
|
||||
|
||||
namespace shader
|
||||
{
|
||||
const bool init(SDL_Window* win, SDL_Texture* backBuffer,
|
||||
const char* vertexShader, const char* fragmentShader=nullptr);
|
||||
|
||||
void setAspectRatio(const float ratio);
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
void render();
|
||||
}
|
||||
5584
source/japi/stb_vorbis.h
Normal file
5584
source/japi/stb_vorbis.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,168 +0,0 @@
|
||||
#include "jaudio.h"
|
||||
#include "jfile.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace audio
|
||||
{
|
||||
// Açò son estructures de mentires, per a usar estructures de SDL_Mixer de forma opaca.
|
||||
// Al final es un punter, així que és irrelevant el tipus del punter,
|
||||
// però si amague que son estructures de SDL_Mixer, no fa falta ficar el include a SDL_mixer fora de este arxiu
|
||||
struct sound
|
||||
{
|
||||
}; // Dummy structs
|
||||
|
||||
static char *buffer = nullptr;
|
||||
static Mix_Music *music = nullptr;
|
||||
static std::string music_filename = "";
|
||||
|
||||
// Inicialitza el sistema de só
|
||||
void init()
|
||||
{
|
||||
// Al final he ficat la configuració automàtica i au. Si en el futur necesitem canviar-ho pos se canvia
|
||||
if (Mix_Init(MIX_INIT_OGG)==0) {
|
||||
printf("Failed Mix_Init()\n");
|
||||
}
|
||||
if (Mix_OpenAudio(48000, AUDIO_S16, 2, 1024)!=0) {
|
||||
printf("Failed Mix_OpenAudio()\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Tanca el sistema de só (no shit, sherlock)
|
||||
void quit()
|
||||
{
|
||||
Mix_CloseAudio();
|
||||
}
|
||||
|
||||
// Carrega un arxiu de música en format OGG
|
||||
const bool loadMusic(const std::string filename)
|
||||
{
|
||||
if (music != nullptr) {
|
||||
Mix_FreeMusic(music);
|
||||
free(buffer);
|
||||
}
|
||||
int filesize=0;
|
||||
buffer = file::getFileBuffer(filename, &filesize);
|
||||
music = Mix_LoadMUS_RW(SDL_RWFromMem(buffer, filesize), 1);
|
||||
|
||||
if (music==nullptr) return false;
|
||||
|
||||
music_filename = filename;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Comença a reproduïr la música en questió
|
||||
void playMusic(const int loop)
|
||||
{
|
||||
if (Mix_PlayMusic(music, loop) == -1) {
|
||||
printf("Failed Mix_PlayMusic()\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Pausa la música que està sonant ara
|
||||
void pauseMusic()
|
||||
{
|
||||
Mix_PauseMusic();
|
||||
}
|
||||
|
||||
// Continua la música pausada
|
||||
void resumeMusic()
|
||||
{
|
||||
Mix_ResumeMusic();
|
||||
}
|
||||
|
||||
// Para la música que estava sonant
|
||||
void stopMusic()
|
||||
{
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
|
||||
// Para la música que estava sonant fent un fade
|
||||
void fadeoutMusic()
|
||||
{
|
||||
Mix_FadeOutMusic(500);
|
||||
}
|
||||
|
||||
// Obté el estat actual de la música
|
||||
const music_state getMusicState()
|
||||
{
|
||||
if (Mix_PausedMusic())
|
||||
{
|
||||
return MUSIC_PAUSED;
|
||||
}
|
||||
else if (Mix_PlayingMusic())
|
||||
{
|
||||
return MUSIC_PLAYING;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MUSIC_STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
// Obté el nom de l'arxiu de música actual
|
||||
std::string whichMusic()
|
||||
{
|
||||
return music_filename;
|
||||
}
|
||||
|
||||
// Carrega un só des d'un arxiu WAV
|
||||
const sound *loadSound(const std::string filename)
|
||||
{
|
||||
return (sound *)Mix_LoadWAV(filename.c_str());
|
||||
}
|
||||
|
||||
// Comença a reproduïr el só especificat
|
||||
const int playSound(sound *snd, const int loop)
|
||||
{
|
||||
return Mix_PlayChannel(-1, (Mix_Chunk *)snd, loop);
|
||||
}
|
||||
|
||||
// Allibera un só
|
||||
void deleteSound(sound *snd)
|
||||
{
|
||||
Mix_FreeChunk((Mix_Chunk *)snd);
|
||||
}
|
||||
|
||||
// Pausa un canal en el que s'estava reproduïnt un só
|
||||
void pauseChannel(const int channel)
|
||||
{
|
||||
Mix_Pause(channel);
|
||||
}
|
||||
|
||||
// Continua un canal pausat
|
||||
void resumeChannel(const int channel)
|
||||
{
|
||||
Mix_Resume(channel);
|
||||
}
|
||||
|
||||
// Para un canal que estava reproduïnt un só
|
||||
void stopChannel(const int channel)
|
||||
{
|
||||
Mix_HaltChannel(channel);
|
||||
}
|
||||
|
||||
// Obté l'estat d'un canal
|
||||
const channel_state getChannelState(const int channel)
|
||||
{
|
||||
if (Mix_Paused(channel))
|
||||
{
|
||||
return CHANNEL_PAUSED;
|
||||
}
|
||||
else if (Mix_Playing(channel))
|
||||
{
|
||||
return CHANNEL_PLAYING;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CHANNEL_FREE;
|
||||
}
|
||||
}
|
||||
|
||||
// Estableix el volum general
|
||||
const int setVolume(int volume)
|
||||
{
|
||||
return Mix_Volume(-1, volume);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace audio
|
||||
{
|
||||
// Enumeració per a representar el estat de un canal de sò
|
||||
enum channel_state
|
||||
{
|
||||
CHANNEL_INVALID,
|
||||
CHANNEL_FREE,
|
||||
CHANNEL_PLAYING,
|
||||
CHANNEL_PAUSED
|
||||
};
|
||||
|
||||
// Enumeració per a representar el estat de la música
|
||||
enum music_state
|
||||
{
|
||||
MUSIC_INVALID,
|
||||
MUSIC_PLAYING,
|
||||
MUSIC_PAUSED,
|
||||
MUSIC_STOPPED
|
||||
};
|
||||
|
||||
// Estructures per a gestionar música i só
|
||||
struct sound;
|
||||
|
||||
/// @brief Inicialitza el sistema de só
|
||||
void init();
|
||||
|
||||
/// @brief Tanca el sistema de só
|
||||
void quit();
|
||||
|
||||
/// @brief Carrega un arxiu de música en format OGG
|
||||
/// @param filename nom de l'arxiu
|
||||
/// @return true si tot be, false si ha fallat
|
||||
const bool loadMusic(const std::string filename);
|
||||
|
||||
/// @brief Comença a reproduïr la música en questió
|
||||
/// @param loop quants bucles farà (-1=infinit, 0=no repeteix, 1=repeteix 1 vegada...)
|
||||
void playMusic(const int loop = -1);
|
||||
|
||||
/// @brief Pausa la música que està sonant ara
|
||||
void pauseMusic();
|
||||
|
||||
/// @brief Continua la música pausada
|
||||
void resumeMusic();
|
||||
|
||||
/// @brief Para la música que estava sonant
|
||||
void stopMusic();
|
||||
|
||||
/// @brief Para la música que estava sonant fent un fade
|
||||
void fadeoutMusic();
|
||||
|
||||
/// @brief Obté el estat actual de la música
|
||||
/// @return estat actual de la música (MUSIC_INVALID, MUSIC_PLAYING, MUSIC_PAUSED o MUSIC_STOPPED)
|
||||
const music_state getMusicState();
|
||||
|
||||
/// @brief Obté el nom de l'arxiu de música actual
|
||||
/// @return el nom de l'arxiu
|
||||
std::string whichMusic();
|
||||
|
||||
/// @brief Carrega un só des d'un arxiu WAV
|
||||
/// @param filename nom de l'arxiu
|
||||
/// @return un punter al só
|
||||
const sound *loadSound(const std::string filename);
|
||||
|
||||
/// @brief Comença a reproduïr el só especificat
|
||||
/// @param snd punter al só a reproduïr
|
||||
/// @param loop si es fa bucle (-1=infinit, 0=no repeteix, 1=repeteix 1 vegada...)
|
||||
/// @return número del canal en que està sonant el só
|
||||
const int playSound(sound *snd, const int loop = 0);
|
||||
|
||||
/// @brief Pausa un canal en el que s'estava reproduïnt un só
|
||||
/// @param channel número del canal a pausar
|
||||
void pauseChannel(const int channel);
|
||||
|
||||
/// @brief Continua un canal pausat
|
||||
/// @param channel número del canal pausat
|
||||
void resumeChannel(const int channel);
|
||||
|
||||
/// @brief Para un canal que estava reproduïnt un só
|
||||
/// @param channel número del canal a parar
|
||||
void stopChannel(const int channel);
|
||||
|
||||
/// @brief Obté l'estat d'un canal
|
||||
/// @param channel canal del que es vol obtindre l'estat
|
||||
/// @return estat del canal (CHANNEL_INVALID, CHANNEL_FREE, CHANNEL_PLAYING o CHANNEL_PAUSED)
|
||||
const channel_state getChannelState(const int channel);
|
||||
|
||||
/// @brief Allibera un só
|
||||
/// @param snd punter al só
|
||||
void deleteSound(sound *snd);
|
||||
|
||||
/// @brief Estableix el volum general
|
||||
/// @param volume valor a establir com a volum (128 màxim)
|
||||
/// @return el volum anterior
|
||||
const int setVolume(int volume);
|
||||
}
|
||||
500
source/jfile.cpp
500
source/jfile.cpp
@@ -1,500 +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 <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_FILENAME "data.jf2"
|
||||
#define DEFAULT_FOLDER "data/"
|
||||
#define CONFIG_FILENAME "config.txt"
|
||||
|
||||
/* FORMAT DEL ARXIU .JF2
|
||||
|
||||
4 bytes header capçalera "PK2" (caracter 0 de final de cadena al final) (en realitat passe de ella, pero be...)
|
||||
4 bytes num_files nombre d'arxius inclosos
|
||||
4 bytes toc_offset en quina posició de l'arxiu està la taula de continguts
|
||||
|
||||
... Ara venen tots els arxius inclosos, uno darrere de l'altre.
|
||||
... Quan acaben ve la taula de continguts. La taula te tantes entrades com arxius inclosos. Cada entrada te el següent format:
|
||||
|
||||
(per cert, toc_offset apunta ací)
|
||||
4 bytes offset en quina posició del arxiu de recursos comença este arxiu
|
||||
4 bytes size tamany d'este arxiu
|
||||
1 byte path_size tamany de la cadena amb la ruta de l'arxiu
|
||||
path_size bytes path ruta de l'arxiu original. La usem per a trobar el arxiu que ens demanen.
|
||||
|
||||
EXEMPLE SIMPLE:
|
||||
|
||||
- Imaginem que volem incloure un arxiu "data/hola.txt" amb el contingut "HOLA", i un arxiu "data/adios.txt" amb el contingut "ADIOS":
|
||||
|
||||
OFFSET CONTINGUT TAMANY DESCRIPCIÓ
|
||||
0 "PK2"+0 4 La capçalera
|
||||
4 2 4 nombre d'arxius
|
||||
8 21 4 offset a la taula de continguts
|
||||
|
||||
--- COMENCEN ELS ARXIUS INCLOSOS ---
|
||||
|
||||
12 HOLA 4 el contingut del primer arxiu
|
||||
16 ADIOS 5 el contingut del segon arxiu
|
||||
|
||||
--- COMENÇA LA TAULA DE CONTINGUTS ---
|
||||
|
||||
21 12 4 offset al primer arxiu
|
||||
25 4 4 tamany del primer axiu
|
||||
29 13 1 tamany de la ruta al primer arxiu
|
||||
30 "data/hola.txt" 13 la ruta al primer arxiu
|
||||
43 16 4 offset al primer arxiu
|
||||
47 4 4 tamany del primer axiu
|
||||
51 13 1 tamany de la ruta al primer arxiu
|
||||
52 "data/adios.txt" 14 la ruta al primer arxiu
|
||||
|
||||
|
||||
- Es un exemple raro, perque ocupa mes la ruta al arxiu que l'arxiu en si, pero espere que la idea quede clara!
|
||||
|
||||
Al principi se carrega la tabla de continguts en memòria, així el acces als arxius es ràpid.
|
||||
|
||||
|
||||
|
||||
Y com funciona tot açò? pos per defecte va a intentar llegir tots els arxius de "data.jf2". Si no troba e l'arxiu, automaticament passa a
|
||||
buscar-ho tot en la carpeta "data" en arxius independents. En principi, si no se tenen requeriments diferents, no fa falta configurar res.
|
||||
|
||||
|
||||
|
||||
Respecte al tema de l'arxiu de configuració, està montat de forma que se pot escriure i llegir valors asociats a una clau sense calfar-se el cap.
|
||||
En l'arxiu de configuració els valor se guarden de la forma:
|
||||
|
||||
CLAU=VALOR
|
||||
|
||||
Cada un en una linea. Si llegim i no existeix, torna cadena buida. Si escrivim i ja exisita, se reemplaça.
|
||||
Tot son valors de cadena. Si en realitat son números, tindràs que encarregar-te tu de la caonversió de cadena a numero o al reves.
|
||||
|
||||
*/
|
||||
|
||||
namespace file
|
||||
{
|
||||
// Estructures
|
||||
// ===============================================================================================================================
|
||||
|
||||
// Estructura que representa un arxiu en la tabla de continguts del arxiu de recursos
|
||||
struct file_t
|
||||
{
|
||||
std::string path; // Ruta relativa de l'arxiu
|
||||
uint32_t size; // Tamany de l'arxiu
|
||||
uint32_t offset; // Offset a l'arxiu dins de l'arxiu de recursos
|
||||
};
|
||||
|
||||
std::vector<file_t> toc; // vector que conté la taula de continguts de l'arxiu de recursos
|
||||
|
||||
/* El std::map me fa coses rares, vaig a usar un good old std::vector amb una estructura key,value propia i au, que sempre funciona */
|
||||
struct keyvalue_t
|
||||
{
|
||||
std::string key, value;
|
||||
};
|
||||
|
||||
// Variables
|
||||
// ===============================================================================================================================
|
||||
static std::string resource_filename = DEFAULT_FILENAME; // Nom de l'arxiu de recursos
|
||||
static std::string resource_folder = DEFAULT_FOLDER; // Nom de la carpeta de recursos
|
||||
static int file_source = SOURCE_FILE; // D'on anem a pillar els recursos, arxiu o carpeta
|
||||
static std::string config_folder; // Nom de la carpeta on guardar la configuració
|
||||
std::vector<keyvalue_t> config; // Vector amb els valors guardats a l'arxiu de configuració
|
||||
|
||||
// Funcions
|
||||
// ===============================================================================================================================
|
||||
|
||||
// Estableix el nom de l'arxiu on estàn guardats els recursos (es "data.jf2" per defecte)
|
||||
void setResourceFilename(const std::string str)
|
||||
{
|
||||
resource_filename = str;
|
||||
}
|
||||
|
||||
// Estableix el nom de la carpeta on estàn guardats els recursos (es "data" per defecte)
|
||||
void setResourceFolder(const std::string str)
|
||||
{
|
||||
resource_folder = str;
|
||||
}
|
||||
|
||||
// Estableix d'on s'han de obtenir els recursos (arxius individuals dins d'una carpeta o arxiu de recursos)
|
||||
void setSource(const int src)
|
||||
{
|
||||
file_source = src % 2; // mod 2 de forma que sempre es un valor vàlid, 0 (arxiu) o 1 (carpeta)
|
||||
|
||||
// Si volem que busque en carpeta i encara no haviem especificat una carpeta, usem la per defecte
|
||||
if (/*src == SOURCE_FOLDER && */resource_folder == "")
|
||||
{
|
||||
setResourceFolder(DEFAULT_FOLDER);
|
||||
}
|
||||
}
|
||||
|
||||
// Carreguem la taula de continguts de l'arxiu de recursos
|
||||
bool getTableOfContents()
|
||||
{
|
||||
// Si encara no haviem especificat un arxiu de recursos, usem el arxiu de recursos per defecte
|
||||
if (resource_filename == "")
|
||||
{
|
||||
setResourceFilename(DEFAULT_FILENAME);
|
||||
}
|
||||
|
||||
// Si l'arxiu de recursos existeix...
|
||||
std::ifstream fi(resource_filename, std::ios::binary);
|
||||
if (fi.is_open())
|
||||
{
|
||||
// Llegim la capçalera (controlar que siga correcta?)
|
||||
char header[4];
|
||||
fi.read(header, 4);
|
||||
|
||||
// LLegim el nombre d'arxius i la posició de la taula de continguts
|
||||
uint32_t num_files, toc_offset;
|
||||
fi.read((char *)&num_files, 4);
|
||||
fi.read((char *)&toc_offset, 4);
|
||||
|
||||
// Anem a la taula de continguts
|
||||
fi.seekg(toc_offset);
|
||||
|
||||
// Per a cada arxiu inclos en l'arxiu de recursos...
|
||||
for (unsigned int i = 0; i < num_files; ++i)
|
||||
{
|
||||
// Llegim en quina posició està i quant ocupa
|
||||
uint32_t file_offset, file_size;
|
||||
fi.read((char *)&file_offset, 4);
|
||||
fi.read((char *)&file_size, 4);
|
||||
|
||||
// Llegim la seua ruta
|
||||
uint8_t path_size;
|
||||
fi.read((char *)&path_size, 1);
|
||||
char file_name[path_size + 1];
|
||||
fi.read(file_name, path_size);
|
||||
file_name[path_size] = 0;
|
||||
std::string filename = file_name;
|
||||
|
||||
// Y afegim les dades a la taula de continguts en memòria
|
||||
toc.push_back({filename, file_size, file_offset});
|
||||
}
|
||||
|
||||
// Tanquem la paradeta i tornem true
|
||||
fi.close();
|
||||
return true;
|
||||
}
|
||||
else // Si no s'ha pogut llegir el arxiu de recursos, tornem false
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Obté un "std::ifstream" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
|
||||
std::ifstream getFileStream(const std::string resourcename, int *filesize, const bool binary)
|
||||
{
|
||||
// Si tenim configurat agafar els recursos de arxiu, pero encara no tenim la taula de continguts carregada...
|
||||
if (file_source == SOURCE_FILE and toc.size() == 0)
|
||||
{
|
||||
// Si fallem al intentar carregar la taula de continguts de l'arxiu de recursos, canviem a pillar els recursos de carpeta
|
||||
if (not getTableOfContents())
|
||||
{
|
||||
setSource(SOURCE_FOLDER);
|
||||
}
|
||||
}
|
||||
|
||||
std::ifstream f;
|
||||
|
||||
// Si estem pillant els recursos de un arxiu de recursos...
|
||||
if (file_source == SOURCE_FILE)
|
||||
{
|
||||
// Busquem el recurs en la taula de continguts usant la ruta
|
||||
bool found = false;
|
||||
uint32_t count = 0;
|
||||
while (!found && count < toc.size())
|
||||
{
|
||||
found = (resource_folder + resourcename == toc[count].path);
|
||||
if (!found)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Si no trobem el recurs, petem el mame
|
||||
if (!found)
|
||||
{
|
||||
printf("ERROR FATAL: No s'ha trobat el recurs '%s' a l'arxiu de recursos '%s'\n", resourcename.c_str(), resource_filename.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Agafem el tamany del recurs de la taula de continguts
|
||||
if (filesize) *filesize = toc[count].size;
|
||||
|
||||
// obrim l'arxiu de recursos
|
||||
f.open(resource_filename.c_str(), binary ? std::ios::binary : std::ios::in);
|
||||
if (!f.is_open()) // En el raruno cas de que a este altures pete al obrir el arxiu de recursos, petem el mame
|
||||
{
|
||||
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu de recursos '%s'\n", resource_filename.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Anem a la posició on està el recurs que volem. Amb açò "f" ja està preparat per a ser tornar.
|
||||
// Ojo, realment estic tornant un FILE* al arxiu de recursos, pero ja apuntant al moment en que comença el recurs que volem.
|
||||
// Ho dic perque si fem fseek(f, 0, SEEK_SET) tornarà al principi de l'arxiu de recursos, no del recurs. Tindre-ho en compte.
|
||||
f.seekg(toc[count].offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Si estem pillant els recursos de carpeta, simplement obrim el arxiu en questió i tornem el FILE* associat.
|
||||
f.open((resource_folder + resourcename), binary ? std::ios::binary : std::ios::in);
|
||||
if (f.rdstate() & std::ios_base::failbit)
|
||||
{
|
||||
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu '%s/%s'\n", resource_folder.c_str(), resourcename.c_str());
|
||||
exit(1);
|
||||
}
|
||||
f.seekg(0, std::ios_base::end);
|
||||
if (filesize) *filesize = f.tellg();
|
||||
f.seekg(0, std::ios_base::beg);
|
||||
}
|
||||
|
||||
// Tornar el punter FILE* al arxiu. OJO! Tenim que tancar-lo quan acabem amb ell
|
||||
return f;
|
||||
}
|
||||
|
||||
// Obté un "FILE*" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
|
||||
FILE *getFilePointer(const std::string resourcename, int *filesize, const bool binary)
|
||||
{
|
||||
// Si tenim configurat agafar els recursos de arxiu, pero encara no tenim la taula de continguts carregada...
|
||||
if (file_source == SOURCE_FILE and toc.size() == 0)
|
||||
{
|
||||
// Si fallem al intentar carregar la taula de continguts de l'arxiu de recursos, canviem a pillar els recursos de carpeta
|
||||
if (not getTableOfContents())
|
||||
{
|
||||
setSource(SOURCE_FOLDER);
|
||||
}
|
||||
}
|
||||
|
||||
FILE *f;
|
||||
|
||||
// Si estem pillant els recursos de un arxiu de recursos...
|
||||
if (file_source == SOURCE_FILE)
|
||||
{
|
||||
// Busquem el recurs en la taula de continguts usant la ruta
|
||||
bool found = false;
|
||||
uint32_t count = 0;
|
||||
while (!found && count < toc.size())
|
||||
{
|
||||
found = (resource_folder + resourcename == toc[count].path);
|
||||
if (!found)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Si no trobem el recurs, petem el mame
|
||||
if (!found)
|
||||
{
|
||||
printf("ERROR FATAL: No s'ha trobat el recurs '%s' a l'arxiu de recursos '%s'\n", resourcename.c_str(), resource_filename.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Agafem el tamany del recurs de la taula de continguts
|
||||
if (filesize) *filesize = toc[count].size;
|
||||
|
||||
// obrim l'arxiu de recursos
|
||||
f = fopen(resource_filename.c_str(), binary ? "rb" : "r");
|
||||
if (!f) // En el raruno cas de que a este altures pete al obrir el arxiu de recursos, petem el mame
|
||||
{
|
||||
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu de recursos '%s'\n", resource_filename.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Anem a la posició on està el recurs que volem. Amb açò "f" ja està preparat per a ser tornar.
|
||||
// Ojo, realment estic tornant un FILE* al arxiu de recursos, pero ja apuntant al moment en que comença el recurs que volem.
|
||||
// Ho dic perque si fem fseek(f, 0, SEEK_SET) tornarà al principi de l'arxiu de recursos, no del recurs. Tindre-ho en compte.
|
||||
fseek(f, toc[count].offset, SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Si estem pillant els recursos de carpeta, simplement obrim el arxiu en questió i tornem el FILE* associat.
|
||||
f = fopen((resource_folder + resourcename).c_str(), binary ? "rb" : "r");
|
||||
if(!f)
|
||||
{
|
||||
printf("ERROR FATAL: No s'ha pogut obrir l'arxiu '%s/%s'\n", resource_folder.c_str(), resourcename.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
if (filesize) *filesize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
// Tornar el punter FILE* al arxiu. OJO! Tenim que tancar-lo quan acabem amb ell
|
||||
return f;
|
||||
}
|
||||
|
||||
// Obté un buffer de memòria en format "char*" del arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos)
|
||||
char *getFileBuffer(const std::string resourcename, int *filesize)
|
||||
{
|
||||
int size;
|
||||
// Usem la funció anterior per a obtinde un FILE*, independentment de on pillem els recursos
|
||||
FILE *f = getFilePointer(resourcename, &size, true);
|
||||
|
||||
// Reservem memòria per al buffer
|
||||
char *buffer = (char *)malloc(size);
|
||||
|
||||
// llegim el contingut del arxiu i el fiquem en el buffer
|
||||
fread(buffer, size, 1, f);
|
||||
|
||||
// Tanquem l'arxiu
|
||||
fclose(f);
|
||||
|
||||
if (filesize) *filesize = size;
|
||||
|
||||
// Tornem el buffer. OJO! recordar alliberar-lo amb free(buffer) quan acabem amb ell.
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Estableix el nom de la carpeta on es guardarà la configuració
|
||||
// Adaptat del codi que va escriure JailDesigner en el JailDoctor's Dilemma
|
||||
// Vull revisar-la tranquilament algun dia
|
||||
void setConfigFolder(const std::string foldername)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
config_folder = std::string(getenv("APPDATA")) + "/" + foldername;
|
||||
#elif __APPLE__
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *homedir = pw->pw_dir;
|
||||
config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
|
||||
#elif __linux__
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *homedir = pw->pw_dir;
|
||||
config_folder = std::string(homedir) + "/." + foldername;
|
||||
#endif
|
||||
|
||||
struct stat st = {0};
|
||||
if (stat(config_folder.c_str(), &st) == -1)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int ret = mkdir(config_folder.c_str());
|
||||
#else
|
||||
int ret = mkdir(config_folder.c_str(), S_IRWXU);
|
||||
#endif
|
||||
|
||||
if (ret == -1)
|
||||
{
|
||||
printf("ERROR CREATING CONFIG FOLDER.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Obté el nom de la carpeta on es guardarà la configuració
|
||||
const std::string getConfigFolder()
|
||||
{
|
||||
return config_folder + "/";
|
||||
}
|
||||
|
||||
// Carrega tots els valors guardats a l'arxiu de recursos
|
||||
void loadConfigValues()
|
||||
{
|
||||
// Buidem la taula de claus-valors en memòria
|
||||
config.clear();
|
||||
|
||||
// Obrim l'arxiu de configuració
|
||||
std::string config_file = config_folder + "/config.txt";
|
||||
FILE *f = fopen(config_file.c_str(), "r");
|
||||
if (f)
|
||||
{
|
||||
// Agafem linea a linea
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), f))
|
||||
{
|
||||
// Separem la clau del valor
|
||||
char *value = strchr(line, '=');
|
||||
if (value)
|
||||
{
|
||||
*value = '\0';
|
||||
value++;
|
||||
value[strlen(value) - 1] = '\0';
|
||||
|
||||
// i els afegim a la taula de claus-valors en memòria
|
||||
config.push_back({line, value});
|
||||
}
|
||||
}
|
||||
|
||||
// tanquem la paradeta
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
// Guardem tots els valors en la taula de claus-valors a l'arxiu de configuració
|
||||
void saveConfigValues()
|
||||
{
|
||||
// Obrim l'arxiu de configuració
|
||||
std::string config_file = config_folder + "/config.txt";
|
||||
FILE *f = fopen(config_file.c_str(), "w");
|
||||
if (f)
|
||||
{
|
||||
// Guardem cada parella clau/valor de la taula en memòria en una linea en format "clau=valor\n" en l'arxiu de configuració
|
||||
for (auto pair : config)
|
||||
{
|
||||
fprintf(f, "%s=%s\n", pair.key.c_str(), pair.value.c_str());
|
||||
}
|
||||
|
||||
// tanquem la paradeta
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
// Obté un valor de l'arxiu de configuració per a la clau donada (o cadena buida si no existeix)
|
||||
const std::string getConfigValue(const std::string key)
|
||||
{
|
||||
// Si la taula de claus-valors esta buida, la carreguem de l'arxiu de configuració
|
||||
if (config.empty())
|
||||
{
|
||||
loadConfigValues();
|
||||
}
|
||||
|
||||
// busquem la clau en la taula
|
||||
for (auto pair : config)
|
||||
{
|
||||
if (pair.key == std::string(key))
|
||||
{
|
||||
// Si la trobem, tornem el seu valor
|
||||
return pair.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Si no la trobem, tornem cadena buida
|
||||
return "";
|
||||
}
|
||||
|
||||
// Estableix un valor en l'arxiu de configuració per a la clau donada
|
||||
void setConfigValue(const std::string key, const std::string value)
|
||||
{
|
||||
// Si la taula de claus-valors esta buida, la carreguem de l'arxiu de configuració
|
||||
if (config.empty())
|
||||
{
|
||||
loadConfigValues();
|
||||
}
|
||||
|
||||
// busquem la clau en la taula
|
||||
for (auto &pair : config)
|
||||
{
|
||||
if (pair.key == std::string(key))
|
||||
{
|
||||
// Si la trobem, actualitzem el seu valor i guardem els canvis a l'arxiu de configuració
|
||||
pair.value = value;
|
||||
saveConfigValues();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Si NO la trobem, afegim la nova clau i el seu valor, i guardem els canvis a l'arxiu de configuració
|
||||
config.push_back({key, value});
|
||||
saveConfigValues();
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#define SOURCE_FILE 0
|
||||
#define SOURCE_FOLDER 1
|
||||
|
||||
// Unitat per a la gestió de l'acces a arxius
|
||||
namespace file
|
||||
{
|
||||
// Funcions d'acces als recursos (gràfics, musiques...)
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
/// @brief Estableix el nom de l'arxiu on estàn guardats els recursos (es "data.jf2" per defecte)
|
||||
/// @param str nom de l'arxiu de recursos
|
||||
void setResourceFilename(const std::string str);
|
||||
|
||||
/// @brief Estableix el nom de la carpeta on estàn guardats els recursos (es "data" per defecte)
|
||||
/// @param str nom de la carpeta de recursos
|
||||
void setResourceFolder(const std::string str);
|
||||
|
||||
/// @brief Estableix d'on s'han de obtenir els recursos (arxius individuals dins d'una carpeta o arxiu de recursos)
|
||||
/// @param src SOURCE_FILE o SOURCE_FOLDER, si es vol que se pillen recursos de arxiu o de carpeta
|
||||
void setSource(const int src);
|
||||
|
||||
/// @brief Obté un "std::ifstream" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos). Recordar tancar-lo al acabar amb ell.
|
||||
/// @param resourcename el nom de l'arxiu que volem obrir
|
||||
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
|
||||
/// @param binary si volem obrir el arxiu en format binary
|
||||
/// @return un std::ifstream al arxiu
|
||||
std::ifstream getFileStream(const std::string resourcename, int *filesize = nullptr, const bool binary = false);
|
||||
|
||||
/// @brief Obté un "FILE*" al arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos). Recordar tancar-lo al acabar amb ell.
|
||||
/// @param resourcename el nom de l'arxiu que volem obrir
|
||||
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
|
||||
/// @param binary si volem obrir el arxiu en format binary
|
||||
/// @return un punter FILE* al arxiu
|
||||
FILE *getFilePointer(const std::string resourcename, int *filesize = nullptr, const bool binary = false);
|
||||
|
||||
/// @brief Obté un buffer de memòria en format "char*" del arxiu que se li demana, independentment de la font (arxius individual en carpeta, o arxiu de recursos).
|
||||
/// @brief Recordar alliberar la memoria del buffer amb free(buffer) al acabar amb ell.
|
||||
/// @param resourcename el nom de l'arxiu del que volem obrindre el buffer
|
||||
/// @param filesize paràmetre de retorn. Ací es torna el tamany de l'arxiu
|
||||
/// @return un array de "filesize" bytes amb el contingut del arxiu
|
||||
char *getFileBuffer(const std::string resourcename, int *filesize = nullptr);
|
||||
|
||||
// Funcions d'access a la configuració (clau = valor)
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
/// @brief Estableix el nom de la carpeta on es guardarà la configuració
|
||||
/// @param foldername nom de la carpeta
|
||||
void setConfigFolder(const std::string foldername);
|
||||
|
||||
/// @brief Obté el nom de la carpeta on es guardarà la configuració
|
||||
/// @return nom de la carpeta
|
||||
const std::string getConfigFolder();
|
||||
|
||||
/// @brief Obté un valor de l'arxiu de configuració per a la clau donada (o cadena buida si no existeix)
|
||||
/// @param key clau de la que obtindre el valor
|
||||
/// @return el valor de la clau especificada
|
||||
const std::string getConfigValue(const std::string key);
|
||||
|
||||
/// @brief Estableix un valor en l'arxiu de configuració per a la clau donada
|
||||
/// @param key clau a la que establir un valor
|
||||
/// @param value valor a establir per a la clau
|
||||
void setConfigValue(const std::string key, const std::string value);
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
#include "jgame.h"
|
||||
#include "gamestates.h"
|
||||
#include "aux_font.h"
|
||||
#include "japi/game.h"
|
||||
#include "gamestates/gamestates.h"
|
||||
#include "aux/font.h"
|
||||
|
||||
void game::init()
|
||||
{
|
||||
draw::init("Arounders", 320, 200, 3);
|
||||
file::setConfigFolder("arounders");
|
||||
draw::init("Arounders", 320, 200, 3, false, 3.0f/4.0f);
|
||||
draw::setShader("gbc.glsl");
|
||||
draw::setTrans(0);
|
||||
|
||||
input::init(3);
|
||||
audio::init();
|
||||
audio::init(48000, SDL_AUDIO_S16, 2);
|
||||
draw::hideCursor();
|
||||
|
||||
font::init();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user