mp3 support for playing background music

This commit is contained in:
Ondřej Novák 2025-06-15 12:56:18 +02:00
parent 73a4187f79
commit dd23d8c989
24 changed files with 2245 additions and 252 deletions

View file

@ -8,6 +8,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <malloc.h> #include <malloc.h>
#include <libs/memman.h> #include <libs/memman.h>
#include <libs/music.h>
#define POCET_POSTAV 6 #define POCET_POSTAV 6
#define HODINA 360 #define HODINA 360
@ -1448,12 +1449,15 @@ void show_textured_button(int x,int y,int xs,int ys,int texture,CTL3D *border3d)
extern short sample_volume; //hlastitost samplu extern short sample_volume; //hlastitost samplu
extern char **sound_table; extern char **sound_table;
void init_tracks(void); void init_tracks(void);
void recalc_volumes(int sector,int side); void recalc_volumes(int sector,int side);
void play_effekt(int x,int y,int xd,int yd,int sector, int side,const TMA_SOUND *p); void play_effekt(int x,int y,int xd,int yd,int sector, int side,const TMA_SOUND *p);
void change_music(const char *name);
char resolve_music_source(const char *name, TMUSIC_SOURCE *new_source, TMUSIC_SOURCE_TYPE *new_type);
void create_playlist(char *playlist); void create_playlist(char *playlist);
const char *get_next_music_from_playlist(void); const char *get_next_music_from_playlist(void);
const char * end_of_song_callback(void *ctx);
void purge_playlist(void); void purge_playlist(void);
void play_sample_at_sector(int sample,int listener,int source,int track, char loop); void play_sample_at_sector(int sample,int listener,int source,int track, char loop);
void play_sample_at_channel(int sample,int channel,int vol); void play_sample_at_channel(int sample,int channel,int vol);
@ -1462,7 +1466,7 @@ char test_playing(int track);
void stop_track_free(int track); void stop_track_free(int track);
void mute_all_tracks(char all); void mute_all_tracks(char all);
void kill_all_sounds(void); void kill_all_sounds(void);
void create_sound_table(char *template,int32_t size); void create_sound_table(char *t,int32_t size);
void create_sound_table_old(void); void create_sound_table_old(void);
void start_play_flute(char ); void start_play_flute(char );
void stop_play_flute(void); void stop_play_flute(void);

View file

