- Initial commit
This commit is contained in:
478
source/gif.c
Normal file
478
source/gif.c
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#define EXTENSION_INTRODUCER 0x21
|
||||||
|
#define IMAGE_DESCRIPTOR 0x2C
|
||||||
|
#define TRAILER 0x3B
|
||||||
|
|
||||||
|
#define GRAPHIC_CONTROL 0xF9
|
||||||
|
#define APPLICATION_EXTENSION 0xFF
|
||||||
|
#define COMMENT_EXTENSION 0xFE
|
||||||
|
#define PLAINTEXT_EXTENSION 0x01
|
||||||
|
|
||||||
|
#define READ(dst, size) memcpy(dst, buffer, size); buffer += size
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned short width;
|
||||||
|
unsigned short height;
|
||||||
|
unsigned char fields;
|
||||||
|
unsigned char background_color_index;
|
||||||
|
unsigned char pixel_aspect_ratio;
|
||||||
|
}
|
||||||
|
screen_descriptor_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char r;
|
||||||
|
unsigned char g;
|
||||||
|
unsigned char b;
|
||||||
|
}
|
||||||
|
rgb;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned short image_left_position;
|
||||||
|
unsigned short image_top_position;
|
||||||
|
unsigned short image_width;
|
||||||
|
unsigned short image_height;
|
||||||
|
unsigned char fields;
|
||||||
|
}
|
||||||
|
image_descriptor_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char byte;
|
||||||
|
int prev;
|
||||||
|
int len;
|
||||||
|
}
|
||||||
|
dictionary_entry_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char extension_code;
|
||||||
|
unsigned char block_size;
|
||||||
|
}
|
||||||
|
extension_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char fields;
|
||||||
|
unsigned short delay_time;
|
||||||
|
unsigned char transparent_color_index;
|
||||||
|
}
|
||||||
|
graphic_control_extension_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char application_id[ 8 ];
|
||||||
|
unsigned char version[ 3 ];
|
||||||
|
}
|
||||||
|
application_extension_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned short left;
|
||||||
|
unsigned short top;
|
||||||
|
unsigned short width;
|
||||||
|
unsigned short height;
|
||||||
|
unsigned char cell_width;
|
||||||
|
unsigned char cell_height;
|
||||||
|
unsigned char foreground_color;
|
||||||
|
unsigned char background_color;
|
||||||
|
}
|
||||||
|
plaintext_extension_t;
|
||||||
|
|
||||||
|
//static unsigned short width = 0;
|
||||||
|
//static unsigned short height = 0;
|
||||||
|
//static unsigned char* uncompressed_data = NULL;
|
||||||
|
|
||||||
|
void uncompress( int code_length,
|
||||||
|
const unsigned char *input,
|
||||||
|
int input_length,
|
||||||
|
unsigned char *out )
|
||||||
|
{
|
||||||
|
//int maxbits;
|
||||||
|
int i, bit;
|
||||||
|
int code, prev = -1;
|
||||||
|
dictionary_entry_t *dictionary;
|
||||||
|
int dictionary_ind;
|
||||||
|
unsigned int mask = 0x01;
|
||||||
|
int reset_code_length;
|
||||||
|
int clear_code; // This varies depending on code_length
|
||||||
|
int stop_code; // one more than clear code
|
||||||
|
int match_len;
|
||||||
|
|
||||||
|
clear_code = 1 << ( code_length );
|
||||||
|
stop_code = clear_code + 1;
|
||||||
|
// To handle clear codes
|
||||||
|
reset_code_length = code_length;
|
||||||
|
|
||||||
|
// Create a dictionary large enough to hold "code_length" entries.
|
||||||
|
// Once the dictionary overflows, code_length increases
|
||||||
|
dictionary = ( dictionary_entry_t * )
|
||||||
|
malloc( sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
|
||||||
|
|
||||||
|
// Initialize the first 2^code_len entries of the dictionary with their
|
||||||
|
// indices. The rest of the entries will be built up dynamically.
|
||||||
|
|
||||||
|
// Technically, it shouldn't be necessary to initialize the
|
||||||
|
// dictionary. The spec says that the encoder "should output a
|
||||||
|
// clear code as the first code in the image data stream". It doesn't
|
||||||
|
// say must, though...
|
||||||
|
for ( dictionary_ind = 0;
|
||||||
|
dictionary_ind < ( 1 << code_length );
|
||||||
|
dictionary_ind++ )
|
||||||
|
{
|
||||||
|
dictionary[ dictionary_ind ].byte = dictionary_ind;
|
||||||
|
// XXX this only works because prev is a 32-bit int (> 12 bits)
|
||||||
|
dictionary[ dictionary_ind ].prev = -1;
|
||||||
|
dictionary[ dictionary_ind ].len = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2^code_len + 1 is the special "end" code; don't give it an entry here
|
||||||
|
dictionary_ind++;
|
||||||
|
dictionary_ind++;
|
||||||
|
|
||||||
|
// TODO verify that the very last byte is clear_code + 1
|
||||||
|
while ( input_length )
|
||||||
|
{
|
||||||
|
code = 0x0;
|
||||||
|
// Always read one more bit than the code length
|
||||||
|
for ( i = 0; i < ( code_length + 1 ); i++ )
|
||||||
|
{
|
||||||
|
// This is different than in the file read example; that
|
||||||
|
// was a call to "next_bit"
|
||||||
|
bit = ( *input & mask ) ? 1 : 0;
|
||||||
|
mask <<= 1;
|
||||||
|
|
||||||
|
if ( mask == 0x100 )
|
||||||
|
{
|
||||||
|
mask = 0x01;
|
||||||
|
input++;
|
||||||
|
input_length--;
|
||||||
|
}
|
||||||
|
|
||||||
|
code = code | ( bit << i );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( code == clear_code )
|
||||||
|
{
|
||||||
|
code_length = reset_code_length;
|
||||||
|
dictionary = ( dictionary_entry_t * ) realloc( dictionary,
|
||||||
|
sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
|
||||||
|
|
||||||
|
for ( dictionary_ind = 0;
|
||||||
|
dictionary_ind < ( 1 << code_length );
|
||||||
|
dictionary_ind++ )
|
||||||
|
{
|
||||||
|
dictionary[ dictionary_ind ].byte = dictionary_ind;
|
||||||
|
// XXX this only works because prev is a 32-bit int (> 12 bits)
|
||||||
|
dictionary[ dictionary_ind ].prev = -1;
|
||||||
|
dictionary[ dictionary_ind ].len = 1;
|
||||||
|
}
|
||||||
|
dictionary_ind++;
|
||||||
|
dictionary_ind++;
|
||||||
|
prev = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if ( code == stop_code )
|
||||||
|
{
|
||||||
|
/*if ( input_length > 1 )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "Malformed GIF (early stop code)\n" );
|
||||||
|
exit( 0 );
|
||||||
|
}*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the dictionary with this character plus the _entry_
|
||||||
|
// (character or string) that came before it
|
||||||
|
if ( ( prev > -1 ) && ( code_length < 12 ) )
|
||||||
|
{
|
||||||
|
if ( code > dictionary_ind )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "code = %.02x, but dictionary_ind = %.02x\n",
|
||||||
|
code, dictionary_ind );
|
||||||
|
exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special handling for KwKwK
|
||||||
|
if ( code == dictionary_ind )
|
||||||
|
{
|
||||||
|
int ptr = prev;
|
||||||
|
|
||||||
|
while ( dictionary[ ptr ].prev != -1 )
|
||||||
|
{
|
||||||
|
ptr = dictionary[ ptr ].prev;
|
||||||
|
}
|
||||||
|
dictionary[ dictionary_ind ].byte = dictionary[ ptr ].byte;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int ptr = code;
|
||||||
|
while ( dictionary[ ptr ].prev != -1 )
|
||||||
|
{
|
||||||
|
ptr = dictionary[ ptr ].prev;
|
||||||
|
}
|
||||||
|
dictionary[ dictionary_ind ].byte = dictionary[ ptr ].byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary[ dictionary_ind ].prev = prev;
|
||||||
|
|
||||||
|
dictionary[ dictionary_ind ].len = dictionary[ prev ].len + 1;
|
||||||
|
|
||||||
|
dictionary_ind++;
|
||||||
|
|
||||||
|
// GIF89a mandates that this stops at 12 bits
|
||||||
|
if ( ( dictionary_ind == ( 1 << ( code_length + 1 ) ) ) &&
|
||||||
|
( code_length < 11 ) )
|
||||||
|
{
|
||||||
|
code_length++;
|
||||||
|
|
||||||
|
dictionary = ( dictionary_entry_t * ) realloc( dictionary,
|
||||||
|
sizeof( dictionary_entry_t ) * ( 1 << ( code_length + 1 ) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = code;
|
||||||
|
|
||||||
|
// Now copy the dictionary entry backwards into "out"
|
||||||
|
match_len = dictionary[ code ].len;
|
||||||
|
while ( code != -1 )
|
||||||
|
{
|
||||||
|
out[ dictionary[ code ].len - 1 ] = dictionary[ code ].byte;
|
||||||
|
if ( dictionary[ code ].prev == code )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "Internal error; self-reference." );
|
||||||
|
exit( 0 );
|
||||||
|
}
|
||||||
|
code = dictionary[ code ].prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
out += match_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_sub_blocks( unsigned char* buffer, unsigned char **data )
|
||||||
|
{
|
||||||
|
int data_length;
|
||||||
|
int index;
|
||||||
|
unsigned char block_size;
|
||||||
|
|
||||||
|
// Everything following are data sub-blocks, until a 0-sized block is
|
||||||
|
// encountered.
|
||||||
|
data_length = 0;
|
||||||
|
*data = NULL;
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
while ( 1 )
|
||||||
|
{
|
||||||
|
READ(&block_size, 1);
|
||||||
|
|
||||||
|
if ( block_size == 0 ) // end of sub-blocks
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_length += block_size;
|
||||||
|
*data = (unsigned char*)realloc( *data, data_length );
|
||||||
|
|
||||||
|
// TODO this could be split across block size boundaries
|
||||||
|
READ(*data + index, block_size);
|
||||||
|
|
||||||
|
index += block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* process_image_descriptor( unsigned char* buffer,
|
||||||
|
rgb *gct,
|
||||||
|
int gct_size,
|
||||||
|
int resolution_bits )
|
||||||
|
{
|
||||||
|
image_descriptor_t image_descriptor;
|
||||||
|
int compressed_data_length;
|
||||||
|
unsigned char *compressed_data = NULL;
|
||||||
|
unsigned char lzw_code_size;
|
||||||
|
int uncompressed_data_length = 0;
|
||||||
|
unsigned char *uncompressed_data = NULL;
|
||||||
|
|
||||||
|
// TODO there could actually be lots of these
|
||||||
|
READ(&image_descriptor, 9);
|
||||||
|
|
||||||
|
// TODO if LCT = true, read the LCT
|
||||||
|
|
||||||
|
READ(&lzw_code_size, 1);
|
||||||
|
|
||||||
|
compressed_data_length = read_sub_blocks( buffer, &compressed_data );
|
||||||
|
|
||||||
|
// width = image_descriptor.image_width;
|
||||||
|
// height = image_descriptor.image_height;
|
||||||
|
uncompressed_data_length = image_descriptor.image_width *
|
||||||
|
image_descriptor.image_height;
|
||||||
|
uncompressed_data = (unsigned char*)malloc( uncompressed_data_length );
|
||||||
|
|
||||||
|
uncompress( lzw_code_size, compressed_data, compressed_data_length,
|
||||||
|
uncompressed_data );
|
||||||
|
|
||||||
|
if ( compressed_data ) free( compressed_data );
|
||||||
|
|
||||||
|
//if ( uncompressed_data )
|
||||||
|
// free( uncompressed_data );
|
||||||
|
|
||||||
|
return uncompressed_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param gif_file the file descriptor of a file containing a
|
||||||
|
* GIF-encoded file. This should point to the first byte in
|
||||||
|
* the file when invoked.
|
||||||
|
*/
|
||||||
|
#define rb (*(buffer++))
|
||||||
|
|
||||||
|
uint32_t* LoadPalette(unsigned char *buffer) {
|
||||||
|
unsigned char header[7];
|
||||||
|
screen_descriptor_t screen_descriptor;
|
||||||
|
//int color_resolution_bits;
|
||||||
|
|
||||||
|
int global_color_table_size = 0; // number of entries in global_color_table
|
||||||
|
uint32_t *global_color_table = NULL;
|
||||||
|
|
||||||
|
READ(header, 6);
|
||||||
|
READ(&screen_descriptor, 7);
|
||||||
|
|
||||||
|
//color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
|
||||||
|
global_color_table = (uint32_t *)calloc(1, 1024);
|
||||||
|
|
||||||
|
if (screen_descriptor.fields & 0x80) {
|
||||||
|
global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
|
||||||
|
|
||||||
|
//global_color_table = (rgb *)malloc(3 * global_color_table_size);
|
||||||
|
//READ(global_color_table, 3 * global_color_table_size);
|
||||||
|
for (int i=0; i<global_color_table_size;++i) {
|
||||||
|
global_color_table[i] = (buffer[0]<<16) + (buffer[1]<<8) + buffer[2];
|
||||||
|
buffer+=3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return global_color_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char* process_gif_stream(unsigned char *buffer, unsigned short* w, unsigned short* h)
|
||||||
|
{
|
||||||
|
unsigned char header[ 7 ];
|
||||||
|
screen_descriptor_t screen_descriptor;
|
||||||
|
int color_resolution_bits;
|
||||||
|
|
||||||
|
int global_color_table_size =0; // number of entries in global_color_table
|
||||||
|
rgb *global_color_table = NULL;
|
||||||
|
|
||||||
|
unsigned char block_type = 0x0;
|
||||||
|
|
||||||
|
// A GIF file starts with a Header (section 17)
|
||||||
|
READ(header, 6);
|
||||||
|
header[ 6 ] = 0x0;
|
||||||
|
|
||||||
|
// XXX there's another format, GIF87a, that you may still find
|
||||||
|
// floating around.
|
||||||
|
/*if ( strcmp( "GIF89a", (char*)header ) )
|
||||||
|
{
|
||||||
|
fprintf( stderr,
|
||||||
|
"Invalid GIF file (header is '%s', should be 'GIF89a')\n",
|
||||||
|
header );
|
||||||
|
return NULL;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Followed by a logical screen descriptor
|
||||||
|
// Note that this works because GIFs specify little-endian order; on a
|
||||||
|
// big-endian machine, the height & width would need to be reversed.
|
||||||
|
|
||||||
|
// Can't use sizeof here since GCC does byte alignment;
|
||||||
|
// sizeof( screen_descriptor_t ) = 8!
|
||||||
|
READ(&screen_descriptor, 7);
|
||||||
|
*w = screen_descriptor.width;
|
||||||
|
*h = screen_descriptor.height;
|
||||||
|
|
||||||
|
color_resolution_bits = ( ( screen_descriptor.fields & 0x70 ) >> 4 ) + 1;
|
||||||
|
|
||||||
|
if ( screen_descriptor.fields & 0x80 )
|
||||||
|
{
|
||||||
|
//int i;
|
||||||
|
// If bit 7 is set, the next block is a global color table; read it
|
||||||
|
global_color_table_size = 1 <<
|
||||||
|
( ( ( screen_descriptor.fields & 0x07 ) + 1 ) );
|
||||||
|
|
||||||
|
global_color_table = ( rgb * ) malloc( 3 * global_color_table_size );
|
||||||
|
|
||||||
|
// XXX this could conceivably return a short count...
|
||||||
|
READ(global_color_table, 3 * global_color_table_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( block_type != TRAILER )
|
||||||
|
{
|
||||||
|
READ(&block_type, 1);
|
||||||
|
|
||||||
|
unsigned char size;
|
||||||
|
switch ( block_type )
|
||||||
|
{
|
||||||
|
case IMAGE_DESCRIPTOR:
|
||||||
|
return process_image_descriptor(buffer,
|
||||||
|
global_color_table,
|
||||||
|
global_color_table_size,
|
||||||
|
color_resolution_bits);
|
||||||
|
break;
|
||||||
|
case EXTENSION_INTRODUCER:
|
||||||
|
buffer++;
|
||||||
|
size = *(buffer++);
|
||||||
|
buffer += size;
|
||||||
|
do {
|
||||||
|
size = *(buffer++);
|
||||||
|
buffer += size;
|
||||||
|
} while (size != 0);
|
||||||
|
|
||||||
|
/*if ( !process_extension( buffer ) )
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}*/
|
||||||
|
break;
|
||||||
|
case TRAILER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf( stderr, "Bailing on unrecognized block type %.02x\n",
|
||||||
|
block_type );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned char* LoadGif(unsigned char *buffer, unsigned short* w, unsigned short* h) {
|
||||||
|
return process_gif_stream(buffer, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*int main( int argc, char *argv[] )
|
||||||
|
{
|
||||||
|
FILE* gif_file;
|
||||||
|
|
||||||
|
if ( argc < 2 )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "Usage: %s <path-to-gif-file>\n", argv[ 0 ] );
|
||||||
|
exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
gif_file = fopen( argv[ 1 ], "rb" );
|
||||||
|
|
||||||
|
if ( gif_file == NULL )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "Unable to open file '%s'", argv[ 1 ] );
|
||||||
|
perror( ": " );
|
||||||
|
}
|
||||||
|
|
||||||
|
process_gif_stream( gif_file );
|
||||||
|
|
||||||
|
fclose( gif_file );
|
||||||
|
}*/
|
||||||
142
source/jaudio.cpp
Normal file
142
source/jaudio.cpp
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#include "jaudio.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
|
||||||
|
struct music
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
Mix_OpenAudio(48000, AUDIO_S16, 2, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tanca el sistema de só (no shit, sherlock)
|
||||||
|
void quit()
|
||||||
|
{
|
||||||
|
Mix_CloseAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carrega un arxiu de música en format OGG
|
||||||
|
const music *loadMusic(const std::string filename)
|
||||||
|
{
|
||||||
|
return (music *)Mix_LoadMUS(filename.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comença a reproduïr la música en questió
|
||||||
|
void playMusic(music *mus, const int loop)
|
||||||
|
{
|
||||||
|
Mix_PlayMusic((Mix_Music *)mus, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allibera una música
|
||||||
|
void deleteMusic(music *mus)
|
||||||
|
{
|
||||||
|
Mix_FreeMusic((Mix_Music *)mus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
97
source/jaudio.h
Normal file
97
source/jaudio.h
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#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;
|
||||||
|
struct music;
|
||||||
|
|
||||||
|
/// @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 punter a la música
|
||||||
|
const music *loadMusic(const std::string filename);
|
||||||
|
|
||||||
|
/// @brief Comença a reproduïr la música en questió
|
||||||
|
/// @param mus punter a la música
|
||||||
|
/// @param loop quants bucles farà (-1=infinit, 0=no repeteix, 1=repeteix 1 vegada...)
|
||||||
|
void playMusic(music *mus, 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 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 Allibera una música
|
||||||
|
/// @param mus punter a la música a alliberar
|
||||||
|
void deleteMusic(music *mus);
|
||||||
|
|
||||||
|
/// @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);
|
||||||
|
}
|
||||||
277
source/jdraw.cpp
Normal file
277
source/jdraw.cpp
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
#include "jdraw.h"
|
||||||
|
#include "SDL2/SDL.h"
|
||||||
|
#include "gif.c"
|
||||||
|
#include "jfile.h"
|
||||||
|
|
||||||
|
namespace draw
|
||||||
|
{
|
||||||
|
// La idea de esta unitat es usar "superficies", que no son mes que arrays de bytes, per a anar pintant.
|
||||||
|
// El resultat final s'ha de pintar en algun moment a la superficie "screen" (o siga, especificar nullptr en setDestination)
|
||||||
|
// Aleshores, en "render" el contingut de screen se volca a la textura SDL que crearem,
|
||||||
|
// i eixa textura se pintarà a pantalla com se sol fer amb SDL. Ho anirem veient en el codi.
|
||||||
|
|
||||||
|
SDL_Window *sdl_window = nullptr; // La finestra de SDL
|
||||||
|
SDL_Renderer *sdl_renderer = nullptr; // El renderer de SDL
|
||||||
|
SDL_Texture *sdl_texture = nullptr; // La textura de SDL a la que pintarem la nostra superficie "screen" i que despres volcarem a pantalla
|
||||||
|
|
||||||
|
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 de oritge
|
||||||
|
|
||||||
|
uint32_t palette[256]; // La paleta de colors
|
||||||
|
uint8_t transparent = 0; // El color transparent
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// [TODO] Incloure gestió de pantalla completa
|
||||||
|
|
||||||
|
// Inicialització de les estructures de SDL
|
||||||
|
sdl_window = SDL_CreateWindow(titol.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width * zoom, height * zoom, SDL_WINDOW_SHOWN);
|
||||||
|
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Creem la superficie "screen" i la establim com a superficie destinació
|
||||||
|
screen = createSurface(width, height);
|
||||||
|
destination = screen;
|
||||||
|
transparent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalització del sistema
|
||||||
|
void quit()
|
||||||
|
{
|
||||||
|
// Si la superficie "screen" existia, alliberem la seua memòria
|
||||||
|
if (screen != nullptr)
|
||||||
|
{
|
||||||
|
freeSurface(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destruim tot el relacionat amb SDL
|
||||||
|
SDL_DestroyTexture(sdl_texture);
|
||||||
|
SDL_DestroyRenderer(sdl_renderer);
|
||||||
|
SDL_DestroyWindow(sdl_window);
|
||||||
|
|
||||||
|
// Fiquem tots els punters a nullptr, per si de cas no estem eixint del programa
|
||||||
|
// i anem a tornar a inicialitzar el sistema
|
||||||
|
sdl_window = nullptr;
|
||||||
|
sdl_renderer = nullptr;
|
||||||
|
sdl_texture = nullptr;
|
||||||
|
screen = destination = source = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea una superficie i torna un punter a ella
|
||||||
|
surface *createSurface(const uint16_t w, const uint16_t h)
|
||||||
|
{
|
||||||
|
// Primer reservem memòria per a la estructura "surface"
|
||||||
|
surface *surf = (surface *)malloc(sizeof(surface));
|
||||||
|
|
||||||
|
// Després reservem memòria per als pixels
|
||||||
|
surf->pixels = (uint8_t *)malloc(w * h);
|
||||||
|
|
||||||
|
// I apuntem el ample i alt de la superficie
|
||||||
|
surf->w = w;
|
||||||
|
surf->h = h;
|
||||||
|
|
||||||
|
// ...i tornem la superficie creada, clar
|
||||||
|
return surf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
|
||||||
|
surface *loadSurface(const std::string &filename)
|
||||||
|
{
|
||||||
|
// Agafem un buffer de bytes de l'arxiu especificat
|
||||||
|
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
|
||||||
|
int size;
|
||||||
|
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
|
||||||
|
|
||||||
|
// Si ens ha tornat nullptr, es que no l'ha trobat, tornem nosaltres també nullptr ja que no s'ha pogut crear la superficie
|
||||||
|
if (buffer == nullptr)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Primer reservem memòria per a la estructura "surface"
|
||||||
|
surface *surf = (surface *)malloc(sizeof(surface));
|
||||||
|
|
||||||
|
// Després li passem el buffer de bytes a la funció de carregar un GIF.
|
||||||
|
// El resultat es un array de bytes, els pixels en sí. Ja havem reservat
|
||||||
|
// la memòria necessaria en "LoadGif", així que no tenim que fer-ho ara,
|
||||||
|
// però, ojo, sí que tindrem que alliberar-la.
|
||||||
|
surf->pixels = LoadGif(buffer, &surf->w, &surf->h);
|
||||||
|
|
||||||
|
// Com ja no ens fa falta, alliberem la memòria del buffer del arxiu
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
// I finalment tornem la superficie
|
||||||
|
return surf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allibera la memòria d'una superficie, els seus pixels inclosos
|
||||||
|
void freeSurface(surface *surf)
|
||||||
|
{
|
||||||
|
// Si la superficie existeix...
|
||||||
|
if (surf != nullptr)
|
||||||
|
{
|
||||||
|
// Si el array de pixels existeix, l'alliberem
|
||||||
|
if (surf->pixels != nullptr)
|
||||||
|
{
|
||||||
|
free(surf->pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... alliberem la superficie
|
||||||
|
free(surf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estableix una superficie com a superficie que rebrà les funcions de pintat (especificar nullptr per a pintar a pantalla)
|
||||||
|
void setDestination(surface *surf)
|
||||||
|
{
|
||||||
|
// Si han especificat nullptr, fiquem "screen" com a destinació
|
||||||
|
destination = surf == nullptr ? screen : surf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estableix una superficie com a superficie de la que s'agafaràn els gràfics per a pintar
|
||||||
|
void setSource(surface *surf)
|
||||||
|
{
|
||||||
|
// Si han especificat nullptr, fiquem "screen" com a font
|
||||||
|
source = surf == nullptr ? screen : surf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estableix la paleta del sistema carregant-la d'un GIF
|
||||||
|
void loadPalette(const std::string &filename)
|
||||||
|
{
|
||||||
|
// Agafem un buffer de bytes de l'arxiu especificat
|
||||||
|
// getFileBuffer() simplement ens torna el arxiu sencer dins de un array de char
|
||||||
|
int size;
|
||||||
|
uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
|
||||||
|
|
||||||
|
// Li passem el array del arxiu a LoadPalette. Ell ens torna un array de uint32_t amb la paleta
|
||||||
|
// Van a ser 256 entrades de 32 bits, cada entrada es un color, amb el format 0xAARRGGBB
|
||||||
|
uint32_t *pal = LoadPalette(buffer);
|
||||||
|
|
||||||
|
// Copiem eixe array al nostre array de la paleta de sistema. Ara ja tenim la paleta carregada.
|
||||||
|
memcpy(palette, pal, 1024); // 32 bits per entrada == 4 bytes x 256 entrades == 1024
|
||||||
|
|
||||||
|
// Alliberem el array que ens habia tornat LoadPalette()
|
||||||
|
free(pal);
|
||||||
|
|
||||||
|
// I també el buffer del arxiu
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esborra la superficie "destination" amb el color especificat
|
||||||
|
void cls(const uint8_t color)
|
||||||
|
{
|
||||||
|
// El tamany es width x height bytes
|
||||||
|
const int size = destination->w * destination->h;
|
||||||
|
|
||||||
|
// Omplim la memòria dels pixels de la superficie de destinació amb "color"
|
||||||
|
memset(destination->pixels, color, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Estableix el color especificat com a transparent
|
||||||
|
void setTrans(const uint8_t color)
|
||||||
|
{
|
||||||
|
transparent = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funció interna per a pintar un pixel d'una superficie sense eixir-se'n de la memòria i petar el mame
|
||||||
|
void pset(surface *surface, const int x, const int y, const uint8_t color)
|
||||||
|
{
|
||||||
|
// Si la coordenada està dins del rang que abarca la superficie,
|
||||||
|
// escriure el byte "color" on toca en el array de pixels de la superficie
|
||||||
|
if (color != transparent && x >= 0 && y >= 0 && x < surface->w && y < surface->h)
|
||||||
|
{
|
||||||
|
surface->pixels[x + y * surface->w] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funció interna per a llegir un pixel d'una superficie eixir-se'n de la memòria i petar el mame
|
||||||
|
const uint8_t pget(surface *surface, const int x, const int y)
|
||||||
|
{
|
||||||
|
// Si la coordenada està dins del rang que abarca la superficie,
|
||||||
|
// tornar el byte que toca del array de pixels de la superficie
|
||||||
|
if (x >= 0 && y >= 0 && x < surface->w && y < surface->h)
|
||||||
|
{
|
||||||
|
return surface->pixels[x + y * surface->w];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no, algo tenim que tornar... pos "0"
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = DRAW_FLIP_NONE)
|
||||||
|
{
|
||||||
|
// Si no hi ha superficie d'oritge especificada, no fem res, o petarà el mame
|
||||||
|
if (source == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// En principi, el quadrat de l'oritge començarà en (sx,sy) i avançarem 1 pixel en positiu tant en x com en y
|
||||||
|
int sdx = 1, sdy = 1, ssx = sx, ssy = sy;
|
||||||
|
|
||||||
|
// Però si s'ha especificat que fem flip en horitzontal...
|
||||||
|
if (flip & DRAW_FLIP_HORIZONTAL)
|
||||||
|
{
|
||||||
|
sdx = -1; // Avançarem 1 pixel en negatiu
|
||||||
|
ssx = sx + w - 1; // I començarem al final, o siga, sumarem a sx el ample
|
||||||
|
}
|
||||||
|
|
||||||
|
// De igual forma per al flip en vertical, per a la y
|
||||||
|
if (flip & DRAW_FLIP_VERTICAL)
|
||||||
|
{
|
||||||
|
sdy = -1;
|
||||||
|
ssy = sy + h - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// guardem la coordenada d'oritge en x per a restablir-la a cada linea
|
||||||
|
int csx = ssx;
|
||||||
|
|
||||||
|
// Anem linea per linea. Les variables dels dos bucles for controlen les coordenades en la destinació, que sempre van avant.
|
||||||
|
for (int y = dy; y < dy + h; ++y)
|
||||||
|
{
|
||||||
|
ssx = csx; // fiquem la coordenada de l'oritge al principi
|
||||||
|
|
||||||
|
// en cada linea, anem pixel a pixel
|
||||||
|
for (int x = dx; x < dx + w; ++x)
|
||||||
|
{
|
||||||
|
pset(destination, x, y, pget(source, ssx, ssy)); // Agafem pixel de l'oritge i el fiquem en la destinació
|
||||||
|
ssx += sdx; // avancem (o retrocedim) la coordenada x de l'oritge
|
||||||
|
}
|
||||||
|
ssy += sdy; // avancem (o retrocedim) la coordenada y de l'oritge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresca la pantalla
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
Uint32 *sdl_pixels; // Punter al array de pixels que enstornarà SDL_LockTexture
|
||||||
|
int sdl_pitch; // Ací estarà guardat el pitch de la textura, com es de 32 bits, no m'afecta
|
||||||
|
const int size = screen->w * screen->h; // tamany de la superficie
|
||||||
|
|
||||||
|
// Bloquejem la textura SDL i agafem els seus pixels (son enters de 32 bits amb format 0xAARRGGBB)
|
||||||
|
SDL_LockTexture(sdl_texture, NULL, (void **)&sdl_pixels, &sdl_pitch);
|
||||||
|
|
||||||
|
// Cada pixel de la superficie "screen" es un enter de 8 bits que representa un index en la paleta de colors
|
||||||
|
// Per tant, per a pintar en la textura SDL, pillem el color de la paleta que correspon al index en "screen"
|
||||||
|
// i el enviem a la textura SDL
|
||||||
|
for (uint32_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
sdl_pixels[i] = palette[screen->pixels[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desbloquejem la textura
|
||||||
|
SDL_UnlockTexture(sdl_texture);
|
||||||
|
|
||||||
|
// Pintem la textura a pantalla
|
||||||
|
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
||||||
|
|
||||||
|
// I ho presentem
|
||||||
|
SDL_RenderPresent(sdl_renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
78
source/jdraw.h
Normal file
78
source/jdraw.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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
|
||||||
|
{
|
||||||
|
// Estructura per a mantindre una superficie de pintat, la "pantalla virtual" de tota la vida
|
||||||
|
struct surface
|
||||||
|
{
|
||||||
|
uint16_t w; // Ample de la superficie
|
||||||
|
uint16_t h; // Alt de la superficie
|
||||||
|
uint8_t *pixels; // pixels de la superficie
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla.
|
||||||
|
/// @brief La finestra serà width*zoom x height*zoom de gran.
|
||||||
|
/// @param titol es el text que apareixerà en la finestra
|
||||||
|
/// @param width es el ample de la finestra "virtual"
|
||||||
|
/// @param height es el alt de la finestra "virtual"
|
||||||
|
/// @param zoom es com de grans son els pixels.
|
||||||
|
void init(const std::string &titol, const uint16_t width, const uint16_t height, const int zoom);
|
||||||
|
|
||||||
|
/// @brief Finalització del sistema (tancar coses de SDL, superficies fixes, etc...)
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
/// @brief Crea una superficie i torna un punter a ella
|
||||||
|
/// @param w ample de la superficie
|
||||||
|
/// @param h alt de la superficie
|
||||||
|
/// @return un punter a una nova superficie
|
||||||
|
surface *createSurface(const uint16_t w, const uint16_t h);
|
||||||
|
|
||||||
|
/// @brief Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
|
||||||
|
/// @param filename nom de l'arxiu GIF d'on carregar la superficie
|
||||||
|
/// @return un punter a una nova superficie
|
||||||
|
surface *loadSurface(const std::string &filename);
|
||||||
|
|
||||||
|
/// @brief Allibera la memòria d'una superficie, els seus pixels inclosos
|
||||||
|
/// @param surf punter a la superficie a alliberar
|
||||||
|
void freeSurface(surface *surf);
|
||||||
|
|
||||||
|
/// @brief Estableix una superficie com a superficie que rebrà les funcions de pintat (especificar nullptr per a pintar a pantalla)
|
||||||
|
/// @param surf punter a la superficie a establir com a destinació
|
||||||
|
void setDestination(surface *surf);
|
||||||
|
|
||||||
|
/// @brief Estableix una superficie com a superficie de la que s'agafaràn els gràfics per a pintar
|
||||||
|
/// @param surf punter a la superficie a establir com a oritge
|
||||||
|
void setSource(surface *surf);
|
||||||
|
|
||||||
|
/// @brief Estableix la paleta del sistema carregant-la d'un GIF
|
||||||
|
/// @param filename nom de l'arxiu GIF d'on carregar la paleta
|
||||||
|
void loadPalette(const std::string &filename);
|
||||||
|
|
||||||
|
/// @brief Esborra la superficie "destination" amb el color especificat
|
||||||
|
/// @param color color a usar per a borrar la superficie de destinació
|
||||||
|
void cls(const uint8_t color);
|
||||||
|
|
||||||
|
/// @brief Estableix el color especificat com a transparent
|
||||||
|
/// @param color color a usar com a transparent
|
||||||
|
void setTrans(const uint8_t color);
|
||||||
|
|
||||||
|
/// @brief Pinta un troç de la superficie "source" en la superficie "destination".
|
||||||
|
/// @param dx coordenada x de la destinació
|
||||||
|
/// @param dy coordenada y de la destinació
|
||||||
|
/// @param w ample del quadrat a pintar
|
||||||
|
/// @param h alt del quadrat a pintar
|
||||||
|
/// @param sx coordenada x de l'oritge
|
||||||
|
/// @param sy coordenada y de l'oritge
|
||||||
|
/// @param flip si s'ha de fer flip en hortizontal o vertical (o ambdos)
|
||||||
|
void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int flip = DRAW_FLIP_NONE);
|
||||||
|
|
||||||
|
/// @brief Refresca la pantalla
|
||||||
|
void render();
|
||||||
|
}
|
||||||
422
source/jfile.cpp
Normal file
422
source/jfile.cpp
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
#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 = ""; // Nom de l'arxiu de recursos
|
||||||
|
static std::string resource_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 (int i = 0; i < num_files; ++i)
|
||||||
|
{
|
||||||
|
// Llegim en quina posició està i quant copua
|
||||||
|
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 "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 = (resourcename == toc[count].path);
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no trobem el recurs, petem el mame
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
// [TODO] Donar mes informació de quin recurs no havem trobat
|
||||||
|
perror("El recurs no s'ha trobat en l'arxiu de recursos");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agafem el tamany del recurs de la taula de continguts
|
||||||
|
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
|
||||||
|
{
|
||||||
|
// [TODO] Donar mes informació de quin recurs no havem trobat
|
||||||
|
perror("No s'ha pogut obrir l'arxiu de recursos");
|
||||||
|
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");
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Usem la funció anterior per a obtinde un FILE*, independentment de on pillem els recursos
|
||||||
|
FILE *f = getFilePointer(resourcename, filesize, true);
|
||||||
|
|
||||||
|
// Reservem memòria per al buffer
|
||||||
|
char *buffer = (char *)malloc(filesize);
|
||||||
|
|
||||||
|
// llegim el contingut del arxiu i el fiquem en el buffer
|
||||||
|
fread(buffer, filesize, 1, f);
|
||||||
|
|
||||||
|
// Tanquem l'arxiu
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
60
source/jfile.h
Normal file
60
source/jfile.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
#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 "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, 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
7
source/main.cpp
Normal file
7
source/main.cpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include "jfile.h"
|
||||||
|
#include "jdraw.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user