@ -117,17 +117,6 @@ int32_t load_section(FILE *f,void **section, int *sct_type,int32_t *sect_size)
return s; return s;
} }
*/ */
static void ddl_file_deleter(void *ctx) {
ablock_free(ctx);
}
TMPFILE_RD *open_ddl_file(const char *name, int group) {
int32_t size;
if (!test_file_exist(group, name)) return NULL;
const void *data = afile(name, group, &size);
if (!data) return NULL;
return temp_storage_from_binary(data, size, &ddl_file_deleter, (void *)data);
}
int32_t load_section_mem(TMPFILE_RD *f,void **section, int *sct_type,int32_t *sect_size) { int32_t load_section_mem(TMPFILE_RD *f,void **section, int *sct_type,int32_t *sect_size) {
int32_t s; int32_t s;

View file

@ -103,7 +103,8 @@ const void *pcx_8bit_decomp(const void *p, int32_t *s, int h);
const void *pcx_fade_decomp(const void *p, int32_t *s, int h); const void *pcx_fade_decomp(const void *p, int32_t *s, int h);
const void *load_text_decomp(const void *p, int32_t *s, int h); const void *load_text_decomp(const void *p, int32_t *s, int h);
static const char *patch_file=NULL; static const char *patch_files[4]={NULL};
int cur_page=0; int cur_page=0;
TSTR_LIST cur_config=NULL; TSTR_LIST cur_config=NULL;
@ -530,6 +531,17 @@ void clrscr(void)
} }
static void ddl_file_deleter(void *ctx) {
ablock_free(ctx);
}
TMPFILE_RD *open_ddl_file(const char *name, int group) {
int32_t size;
if (!test_file_exist(group, name)) return NULL;
const void *data = afile(name, group, &size);
if (!data) return NULL;
return temp_storage_from_binary(data, size, &ddl_file_deleter, (void *)data);
}
@ -538,18 +550,6 @@ void back_music(THE_TIMER *t)
mix_back_sound(0); mix_back_sound(0);
} }
/*void *anim_idle(EVENT_MSG *msg,void **usr)
{
usr;
if (msg->msg==E_TIMER) calc_animations();
return &anim_idle;
}*/
/*void timer_error(void)
{
puts("\x7");
}
*/
void timming(EVENT_MSG *msg,void **data) void timming(EVENT_MSG *msg,void **data)
{ {
THE_TIMER *p,*q; THE_TIMER *p,*q;
@ -881,37 +881,6 @@ const void *boldcz;
#define ERR_WINX 320 #define ERR_WINX 320
#define ERR_WINY 100 #define ERR_WINY 100
/*
char device_error(int chyba,char disk,char info)
{
char c;
void *old;
old=_STACKLOW;
_STACKLOW=NULL;
chyba,disk,info;
curfont=&boldcz;
charcolors[0]=0xffff;
for(c=1;c<5;c++) charcolors[c]=0x7fff;
memcpy(buffer_2nd,screen,screen_buffer_size);
trans_bar(320-ERR_WINX/2,240-ERR_WINY/2,ERR_WINX,ERR_WINY,0);
curcolor=0x7fff;
rectangle(320-ERR_WINX/2,240-ERR_WINY/2,320+ERR_WINX/2,240+ERR_WINY/2,0x7fff);
set_aligned_position(320,230,1,1,texty[8]);outtext(texty[8]);
set_aligned_position(320,250,1,1,texty[9]);outtext(texty[9]);
showview(0,0,0,0);
do
{
c=getche();
}
while (c!=13 && c!=27);
memcpy(screen,buffer_2nd,screen_buffer_size);
showview(0,0,0,0);
_STACKLOW=old;
return (c==13?_ERR_RETRY:_ERR_FAIL);
}
*/
void init_DDL_manager() { void init_DDL_manager() {
@ -919,9 +888,14 @@ void init_DDL_manager() {
ddlfile = local_strdup(ddlfile); ddlfile = local_strdup(ddlfile);
init_manager(); init_manager();
if (patch_file && !add_patch_file(patch_file)) { for (size_t sz = countof(patch_files); sz > 0;) {
display_error("Can't open resource file (adv_patch): %s", ddlfile); --sz;
abort(); if (patch_files[sz]) {
if (!add_patch_file(patch_files[sz])) {
display_error("Can't open resource file (adv_patch): %s", patch_files[sz]);
abort();
}
}
} }
const char *lang_fld = lang_get_folder(); const char *lang_fld = lang_get_folder();
if (lang_fld) { if (lang_fld) {
@ -981,6 +955,12 @@ void show_loading_picture(char *filename)
ablock_free(p); ablock_free(p);
} }
char end_of_song_callback(void *, TMUSIC_SOURCE *s, TMUSIC_SOURCE_TYPE *t) {
const char *ms = get_next_music_from_playlist();
return resolve_music_source(ms, s, t);
}
void init_skeldal(const INI_CONFIG *cfg) void init_skeldal(const INI_CONFIG *cfg)
{ {
@ -1173,121 +1153,7 @@ void enter_game(void)
if (end==255) konec_hry(); if (end==255) konec_hry();
} }
/*int dos58(int mode);
#pragma aux dos58=\
"mov al,1"\
"mov ah,58h"\
"int 21h"\
parm[ebx] value [eax]
*/
/*
static int do_config_skeldal(int num,int numdata,char *txt)
{
switch (num)
{
case 0:vmode=numdata;break;
case 1:zoom_speed(numdata);break;
case 2:turn_speed(numdata);break;
case 3:init_music_vol=numdata;break;
case 4:init_gfx_vol=numdata;break;
case 5:sscanf(txt,"%d %x %d %d",&snd_devnum,&snd_parm1,&snd_parm2,&snd_parm3);
sound_detection=0;
break;
case 6:snd_mixing=numdata;break;
case 7:strcopy_n(default_map,txt,20);default_map[19]='\0';break;
case 8:gamespeed=numdata;break;
case 9:level_preload=numdata;break;
// case 10:system(txt);break;
case 11:mman_patch=numdata;break;
case 12:skip_intro=numdata;break;
case 13:autosave_enabled=numdata;break;
case 14: debug_enabled=numdata;break;
case 15:full_video=numdata;break;
case 16:patch_file=getmem(strlen(txt)+1);
strcpy(patch_file,txt);
txt=strchr(patch_file,'\n');if (txt!=NULL) txt[0]=0;
break;
case 17:titles_on=numdata;break;
case 18:charmin=numdata;break;
case 19:charmax=numdata;break;
case 20:game_extras=numdata;break;
case 21:windowed=numdata;break;
case 22:gamespeedbattle=numdata;break;
case 23:windowedzoom=numdata;break;
case 24:monitor=numdata;break;
case 25:if (VERSIONNUM<numdata)
display_error("Pozor! Hra je starsi verze, nez vyzaduje dobrodruzstvi. Ve vlastnim zajmu si stahnete novou verzi, protoze toto dobrodruzstvi nemusi byt s aktualni verzi dohratelne");
break;
case 26:refresh=numdata;break;
default:num-=CESTY_POS;
gpathtable[num] = strdup(txt);
SEND_LOG("(GAME) Directory '%s' has been assigned to group nb. %d",txt,num);
break;
}
return 0;
}
*/
/*
static void config_skeldal(const char *line)
{
int ndata=0,i,maxi;
char *data=0;char *c;
c=strchr(line,' ');if (c==NULL) return;
c++;
maxi=strlen(c);
data=alloca(maxi+1);
strcpy(data,c);
while (maxi && (isspace(data[maxi-1]))) {
--maxi;
data[maxi]=0;
}
maxi=(sizeof(sinit)/sizeof(INIS));
for(i=0;i<maxi;i++) if (comcmp(line,sinit[i].heslo)) break;
if (i==maxi)
{
char s[256];
i=data-line;
strcpy(s,"Chyba v INI souboru: Neznama promenna - ");
strncat(s,line,i);
SEND_LOG("(ERROR) %s",s);
}
else
{
if (sinit[i].parmtype==INI_INT) if (sscanf(data,"%d",&ndata)!=1)
{
char s[256];
sprintf(s,"Chyba v INI souboru: Ocekava se ciselna hodnota\n%s\n",line);
SEND_LOG("(ERROR) %s",s);
}
do_config_skeldal(i,ndata,data);
}
}
*/
/*
static void configure(char *filename)
{
SEND_LOG("(GAME) Reading config. file '%s'",filename);
cur_config=read_config(filename);
if (cur_config==NULL)
{
char s[256];
sprintf(s,"\nNemohu precist konfiguracni soubor \"%s\".\n",filename);
SEND_LOG("(ERROR) %s",s);
puts(s);
exit(1);
}
process_ini(cur_config,config_skeldal);
}
*/
static int update_config(void) static int update_config(void)
{ {
@ -1314,6 +1180,7 @@ void set_verify(char state);
*/ */
void play_movie_seq(const char *s,int y) void play_movie_seq(const char *s,int y)
{ {
change_music(NULL);
play_animation(s,0,y,0); play_animation(s,0,y,0);
} }
@ -1647,7 +1514,7 @@ const char *configure_pathtable(const INI_CONFIG *cfg) {
if (defmap) { if (defmap) {
strcopy_n(default_map, defmap, sizeof(default_map)); strcopy_n(default_map, defmap, sizeof(default_map));
} }
patch_file = ini_get_string(paths, "patch_file", NULL); patch_files[0] = ini_get_string(paths, "patch_file", NULL);
if (ini_get_boolean(paths, "patch_mode", 0)) { if (ini_get_boolean(paths, "patch_mode", 0)) {
mman_patch = 1; mman_patch = 1;
} }
@ -1721,6 +1588,10 @@ int skeldal_entry_point(const SKELDAL_CONFIG *start_cfg)
return 1; return 1;
} }
if (start_cfg->patch_file) {
patch_files[1] = start_cfg->patch_file;
}
if (start_cfg->adventure_path) { if (start_cfg->adventure_path) {
TSTR_LIST adv_config=read_config(start_cfg->adventure_path); TSTR_LIST adv_config=read_config(start_cfg->adventure_path);
if (!adv_config) { if (!adv_config) {

View file

@ -12,6 +12,7 @@ typedef struct {
const char *adventure_path; const char *adventure_path;
const char *config_path; const char *config_path;
const char *lang_path; const char *lang_path;
const char *patch_file;
} SKELDAL_CONFIG; } SKELDAL_CONFIG;

View file

@ -498,9 +498,6 @@ void create_playlist(char *playlist)
playing_track=-1; playing_track=-1;
} }
const char * end_of_song_callback(void *ctx) {
return get_next_music_from_playlist();
}
const char *get_next_music_from_playlist() const char *get_next_music_from_playlist()
{ {
int i,step; int i,step;
@ -520,10 +517,7 @@ const char *get_next_music_from_playlist()
} }
while (step); while (step);
playing_track=i; playing_track=i;
const char *d = build_pathname(2, gpathtable[SR_MUSIC], cur_playlist[i]+1); const char *d = cur_playlist[i]+1;
if (!check_file_exists(d)) {
return NULL;
}
cur_playlist[i][0]=33; cur_playlist[i][0]=33;
remain_play--; remain_play--;
return d; return d;
@ -722,3 +716,37 @@ char enable_sound(char enbl)
SEND_LOG("(SOUND) Sound status (en/dis) changed: new %d, old %d",enbl,save); SEND_LOG("(SOUND) Sound status (en/dis) changed: new %d, old %d",enbl,save);
return save; return save;
} }
void change_music(const char *name) {
TMUSIC_SOURCE src;
TMUSIC_SOURCE_TYPE t;
if (resolve_music_source(name, &src, &t)) {
play_music(&src, t);
} else {
stop_play_music();
}
}
static void music_destructor(TMUSIC_SOURCE *me) {
afile_mapped_free(me->data, me->size);
}
char resolve_music_source(const char *name, TMUSIC_SOURCE *new_source, TMUSIC_SOURCE_TYPE *new_type) {
if (name == NULL || !test_file_exist(SR_MUSIC, name)) return 0;
int32_t sz;
size_t l = strlen(name);
if (l<4) return 0;
if (istrcmp(name+l-4, ".MUS") == 0) *new_type = MUSIC_SOURCE_MUS;
else if (istrcmp(name+l-4, ".MP3") == 0) *new_type = MUSIC_SOURCE_MP3;
else return 0;
const void *v = afile_mapped(name, SR_MUSIC, &sz);
new_source->data = v;
new_source->size = sz;
new_source->destructor = music_destructor;
return 1;
}

View file

@ -14,6 +14,7 @@ SET(files basicobj.c
wav_mem.c wav_mem.c
strlite.c strlite.c
cztable.c cztable.c
minimp3.c
music.cpp music.cpp
string_table.cpp ) string_table.cpp )

View file

@ -334,6 +334,12 @@ int test_file_exist(int group,const char *filename)
return 1; return 1;
} }
int file_is_in_ddl(int group, const char *filename) {
THANDLE_DATA h;
return get_file_entry(group, filename, &h);
}
THANDLE_DATA *def_handle(int handle,const char *filename,ABLOCK_DECODEPROC decompress,char path) THANDLE_DATA *def_handle(int handle,const char *filename,ABLOCK_DECODEPROC decompress,char path)
{ {
THANDLE_DATA *h; THANDLE_DATA *h;
@ -369,11 +375,11 @@ THANDLE_DATA *def_handle(int handle,const char *filename,ABLOCK_DECODEPROC decom
return h; return h;
} }
const void *afile(const char *filename,int group,int32_t *blocksize) static const void *afile2(const char *filename,int group,int32_t *blocksize, char mapped)
{ {
char *d; char *d;
char entr; char entr;
void *p; const void *p;
d=alloca(strlen(filename)+1); d=alloca(strlen(filename)+1);
strcpy(d,filename); strcpy(d,filename);
@ -388,18 +394,36 @@ const void *afile(const char *filename,int group,int32_t *blocksize)
*blocksize = *szptr; *blocksize = *szptr;
return szptr+1; return szptr+1;
} }
else if (mman_pathlist!=NULL) else if (mman_pathlist!=NULL) {
{
const char *name = build_pathname(2,mman_pathlist[group],d); const char *name = build_pathname(2,mman_pathlist[group],d);
size_t sz; size_t sz;
SEND_LOG("(LOAD) Afile is loading file '%s' from disk (group %d)",d,group); SEND_LOG("(LOAD) Afile is loading file '%s' from disk (group %d)",d,group);
p=load_file(name, &sz); if (mapped) {
*blocksize=sz; const char *iname = file_icase_find(name);
p = map_file_to_memory(iname, &sz);
*blocksize = sz;
}
else {
p=load_file(name, &sz);
*blocksize=sz;
} }
else return NULL;
return p;
} }
else {
return NULL;
}
return p;
}
const void *afile(const char *filename,int group,int32_t *blocksize) {
return afile2(filename, group, blocksize, 0);
}
const void *afile_mapped(const char *filename,int group,int32_t *blocksize) {
return afile2(filename, group, blocksize, 1);
}
void *afile_copy(const char *filename,int group,int32_t *blocksize) { void *afile_copy(const char *filename,int group,int32_t *blocksize) {
const void *ptr = afile(filename, group, blocksize); const void *ptr = afile(filename, group, blocksize);
@ -721,3 +745,8 @@ int32_t get_handle_size(int handle)
void ablock_free(const void *ptr) { void ablock_free(const void *ptr) {
if (need_to_be_free(ptr)) free((void *)ptr); if (need_to_be_free(ptr)) free((void *)ptr);
} }
void afile_mapped_free(const void *ptr, int32_t sz) {
if (need_to_be_free(ptr)) unmap_file(ptr, sz);
}

View file

@ -96,12 +96,18 @@ THANDLE_DATA *zneplatnit_block(int handle); //zneplatni data bloku
THANDLE_DATA *get_handle(int handle); //vraci informace o rukojeti THANDLE_DATA *get_handle(int handle); //vraci informace o rukojeti
int find_handle(const char *name,ABLOCK_DECODEPROC decomp); //hleda mezi rukojeti stejnou definici int find_handle(const char *name,ABLOCK_DECODEPROC decomp); //hleda mezi rukojeti stejnou definici
int test_file_exist(int group,const char *filename); //testuje zda soubor existuje v ramci mmanageru int test_file_exist(int group,const char *filename); //testuje zda soubor existuje v ramci mmanageru
int file_is_in_ddl(int group, const char *filename); //testuje zde je soubor v DDL
const void *afile(const char *filename,int group,int32_t *blocksize); //nahraje do pameti soubor registrovany v ramci mmanageru const void *afile(const char *filename,int group,int32_t *blocksize); //nahraje do pameti soubor registrovany v ramci mmanageru
void *afile_copy(const char *filename,int group,int32_t *blocksize); //nahraje do pameti soubor registrovany v ramci mmanageru void *afile_copy(const char *filename,int group,int32_t *blocksize); //nahraje do pameti soubor registrovany v ramci mmanageru
int32_t get_handle_size(int handle); int32_t get_handle_size(int handle);
//void get_mem_info(MEMORYSTATUS *mem); //void get_mem_info(MEMORYSTATUS *mem);
void ablock_free(const void *ptr); void ablock_free(const void *ptr);
//access file using memory mapping feature
const void *afile_mapped(const char *filename,int group,int32_t *blocksize);
//free file memory opened by afile_mapped
void afile_mapped_free(const void *ptr, int32_t sz);
int read_group(int index); int read_group(int index);
char add_patch_file(const char *filename); //pripojuje zaplatu char add_patch_file(const char *filename); //pripojuje zaplatu

View file

@ -185,8 +185,7 @@ void BigPlayProc(MGIF_HEADER_T *hdr,int act,const void *data,int csize)
void play_animation(const char *filename,char mode,int posy,char sound) void play_animation(const char *filename,char mode,int posy,char sound)
{ {
size_t sz; size_t sz;
void *mgf=map_file_to_memory(file_icase_find(filename), &sz); const void *mgf=map_file_to_memory(file_icase_find(filename), &sz);
change_music(NULL);
if (mgf==NULL) return; if (mgf==NULL) return;
game_display_disable_crt_effect_temporary(1); game_display_disable_crt_effect_temporary(1);
PlayMGFFile(mgf,BigPlayProc,posy,mode & 0x80); PlayMGFFile(mgf,BigPlayProc,posy,mode & 0x80);

3
libs/minimp3.c Normal file
View file

@ -0,0 +1,3 @@
#define MINIMP3_IMPLEMENTATION
#define MINIMP3_ONLY_MP3
#include "minimp3.h"

1865
libs/minimp3.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,10 @@
#include <string_view> #include <string_view>
#include "music.h" #include "music.h"
#include "minimp3.h"
#include <platform/platform.h> #include <platform/platform.h>
#include "memman.h"
#include <atomic> #include <atomic>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@ -10,30 +12,40 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include <utility> #include <utility>
#include <memory>
struct MResDeleter {
TMUSIC_SOURCE src;
void operator()(const void *ptr) {
src.destructor(&src);
}
};
using MusicData = std::unique_ptr<const void, MResDeleter>;
class MUSStreamParser: public IMusicStream { class MUSStreamParser: public IMusicStream {
public: public:
MUSStreamParser(const char *name) { MUSStreamParser(const TMUSIC_SOURCE *src)
_data = map_file_to_memory(file_icase_find(name), &sz); :_mdata(src->data, {*src}) {
if (_data) {
_loaded = true; if (_mdata) {
iter = reinterpret_cast<const uint8_t *>(_data); _data = _mdata.get();
chans = read_short(); if (_data) {
freq = read_int(); _loaded = true;
iter += 4; iter = reinterpret_cast<const uint8_t *>(_data);
blocks = read_int(); chans = read_short();
iter += 8; freq = read_int();
btable = reinterpret_cast<const short *>(iter); iter += 4;
iter += 512; blocks = read_int();
iter += 8;
btable = reinterpret_cast<const short *>(iter);
iter += 512;
}
} }
} }
~MUSStreamParser() {
if (_data) unmap_file(_data, sz);
}
MUSStreamParser(const MUSStreamParser &) = delete; MUSStreamParser(const MUSStreamParser &) = delete;
MUSStreamParser& operator=(const MUSStreamParser &) = delete; MUSStreamParser& operator=(const MUSStreamParser &) = delete;
@ -60,8 +72,8 @@ public:
protected: protected:
bool _loaded = false; bool _loaded = false;
void *_data; MusicData _mdata;
std::size_t sz; const void *_data;
std::int16_t chans; std::int16_t chans;
std::int32_t freq; std::int32_t freq;
@ -111,6 +123,85 @@ protected:
}; };
class MP3StreamParser: public IMusicStream {
public:
MP3StreamParser(const TMUSIC_SOURCE *src)
:_data(src->data, {*src})
,_mp3_size(src->size) {
if (_data && find_music_info()) {
_info.format = 2;
_loaded = true;
mp3dec_init(&dec);
}
}
virtual std::string_view read() override {
if (!_put_back_buffer.empty() || _eof) return std::exchange(_put_back_buffer, {});
while (_mp3_offset < _mp3_size) {
mp3dec_frame_info_t frame_info;
int samples = mp3dec_decode_frame(&dec,
reinterpret_cast<const uint8_t *>(_data.get())+_mp3_offset,
_mp3_size - _mp3_offset, pcm_buffer,&frame_info);
if (samples == 0) {
// Nenalezen platný frame, posuneme se o 1 bajt a zkusíme dál
_mp3_offset++;
continue;
}
_mp3_offset += frame_info.frame_bytes ;
size_t data_size = samples* frame_info.channels * sizeof(mp3d_sample_t);
return std::string_view(reinterpret_cast<const char*>(pcm_buffer), data_size);
}
_eof = true;
return {};
}
virtual void put_back(std::string_view data) override {
_put_back_buffer = data;
}
virtual TMUSIC_STREAM_INFO get_info() const override {
return _info;
}
bool is_loaded() const {return _loaded;}
protected:
MusicData _data;
size_t _mp3_size;
size_t _mp3_offset = 0;
TMUSIC_STREAM_INFO _info = {};
mp3dec_t dec{};
std::string_view _put_back_buffer = {};
mp3d_sample_t pcm_buffer[MINIMP3_MAX_SAMPLES_PER_FRAME] = {};
bool _eof = false;
bool _loaded = false;
bool find_music_info() {
mp3dec_t tmpdec{};
mp3dec_init(&tmpdec);
size_t ofs = 0;
size_t max_ofs = std::min<size_t>(_mp3_size,4096);
mp3dec_frame_info_t frame_info;
while (ofs < max_ofs) {
int samples = mp3dec_decode_frame(&tmpdec,
reinterpret_cast<const uint8_t *>(_data.get())+ofs,
_mp3_size-ofs, pcm_buffer, &frame_info);
if (samples > 0) {
_info.freq = frame_info.hz;
_info.channels = frame_info.channels;
return true;
}
++ofs;
}
return false;
}
};
void music_close(TMUSIC_STREAM *stream) { void music_close(TMUSIC_STREAM *stream) {
if (stream) { if (stream) {
@ -119,13 +210,31 @@ void music_close(TMUSIC_STREAM *stream) {
} }
} }
TMUSIC_STREAM *music_open(const char *filename) { TMUSIC_STREAM *music_open_mus(const TMUSIC_SOURCE *src) {
MUSStreamParser *player = new MUSStreamParser(filename); auto player = new MUSStreamParser(src);
if (player->is_loaded()) return player; if (player->is_loaded()) return player;
delete player; delete player;
return NULL; return NULL;
} }
TMUSIC_STREAM *music_open_mp3(const TMUSIC_SOURCE *src) {
auto player = new MP3StreamParser(src);
if (player->is_loaded()) return player;
delete player;
return NULL;
}
TMUSIC_STREAM *music_open(const TMUSIC_SOURCE *source, TMUSIC_SOURCE_TYPE t) {
if (t == MUSIC_SOURCE_MUS) {
return music_open_mus(source);
} else {
return music_open_mp3(source);
}
}
TMUSIC_STREAM_CHUNK music_read(TMUSIC_STREAM *stream) { TMUSIC_STREAM_CHUNK music_read(TMUSIC_STREAM *stream) {
TMUSIC_STREAM_CHUNK r = {NULL,0}; TMUSIC_STREAM_CHUNK r = {NULL,0};
if (stream) { if (stream) {
@ -157,4 +266,3 @@ TMUSIC_STREAM_INFO music_get_info(const TMUSIC_STREAM *stream) {
return r; return r;
} }
} }

View file

@ -5,6 +5,17 @@
extern "C" { extern "C" {
#endif #endif
typedef enum music_source_type {
MUSIC_SOURCE_MUS,
MUSIC_SOURCE_MP3
} TMUSIC_SOURCE_TYPE;
typedef struct music_source_t {
const void *data;
size_t size;
void (*destructor)(struct music_source_t *me);
} TMUSIC_SOURCE;
typedef struct music_stream_t { typedef struct music_stream_t {
char dummy; char dummy;
} TMUSIC_STREAM; } TMUSIC_STREAM;
@ -23,8 +34,23 @@ typedef struct music_stream_chunk_t {
///open music stream
TMUSIC_STREAM *music_open(const char *filename); ///open music stream MUS file
/**
* @param source source descriptor - it is copied into reader
* @return music stream if valid, or NULL, if not
*/
TMUSIC_STREAM *music_open_mus(const TMUSIC_SOURCE *source);
///open music stream MP3 file
/**
* @param source source descriptor - it is copied into reader
* @return music stream if valid, or NULL, if not
*/
TMUSIC_STREAM *music_open_mp3(const TMUSIC_SOURCE *source);
TMUSIC_STREAM *music_open(const TMUSIC_SOURCE *source, TMUSIC_SOURCE_TYPE type);
///retrieve information ///retrieve information
TMUSIC_STREAM_INFO music_get_info(const TMUSIC_STREAM *stream); TMUSIC_STREAM_INFO music_get_info(const TMUSIC_STREAM *stream);
@ -38,6 +64,7 @@ void music_put_back_chunk(TMUSIC_STREAM *stream, const TMUSIC_STREAM_CHUNK *chun
void music_close(TMUSIC_STREAM *stream); void music_close(TMUSIC_STREAM *stream);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -14,10 +14,11 @@ void show_help(const char *arg0) {
"\n" "\n"
"Usage:" "Usage:"
); );
printf("%s [-f <file>] [-a <file>] [-l <lang>] [-s <dir>] [-h]\n\n", arg0); printf("%s [-f <file>] [-a <file>] [-p <file> ] [-l <lang>] [-s <dir>] [-h]\n\n", arg0);
printf("-f <file> path to configuration file\n" printf("-f <file> path to configuration file\n"
"-a <adv> path for adventure file (.adv)\n" "-a <adv> path for adventure file (.adv)\n"
"-p <file> patch data with custom DDL\n"
"-l <lang> set language (cz|en)\n" "-l <lang> set language (cz|en)\n"
"-s <directory> generate string-tables (for localization) and exit\n" "-s <directory> generate string-tables (for localization) and exit\n"
"-h this help\n"); "-h this help\n");
@ -33,12 +34,14 @@ int main(int argc, char **argv) {
std::string config_name = SKELDALINI; std::string config_name = SKELDALINI;
std::string adv_config_file; std::string adv_config_file;
std::string gen_stringtable_path; std::string gen_stringtable_path;
std::string patch;
std::string lang; std::string lang;
for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:")) != -1; ) { for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:p:")) != -1; ) {
switch (optchr) { switch (optchr) {
case 'f': config_name = optarg;break; case 'f': config_name = optarg;break;
case 'a': adv_config_file = optarg;break; case 'a': adv_config_file = optarg;break;
case 'h': show_help(argv[0]);break; case 'h': show_help(argv[0]);break;
case 'p': patch = optarg; break;
case 'l': lang = optarg;break; case 'l': lang = optarg;break;
case 's': gen_stringtable_path = optarg;break; case 's': gen_stringtable_path = optarg;break;
default: show_help_short(); default: show_help_short();
@ -54,6 +57,7 @@ int main(int argc, char **argv) {
cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str(); cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str();
cfg.config_path = config_name.c_str(); cfg.config_path = config_name.c_str();
cfg.lang_path = lang.empty()?NULL:lang.c_str(); cfg.lang_path = lang.empty()?NULL:lang.c_str();
cfg.patch_file = patch.empty()?NULL:patch.c_str();
try { try {
if (!gen_stringtable_path.empty()) { if (!gen_stringtable_path.empty()) {

View file

@ -13,7 +13,7 @@
#include <errno.h> #include <errno.h>
// Funkce pro mapování souboru do paměti // Funkce pro mapování souboru do paměti
void* map_file_to_memory(const char *name, size_t *sz) { const void* map_file_to_memory(const char *name, size_t *sz) {
if (!name || !sz) { if (!name || !sz) {
return NULL; return NULL;
} }
@ -49,13 +49,13 @@ void* map_file_to_memory(const char *name, size_t *sz) {
} }
// Funkce pro zrušení mapování // Funkce pro zrušení mapování
void unmap_file(void *ptr, size_t sz) { void unmap_file(const void *ptr, size_t sz) {
if (!ptr || sz == 0) { if (!ptr || sz == 0) {
return; return;
} }
// Zrušení mapování // Zrušení mapování
if (munmap(ptr, sz) == -1) { if (munmap((void *)ptr, sz) == -1) {
perror("Chyba při rušení mapování"); perror("Failed to unmap file");
} }
} }

View file

@ -5,8 +5,8 @@
extern "C" { extern "C" {
#endif #endif
void *map_file_to_memory(const char *name, size_t *sz); const void *map_file_to_memory(const char *name, size_t *sz);
void unmap_file(void *ptr, size_t sz); void unmap_file(const void *ptr, size_t sz);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -82,8 +82,8 @@ char change_current_directory(const char *path);
void *map_file_to_memory(const char *name, size_t *sz); const void *map_file_to_memory(const char *name, size_t *sz);
void unmap_file(void *ptr, size_t sz); void unmap_file(const void *ptr, size_t sz);
char copy_text_to_clipboard(const char *); char copy_text_to_clipboard(const char *);

View file

@ -130,10 +130,10 @@ size_t copy_to_music_buffer(const void *data, size_t data_size) {
} }
static const char * (*end_of_song_callback)(void *ctx) = NULL; static TEND_OF_SONG_CALLBACK end_of_song_callback;
static void *end_of_song_callback_ctx = NULL; static void *end_of_song_callback_ctx = NULL;
void set_end_of_song_callback(const char * (*cb)(void *), void *ctx) { void set_end_of_song_callback(TEND_OF_SONG_CALLBACK cb, void *ctx) {
end_of_song_callback = cb; end_of_song_callback = cb;
end_of_song_callback_ctx = ctx; end_of_song_callback_ctx = ctx;
} }
@ -191,9 +191,9 @@ void create_new_music_track(int freq, int buffsize) {
music_buffer_write_pos = buffsize/2; music_buffer_write_pos = buffsize/2;
} }
static std::unique_ptr<IMusicStream> load_music_ex(const char *name) { static std::unique_ptr<IMusicStream> load_music_ex(const TMUSIC_SOURCE *src, TMUSIC_SOURCE_TYPE type) {
if (name) { if (src) {
TMUSIC_STREAM *stream = music_open(name); TMUSIC_STREAM *stream = music_open(src, type);
if (stream) { if (stream) {
TMUSIC_STREAM_INFO nfo = music_get_info(stream); TMUSIC_STREAM_INFO nfo = music_get_info(stream);
if (nfo.channels != 2) { if (nfo.channels != 2) {
@ -212,8 +212,10 @@ static std::unique_ptr<IMusicStream> load_music_ex(const char *name) {
static void handle_end_of_song() { static void handle_end_of_song() {
current_music.reset(); current_music.reset();
if (end_of_song_callback != NULL) { if (end_of_song_callback != NULL) {
const char *new_music = end_of_song_callback(end_of_song_callback_ctx); TMUSIC_SOURCE src;
current_music = load_music_ex(new_music); TMUSIC_SOURCE_TYPE type;
char ok = end_of_song_callback(end_of_song_callback_ctx, &src, &type);
current_music = ok?load_music_ex(&src, type):std::unique_ptr<IMusicStream>{};
if (current_music) { if (current_music) {
auto nfo = current_music->get_info(); auto nfo = current_music->get_info();
create_new_music_track(nfo.freq, BACK_BUFF_SIZE); create_new_music_track(nfo.freq, BACK_BUFF_SIZE);
@ -246,13 +248,16 @@ int mix_back_sound(int _) {
} }
//int open_backsound(char *filename); void stop_play_music() {
void change_music(const char *filename) {
if (music_source) { if (music_source) {
fade_music(); fade_music();
stop_music(); stop_music();
} }
current_music = load_music_ex(filename); }
void play_music(const TMUSIC_SOURCE *source, TMUSIC_SOURCE_TYPE type) {
stop_play_music();
current_music = load_music_ex(source, type);
if (current_music) { if (current_music) {
auto nfo = current_music->get_info(); auto nfo = current_music->get_info();
create_new_music_track(nfo.freq, BACK_BUFF_SIZE); create_new_music_track(nfo.freq, BACK_BUFF_SIZE);
@ -309,7 +314,7 @@ char set_snd_effect(AUDIO_PROPERTY funct,int data) {
case SND_PING: break; case SND_PING: break;
case SND_GFX: sound_effect_volume = data/256.0;break; case SND_GFX: sound_effect_volume = data/256.0;break;
case SND_MUSIC: music_volume = data/128.0;update_music_volume();break; case SND_MUSIC: music_volume = data/128.0;update_music_volume();break;
case SND_GVOLUME: master_volume = data/512.0;update_music_volume();break; case SND_GVOLUME: master_volume = data/512.0;update_music_volume();break;
case SND_SWAP: swap_channels = !!data;break; case SND_SWAP: swap_channels = !!data;break;
case SND_BASS: bass_boost = data/25.0;sound_mixer.set_bass(bass_boost);break; case SND_BASS: bass_boost = data/25.0;sound_mixer.set_bass(bass_boost);break;
case SND_TREBL: treble_boost = data/25.0;sound_mixer.set_treble(treble_boost);break; case SND_TREBL: treble_boost = data/25.0;sound_mixer.set_treble(treble_boost);break;

View file

@ -24,14 +24,19 @@ SND_MAXFUNCT} AUDIO_PROPERTY;
void game_sound_init_device(const INI_CONFIG_SECTION *audio_section); void game_sound_init_device(const INI_CONFIG_SECTION *audio_section);
typedef char (*TEND_OF_SONG_CALLBACK)(void *ctx, TMUSIC_SOURCE *, TMUSIC_SOURCE_TYPE *);
char start_mixing(); char start_mixing();
void stop_mixing(); void stop_mixing();
void play_sample(int channel,const void *sample,int32_t size,int32_t lstart,int32_t sfreq,int type); void play_sample(int channel,const void *sample,int32_t size,int32_t lstart,int32_t sfreq,int type);
void set_channel_volume(int channel,int left,int right); void set_channel_volume(int channel,int left,int right);
void set_end_of_song_callback(const char * (*cb)(void *), void *ctx); void set_end_of_song_callback(TEND_OF_SONG_CALLBACK cb, void *ctx);
void play_music(const TMUSIC_SOURCE *source, TMUSIC_SOURCE_TYPE type);
void stop_play_music();
void fade_music(); void fade_music();
int mix_back_sound(int synchro); int mix_back_sound(int synchro);
void change_music(const char *filename);
char get_channel_state(int channel); char get_channel_state(int channel);
void get_channel_volume(int channel,int *left,int *right); void get_channel_volume(int channel,int *left,int *right);
void mute_channel(int channel); void mute_channel(int channel);

View file

@ -11,7 +11,7 @@
#include <shellapi.h> #include <shellapi.h>
void show_help(std::ostream &out, const char *arg0) { void show_help(std::ostream &out, const char *arg0) {
out << out <<
"Brany Skeldalu (Gates of Skeldal) portable game player\n" "Brany Skeldalu (Gates of Skeldal) portable game player\n"
"Copyright (c) 2025 Ondrej Novak. All rights reserved.\n\n" "Copyright (c) 2025 Ondrej Novak. All rights reserved.\n\n"
"This work is licensed under the terms of the MIT license.\n" "This work is licensed under the terms of the MIT license.\n"
@ -21,9 +21,10 @@ void show_help(std::ostream &out, const char *arg0) {
out << arg0 << " [-f <file>] [-a <file>] [-l <lang>] [-s <dir>] [-h]\n\n"; out << arg0 << " [-f <file>] [-a <file>] [-l <lang>] [-s <dir>] [-h]\n\n";
out << "-f <file> path to configuration file\n" out << "-f <file> path to configuration file\n"
"-a <adv> path for adventure file (.adv)\n" "-a <adv> path for adventure file (.adv)\n"
"-p <file> patch data with custom DDL\n"
"-l <lang> set language (cz|en)\n" "-l <lang> set language (cz|en)\n"
"-s <directory> generate string-tables (for localization) and exit\n" "-s <directory> generate string-tables (for localization) and exit\n"
"-h this help\n"; "-h this help\n";
} }
void show_help_short(std::ostream &out) { void show_help_short(std::ostream &out) {
@ -31,20 +32,22 @@ void show_help_short(std::ostream &out) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
std::string config_name = SKELDALINI; std::string config_name = SKELDALINI;
std::string adv_config_file; std::string adv_config_file;
std::string gen_stringtable_path; std::string gen_stringtable_path;
std::string lang; std::string lang;
std::string patch;
std::ostringstream console; std::ostringstream console;
for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:")) != -1; ) { for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:p:")) != -1; ) {
switch (optchr) { switch (optchr) {
case 'f': config_name = optarg;break; case 'f': config_name = optarg;break;
case 'a': adv_config_file = optarg;break; case 'a': adv_config_file = optarg;break;
case 'h': show_help(console, argv[0]);break; case 'h': show_help(console, argv[0]);break;
case 'p': patch = optarg; break;
case 'l': lang = optarg;break; case 'l': lang = optarg;break;
case 's': gen_stringtable_path = optarg;break; case 's': gen_stringtable_path = optarg;break;
default: show_help_short(console);break; default: show_help_short(console);break;
} }
} }
@ -64,6 +67,7 @@ int main(int argc, char **argv) {
cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str(); cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str();
cfg.config_path = config_name.c_str(); cfg.config_path = config_name.c_str();
cfg.lang_path = lang.empty()?NULL:lang.c_str(); cfg.lang_path = lang.empty()?NULL:lang.c_str();
cfg.patch_file = patch.empty()?NULL:patch.c_str();
{ {
std::string msg = console.str(); std::string msg = console.str();

View file

@ -12,7 +12,7 @@ extern "C" {
#include <stdexcept> #include <stdexcept>
// Funkce pro mapování souboru do paměti // Funkce pro mapování souboru do paměti
void* map_file_to_memory_cpp(const char *name, size_t *sz) { const void* map_file_to_memory_cpp(const char *name, size_t *sz) {
if (!name || !sz) { if (!name || !sz) {
return NULL; return NULL;
} }
@ -26,7 +26,7 @@ void* map_file_to_memory_cpp(const char *name, size_t *sz) {
throw std::runtime_error(std::string("failed to get size of file:").append(name)); throw std::runtime_error(std::string("failed to get size of file:").append(name));
} }
HANDLE hMapping = CreateFileMapping(h,NULL,PAGE_READONLY,0,0,NULL); HANDLE hMapping = CreateFileMapping(h,NULL,PAGE_READONLY,0,0,NULL);
if (hMapping == NULL || hMapping == INVALID_HANDLE_VALUE) { if (hMapping == NULL || hMapping == INVALID_HANDLE_VALUE) {
@ -40,16 +40,16 @@ void* map_file_to_memory_cpp(const char *name, size_t *sz) {
if (mappedData == NULL) { if (mappedData == NULL) {
throw std::runtime_error(std::string("Failed to map file:").append(name)); throw std::runtime_error(std::string("Failed to map file:").append(name));
} }
*sz = fsize.LowPart; *sz = fsize.LowPart;
return mappedData; return mappedData;
} }
void* map_file_to_memory(const char *name, size_t *sz) { const void* map_file_to_memory(const char *name, size_t *sz) {
return map_file_to_memory_cpp(name, sz); return map_file_to_memory_cpp(name, sz);
} }
// Funkce pro zrušení mapování // Funkce pro zrušení mapování
void unmap_file(void *ptr, size_t) { void unmap_file(const void *ptr, size_t) {
UnmapViewOfFile(ptr); UnmapViewOfFile((void *)ptr);
} }

View file

@ -1,4 +1,4 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
void *map_file_to_memory(const char *name, size_t *sz); const void *map_file_to_memory(const char *name, size_t *sz);
void unmap_file(void *ptr, size_t sz); void unmap_file(const void *ptr, size_t sz);

View file

@ -1,4 +1,5 @@
add_executable(ddl_ar ddl_ar.cpp ddl_ar_class.cpp) add_executable(ddl_ar ddl_ar.cpp ddl_ar_class.cpp)
add_executable(pcx_diff_tool pcx_diff_tool.c) add_executable(pcx_diff_tool pcx_diff_tool.c)
add_executable(deenc deenc.c)
set_property(TARGET ddl_ar PROPERTY CXX_STANDARD 20) set_property(TARGET ddl_ar PROPERTY CXX_STANDARD 20)

43
tools/deenc.c Normal file
View file

@ -0,0 +1,43 @@
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file.ENC>\n", argv[0]);
return 1;
}
FILE *f = fopen(argv[1], "rb");
if (!f) {
perror("Can't open file");
return 1;
}
// Získání velikosti souboru
fseek(f, 0, SEEK_END);
long size = ftell(f);
rewind(f);
// Alokace bufferu
unsigned char *data = malloc(size);
if (!data) {
perror("Memory alloc error");
fclose(f);
return 1;
}
fread(data, 1, size, f);
fclose(f);
int last=0;
for (int i = 0; i < size; ++i) {
last = (last + data[i]) & 0xFF;
data[i] = last;
}
// Výstup na stdout
fwrite(data, 1, size, stdout);
free(data);
return 0;
}