large refactoring WIP

windows & osx untested
bgm untested
too many changes to comment on
This commit is contained in:
Andrei "Akari" Alexeyev 2017-03-02 12:23:30 +02:00
parent 25f856a3bc
commit ca16c30966
44 changed files with 1571 additions and 1339 deletions

View file

@ -17,7 +17,6 @@ pkg_check_modules(PNG libpng REQUIRED)
if(NOT NO_AUDIO)
pkg_check_modules(SDL2_MIXER SDL2_mixer REQUIRED)
add_definitions(-DHAVE_MIXER)
endif()
set(SRCs
@ -50,6 +49,7 @@ set(SRCs
transition.c
color.c
difficulty.c
audio_common.c
menu/menu.c
menu/mainmenu.c
menu/options.c
@ -96,9 +96,17 @@ else()
endif()
if(NO_AUDIO)
set(SRCs ${SRCs} resource/audio_null.c resource/bgm_null.c)
set(SRCs ${SRCs}
audio_null.c
resource/bgm_null.c
resource/sfx_null.c
)
else()
set(SRCs ${SRCs} resource/audio.c resource/bgm.c)
set(SRCs ${SRCs}
audio_mixer.c
resource/bgm_mixer.c
resource/sfx_mixer.c
)
endif()
if(WIN32)
@ -126,7 +134,6 @@ endif()
set(LIBs ${LIBs}
${SDL2_LIBRARIES}
${SDL2_TTF_LIBRARIES}
${SDL2_MIXER_LIBRARIES}
${PNG_LIBRARIES}
${ZLIB_LIBRARIES}
${OPENGL_LIBRARIES}
@ -152,13 +159,19 @@ endif()
set(INCs
${CMAKE_CURRENT_SOURCE_DIR}
${SDL2_INCLUDE_DIRS}
${SDL2_MIXER_INCLUDE_DIRS}
${SDL2_TTF_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
${OPENGL_INCLUDE_DIR}
${PNG_INCLUDE_DIRS}
)
# stupid cmake caching SHIT
if(NOT NO_AUDIO)
set(INCs ${INCs} ${SDL2_MIXER_INCLUDE_DIRS})
set(LIBs ${LIBs} ${SDL2_MIXER_LIBRARIES})
set(LIBDIRs ${LIBDIRs} ${SDL2_MIXER_LIBRARY_DIRS})
endif()
set(CMAKE_REQUIRED_INCLUDES ${INCs})
include_directories(${INCs})

53
src/audio.h Normal file
View file

@ -0,0 +1,53 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
*/
#ifndef AUDIO_H
#define AUDIO_H
#include "resource/sfx.h"
#include "resource/bgm.h"
typedef struct CurrentBGM {
char *name;
char *title;
int isboss;
int started_at;
Music *music;
} CurrentBGM;
extern CurrentBGM current_bgm;
void audio_backend_init(void);
void audio_backend_shutdown(void);
bool audio_backend_initialized(void);
void audio_backend_set_sfx_volume(float gain);
void audio_backend_set_bgm_volume(float gain);
bool audio_backend_music_is_paused(void);
bool audio_backend_music_is_playing(void);
void audio_backend_music_resume(void);
void audio_backend_music_stop(void);
void audio_backend_music_pause(void);
bool audio_backend_music_play(void *impl);
bool audio_backend_sound_play(void *impl);
void audio_init(void);
void audio_shutdown(void);
void play_sound(const char *name);
void play_ui_sound(const char *name);
void reset_sounds(void);
Sound* get_sound(const char *name);
Music* get_music(const char *music);
void start_bgm(const char *name);
void stop_bgm(void);
void resume_bgm(void);
void save_bgm(void);
void restore_bgm(void);
#endif

193
src/audio_common.c Normal file
View file

@ -0,0 +1,193 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
* Copyright (C) 2012, Alexeyew Andrew <http://akari.thebadasschoobs.org/>
*/
#include <string.h>
#include <stdio.h>
#include "audio.h"
#include "resource/resource.h"
#include "global.h"
static char *saved_bgm;
static Hashtable *bgm_descriptions;
CurrentBGM current_bgm = { .name = NULL };
static void play_sound_internal(const char *name, int unconditional) {
if(!audio_backend_initialized() || global.frameskip) {
return;
}
Sound *snd = get_sound(name);
if(!snd || (!unconditional && snd->lastplayframe == global.frames)) {
return;
}
snd->lastplayframe = global.frames;
audio_backend_sound_play(snd->impl);
}
void play_sound(const char *name) {
play_sound_internal(name, false);
}
void play_ui_sound(const char *name) {
play_sound_internal(name, true);
}
void reset_sounds(void) {
Resource *snd;
for(HashtableIterator *i = hashtable_iter(resources.handlers[RES_SFX].mapping);
hashtable_iter_next(i, NULL, (void**)&snd);) {
snd->sound->lastplayframe = 0;
}
}
Sound* get_sound(const char *name) {
Resource *res = get_resource(RES_SFX, name, 0);
return res ? res->sound : NULL;
}
Music* get_music(const char *name) {
Resource *res = get_resource(RES_BGM, name, 0);
return res ? res->music : NULL;
}
static void sfx_cfg_volume_callback(ConfigIndex idx, ConfigValue v) {
audio_backend_set_sfx_volume(config_set_float(idx, v.f));
}
static void bgm_cfg_volume_callback(ConfigIndex idx, ConfigValue v) {
audio_backend_set_bgm_volume(config_set_float(idx, v.f));
}
static void load_bgm_descriptions(void) {
char *fullname = strjoin(get_prefix(), "bgm/bgm.conf", NULL);
FILE *fp = fopen(fullname, "rt");
free(fullname);
bgm_descriptions = hashtable_new_stringkeys(16);
if(fp == NULL) {
return;
}
char line[256];
while(fgets(line, sizeof(line), fp)) {
char *rem;
while((rem = strchr(line,'\n')) != NULL) *rem = '\0';
while((rem = strchr(line,'\r')) != NULL) *rem = '\0';
while((rem = strchr(line,'\t')) != NULL) *rem = ' ';
if((rem = strchr(line,' ' )) == NULL) {
if(strlen(line) > 0)
warnx("load_bgm_description(): illegal string format. See README.");
continue;
}
*(rem++)='\0';
char *value = strjoin("BGM: ", rem, NULL);
hashtable_set_string(bgm_descriptions, line, value);
printf("Music %s is now known as \"%s\".\n", line, value);
}
fclose(fp);
return;
}
static inline char* get_bgm_desc(char *name) {
return (char*)hashtable_get_string(bgm_descriptions, name);
}
void resume_bgm(void) {
start_bgm(current_bgm.name); // In most cases it just unpauses existing music.
}
void stop_bgm(void) {
if(!current_bgm.name) {
return;
}
if(audio_backend_music_is_playing() && !audio_backend_music_is_paused()) {
audio_backend_music_pause(); // Pause, not halt - to be unpaused in continue_bgm() if needed.
printf("BGM stopped.\n");
} else {
printf("stop_bgm(): No BGM was playing.\n");
}
}
void save_bgm(void) {
// Deal with consequent saves without restore.
stralloc(&saved_bgm, current_bgm.name);
}
void restore_bgm(void) {
start_bgm(saved_bgm);
free(saved_bgm);
saved_bgm = NULL;
}
void start_bgm(const char *name) {
if(!name || !*name) {
stop_bgm();
return;
}
// if BGM has changed, change it and start from beginning
if(!current_bgm.name || strcmp(name, current_bgm.name)) {
audio_backend_music_stop();
stralloc(&current_bgm.name, name);
if((current_bgm.music = get_music(name)) == NULL) {
warnx("start_bgm(): BGM '%s' does not not exist", current_bgm.name);
stop_bgm();
free(current_bgm.name);
current_bgm.name = NULL;
return;
}
}
if(audio_backend_music_is_paused()) {
audio_backend_music_resume();
}
if(audio_backend_music_is_playing()) {
return;
}
if(!audio_backend_music_play(current_bgm.music->impl)) {
return;
}
// Support drawing BGM title in game loop (only when music changed!)
if((current_bgm.title = get_bgm_desc(current_bgm.name)) != NULL) {
current_bgm.started_at = global.frames;
// Boss BGM title color may differ from the one at beginning of stage
current_bgm.isboss = strendswith(current_bgm.name, "boss");
} else {
current_bgm.started_at = -1;
}
printf("Started %s\n", (current_bgm.title ? current_bgm.title : current_bgm.name));
}
void audio_init(void) {
audio_backend_init();
load_bgm_descriptions();
config_set_callback(CONFIG_SFX_VOLUME, sfx_cfg_volume_callback);
config_set_callback(CONFIG_BGM_VOLUME, bgm_cfg_volume_callback);
}
void audio_shutdown(void) {
audio_backend_shutdown();
hashtable_foreach(bgm_descriptions, hashtable_iter_free_data, NULL);
hashtable_free(bgm_descriptions);
}

173
src/audio_mixer.c Normal file
View file

@ -0,0 +1,173 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
*/
#include <SDL_mixer.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "audio.h"
#include "global.h"
#include "list.h"
#include "taisei_err.h"
#define AUDIO_FREQ 44100
#define AUDIO_FORMAT MIX_DEFAULT_FORMAT
#define AUDIO_CHANNELS 100
static bool mixer_loaded = false;
const char *mixer_audio_exts[] = { ".wav", ".ogg", ".mp3", ".mod", ".xm", ".s3m",
".669", ".it", ".med", ".mid", ".flac", ".aiff", ".voc",
NULL };
void audio_backend_init(void) {
if(mixer_loaded) {
return;
}
if(SDL_InitSubSystem(SDL_INIT_AUDIO)) {
warnx("audio_backend_init(): SDL_InitSubSystem() failed: %s.\n", SDL_GetError());
return;
}
if(!Mix_Init(MIX_INIT_OGG | MIX_INIT_FLAC | MIX_INIT_MOD | MIX_INIT_MP3)) {
Mix_Quit(); // Try to shutdown mixer if it was partly initialized
return;
}
if(Mix_OpenAudio(AUDIO_FREQ, AUDIO_FORMAT, 2, config_get_int(CONFIG_MIXER_CHUNKSIZE)) == -1) {
warnx("audio_backend_init(): Mix_OpenAudio() failed: %s.\n", Mix_GetError());
Mix_Quit();
return;
}
int channels = Mix_AllocateChannels(AUDIO_CHANNELS);
if(!channels) {
warnx("audio_backend_init(): unable to allocate any channels.\n");
Mix_CloseAudio();
Mix_Quit();
return;
}
if(channels < AUDIO_CHANNELS) {
warnx("audio_backend_init(): allocated only %d of %d channels.\n", channels, AUDIO_CHANNELS);
}
mixer_loaded = true;
audio_backend_set_sfx_volume(config_get_float(CONFIG_SFX_VOLUME));
audio_backend_set_bgm_volume(config_get_float(CONFIG_BGM_VOLUME));
int frequency = 0;
uint16_t format = 0;
Mix_QuerySpec(&frequency, &format, &channels);
if(frequency != AUDIO_FREQ || format != AUDIO_FORMAT) {
warnx( "audio_backend_init(): mixer spec doesn't match our request, "
"requested (freq=%i, fmt=%u), got (freq=%i, fmt=%u). "
"Sound may be distorted.",
AUDIO_FREQ, AUDIO_FORMAT, frequency, format);
}
printf("audio_backend_init(): audio subsystem initialized (SDL2_Mixer)\n");
}
void audio_backend_shutdown(void) {
Mix_CloseAudio();
Mix_Quit();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
mixer_loaded = false;
printf("audio_backend_shutdown(): audio subsystem uninitialized (SDL2_Mixer)\n");
}
bool audio_backend_initialized(void) {
return mixer_loaded;
}
void audio_backend_set_sfx_volume(float gain) {
if(mixer_loaded)
Mix_Volume(-1, gain * MIX_MAX_VOLUME);
}
void audio_backend_set_bgm_volume(float gain) {
if(mixer_loaded)
Mix_VolumeMusic(gain * MIX_MAX_VOLUME);
}
char* audio_mixer_sound_path(const char *name) {
for(const char **ext = mixer_audio_exts; *ext; ++ext) {
char *p = strjoin(get_prefix(), SFX_PATH_PREFIX, name, *ext, NULL);
struct stat statbuf;
if(!stat(p, &statbuf)) {
return p;
}
free(p);
}
return NULL;
}
bool audio_mixer_check_sound_path(const char *path, bool isbgm) {
if(strstartswith(resource_util_filename(path), "bgm_") == isbgm) {
return strendswith_any(path, mixer_audio_exts);
}
return false;
}
void audio_backend_music_stop(void) {
if(mixer_loaded)
Mix_HaltMusic();
}
bool audio_backend_music_is_paused(void) {
return mixer_loaded && Mix_PausedMusic();
}
bool audio_backend_music_is_playing(void) {
return mixer_loaded && Mix_PlayingMusic();
}
void audio_backend_music_resume(void) {
if(mixer_loaded)
Mix_ResumeMusic();
}
void audio_backend_music_pause(void) {
if(mixer_loaded)
Mix_PauseMusic();
}
bool audio_backend_music_play(void *impl) {
if(!mixer_loaded)
return false;
bool result = (Mix_PlayMusic((Mix_Music*)impl, -1) != -1);
if(!result) {
warnx("audio_backend_music_play(): Mix_PlayMusic() failed: %s", Mix_GetError());
}
return result;
}
bool audio_backend_sound_play(void *impl) {
if(!mixer_loaded)
return false;
bool result = (Mix_PlayChannel(-1, (Mix_Chunk*)impl, 0) != -1);
if(!result) {
warnx("audio_backend_sound_play(): Mix_PlayChannel() failed: %s", Mix_GetError());
}
return result;
}

15
src/audio_null.c Normal file
View file

@ -0,0 +1,15 @@
#include "audio.h"
void audio_backend_init(void) {}
void audio_backend_shutdown(void) {}
bool audio_backend_initialized(void) { return false; }
void audio_backend_set_sfx_volume(float gain) {}
void audio_backend_set_bgm_volume(float gain) {}
bool audio_backend_music_is_paused(void) { return false; }
bool audio_backend_music_is_playing(void) { return false; }
void audio_backend_music_resume(void) { return; }
void audio_backend_music_stop(void) {}
void audio_backend_music_pause(void) {}
bool audio_backend_music_play(void *impl) { return false; }
bool audio_backend_sound_play(void *impl) { return false; }

View file

@ -70,9 +70,6 @@
CONFIGDEF_INT (VID_HEIGHT, "vid_height", RESY) \
CONFIGDEF_INT (VID_RESIZABLE, "vid_resizable", 0) \
CONFIGDEF_INT (VSYNC, "vsync", 2) \
CONFIGDEF_INT (NO_SHADER, "disable_shader", 0) \
CONFIGDEF_INT (NO_AUDIO, "disable_audio", 0) \
CONFIGDEF_INT (NO_MUSIC, "disable_bgm", 0) \
CONFIGDEF_INT (MIXER_CHUNKSIZE, "mixer_chunksize", 1024) \
CONFIGDEF_FLOAT (SFX_VOLUME, "sfx_volume", 1.0) \
CONFIGDEF_FLOAT (BGM_VOLUME, "bgm_volume", 1.0) \

View file

@ -96,11 +96,10 @@ void credits_towerwall_draw(Vector pos) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, get_tex("stage6/towerwall")->gltex);
if(!config_get_int(CONFIG_NO_SHADER)) {
Shader *s = get_shader("tower_wall");
glUseProgram(s->prog);
glUniform1i(uniloc(s, "lendiv"), 2800.0 + 300.0 * sin(global.frames / 77.7));
}
Shader *s = get_shader("tower_wall");
glUseProgram(s->prog);
glUniform1i(uniloc(s, "lendiv"), 2800.0 + 300.0 * sin(global.frames / 77.7));
glPushMatrix();
glTranslatef(pos[0], pos[1], pos[2]);

View file

@ -243,6 +243,26 @@ bool strendswith(const char *s, const char *e) {
return true;
}
bool strstartswith(const char *s, const char *p) {
int ls = strlen(s);
int lp = strlen(p);
if(ls < lp)
return false;
return !strncmp(s, p, lp);
}
bool strendswith_any(const char *s, const char **earray) {
for(const char **e = earray; *e; ++e) {
if(strendswith(s, *e)) {
return true;
}
}
return false;
}
void stralloc(char **dest, const char *src) {
free(*dest);
@ -254,6 +274,73 @@ void stralloc(char **dest, const char *src) {
}
}
char* strjoin(const char *first, ...) {
va_list args;
size_t size = strlen(first) + 1;
char *str = malloc(size);
strcpy(str, first);
va_start(args, first);
for(;;) {
char *next = va_arg(args, char*);
if(!next) {
break;
}
size += strlen(next);
str = realloc(str, size);
strcat(str, next);
}
va_end(args);
return str;
}
char* read_all(const char *filename, int *outsize) {
char *text;
size_t size;
FILE *file = fopen(filename, "r");
if(file == NULL)
errx(-1, "Error opening '%s'", filename);
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);
text = malloc(size+1);
fread(text, size, 1, file);
text[size] = 0;
fclose(file);
if(outsize) {
*outsize = size;
}
return text;
}
char* copy_segment(const char *text, const char *delim, int *size) {
char *seg, *beg, *end;
beg = strstr(text, delim);
if(!beg)
return NULL;
beg += strlen(delim);
end = strstr(beg, "%%");
if(!end)
return NULL;
*size = end-beg;
seg = malloc(*size+1);
strlcpy(seg, beg, *size+1);
return seg;
}
// Inputdevice-agnostic method of checking whether a game control is pressed.
// ALWAYS use this instead of SDL_GetKeyState if you need it.
bool gamekeypressed(KeyIndex key) {

View file

@ -16,7 +16,7 @@
#include "tscomplex.h"
#include "color.h"
#include "resource/audio.h"
#include "resource/sfx.h"
#include "resource/bgm.h"
#include "resource/shader.h"
#include "resource/font.h"
@ -42,6 +42,7 @@
#include "events.h"
#include "difficulty.h"
#include "color.h"
#include "audio.h"
#include "taisei_err.h"
#include "rwops/all.h"
@ -160,9 +161,14 @@ double clamp(double, double, double);
double approach(double v, double t, double d);
double psin(double);
bool strendswith(const char *s, const char *e);
bool strstartswith(const char *s, const char *p);
bool strendswith_any(const char *s, const char **earray);
void stralloc(char **dest, const char *src);
char* strjoin(const char *first, ...);
bool gamekeypressed(KeyIndex key);
int getenvint(const char *v);
char* read_all(const char *filename, int *size);
char* copy_segment(const char *text, const char *delim, int *size);
#define SIGN(x) ((x > 0) - (x < 0))

View file

@ -1,4 +1,3 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
@ -30,8 +29,15 @@ struct Hashtable {
HTHashFunc hash_func;
HTCopyFunc copy_func;
HTFreeFunc free_func;
ListContainer *deferred_unsets;
};
typedef struct HashtableIterator {
Hashtable *hashtable;
size_t bucketnum;
HashtableElement *elem;
} HashtableIterator;
/*
* Generic functions
*/
@ -46,6 +52,9 @@ Hashtable* hashtable_new(size_t size, HTCmpFunc cmp_func, HTHashFunc hash_func,
ht->hash_func = hash_func;
ht->copy_func = copy_func;
ht->free_func = free_func;
ht->deferred_unsets = NULL;
assert(ht->hash_func != NULL);
return ht;
}
@ -62,6 +71,8 @@ static void hashtable_delete_callback(void **vlist, void *velem, void *vht) {
}
void hashtable_unset_all(Hashtable *ht) {
assert(ht != NULL);
for(size_t i = 0; i < ht->table_size; ++i) {
delete_all_elements_witharg((void**)(ht->table + i), hashtable_delete_callback, ht);
}
@ -77,7 +88,7 @@ void hashtable_free(Hashtable *ht) {
free(ht);
}
static inline bool hashtable_compare(Hashtable *ht, void *key1, void *key2) {
static bool hashtable_compare(Hashtable *ht, void *key1, void *key2) {
if(ht->cmp_func) {
return ht->cmp_func(key1, key2);
}
@ -86,6 +97,8 @@ static inline bool hashtable_compare(Hashtable *ht, void *key1, void *key2) {
}
void* hashtable_get(Hashtable *ht, void *key) {
assert(ht != NULL);
hash_t hash = ht->hash_func(key);
HashtableElement *elems = ht->table[hash % ht->table_size];
@ -99,6 +112,8 @@ void* hashtable_get(Hashtable *ht, void *key) {
}
void hashtable_set(Hashtable *ht, void *key, void *data) {
assert(ht != NULL);
hash_t hash = ht->hash_func(key);
size_t idx = hash % ht->table_size;
HashtableElement *elems = ht->table[idx], *elem;
@ -133,7 +148,36 @@ void hashtable_unset(Hashtable *ht, void *key) {
hashtable_set(ht, key, NULL);
}
void hashtable_unset_deferred(Hashtable *ht, void *key) {
assert(ht != NULL);
ListContainer *c = create_element((void**)&ht->deferred_unsets, sizeof(ListContainer));
if(ht->copy_func) {
ht->copy_func(&c->data, key);
} else {
c->data = key;
}
}
void hashtable_unset_deferred_now(Hashtable *ht) {
ListContainer *next;
assert(ht != NULL);
for(ListContainer *c = ht->deferred_unsets; c; c = next) {
next = c->next;
hashtable_unset(ht, c->data);
delete_element((void**)&ht->deferred_unsets, c);
}
}
/*
* Iteration functions
*/
void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg) {
assert(ht != NULL);
void *ret = NULL;
for(size_t i = 0; i < ht->table_size; ++i) {
@ -148,6 +192,44 @@ void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg) {
return ret;
}
HashtableIterator* hashtable_iter(Hashtable *ht) {
assert(ht != NULL);
HashtableIterator *iter = malloc(sizeof(HashtableIterator));
iter->hashtable = ht;
iter->bucketnum = (size_t)-1;
return iter;
}
bool hashtable_iter_next(HashtableIterator *iter, void **out_key, void **out_data) {
Hashtable *ht = iter->hashtable;
if(iter->bucketnum == (size_t)-1) {
iter->bucketnum = 0;
iter->elem = ht->table[iter->bucketnum];
} else {
iter->elem = iter->elem->next;
}
while(!iter->elem) {
if(++iter->bucketnum == ht->table_size) {
free(iter);
return false;
}
iter->elem = ht->table[iter->bucketnum];
}
if(out_key) {
*out_key = iter->elem->key;
}
if(out_data) {
*out_data = iter->elem->data;
}
return true;
}
/*
* Convenience functions for hashtables with string keys
*/
@ -206,10 +288,8 @@ void* hashtable_iter_free_data(void *key, void *data, void *arg) {
size_t hashtable_get_approx_overhead(Hashtable *ht) {
size_t o = sizeof(Hashtable) + sizeof(HashtableElement*) * ht->table_size;
for(size_t i = 0; i < ht->table_size; ++i) {
for(HashtableElement *e = ht->table[i]; e; e = e->next) {
o += sizeof(HashtableElement);
}
for(HashtableIterator *i = hashtable_iter(ht); hashtable_iter_next(i, NULL, NULL);) {
o += sizeof(HashtableElement);
}
return o;

View file

@ -14,6 +14,7 @@
#include <stdlib.h>
typedef struct Hashtable Hashtable;
typedef struct HashtableIterator HashtableIterator;
typedef uint32_t hash_t;
typedef bool (*HTCmpFunc)(void *key1, void *key2);
@ -27,8 +28,13 @@ void hashtable_free(Hashtable *ht);
void* hashtable_get(Hashtable *ht, void *key);
void hashtable_set(Hashtable *ht, void *key, void *data);
void hashtable_unset(Hashtable *ht, void *key);
void hashtable_unset_deferred(Hashtable *ht, void *key);
void hashtable_unset_deferred_now(Hashtable *ht);
void hashtable_unset_all(Hashtable *ht);
void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg);
HashtableIterator* hashtable_iter(Hashtable *ht);
bool hashtable_iter_next(HashtableIterator *iter, void **out_key, void **out_data);
bool hashtable_cmpfunc_string(void *str1, void *str2);
hash_t hashtable_hashfunc_string(void *vstr);
@ -39,6 +45,7 @@ Hashtable* hashtable_new_stringkeys(size_t size);
void* hashtable_get_string(Hashtable *ht, const char *key);
void hashtable_set_string(Hashtable *ht, const char *key, void *data);
void hashtable_unset_string(Hashtable *ht, const char *key);
void hashtable_unset_deferred_string(Hashtable *ht, const char *key);
void* hashtable_iter_free_data(void *key, void *data, void *arg);

View file

@ -143,7 +143,7 @@ void draw_lasers(void) {
Laser *laser;
for(laser = global.lasers; laser; laser = laser->next) {
if(laser->shader && !config_get_int(CONFIG_NO_SHADER) && glext.draw_instanced)
if(laser->shader && glext.draw_instanced)
draw_laser_curve_instanced(laser);
else
draw_laser_curve(laser);

View file

@ -38,6 +38,10 @@ void *create_element(void **dest, int size) {
return e;
}
ListContainer* create_container(ListContainer **dest) {
return create_element((void**)dest, sizeof(ListContainer));
}
void delete_element(void **dest, void *e) {
if(((List *)e)->prev != NULL)
((List *)((List *)e)->prev)->next = ((List *)e)->next;

View file

@ -17,6 +17,14 @@ void delete_element(void **dest, void *e);
void delete_all_elements(void **dest, void (callback)(void **, void *));
void delete_all_elements_witharg(void **dest, void (callback)(void **, void *, void *), void *arg);
typedef struct ListContainer {
void *next;
void *prev;
void *data;
} ListContainer;
ListContainer* create_container(ListContainer **dest);
typedef struct {
void *ptr;
int refs;

View file

@ -10,6 +10,7 @@
#include "global.h"
#include "video.h"
#include "audio.h"
#include "stage.h"
#include "menu/mainmenu.h"
#include "paths/native.h"
@ -23,11 +24,9 @@ void taisei_shutdown(void) {
progress_save();
printf("\nshutdown:\n");
if(!config_get_int(CONFIG_NO_AUDIO)) shutdown_sfx();
if(!config_get_int(CONFIG_NO_MUSIC)) shutdown_bgm();
free_all_refs();
free_resources();
audio_shutdown();
video_shutdown();
gamepad_shutdown();
stage_free_array();
@ -137,27 +136,26 @@ int main(int argc, char **argv) {
printf("initialize:\n");
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0)
if(SDL_Init(SDL_INIT_VIDEO) < 0)
errx(-1, "Error initializing SDL: %s", SDL_GetError());
printf("-- SDL_Init\n");
printf("-- SDL\n");
init_global();
video_init();
printf("-- Video and OpenGL\n");
audio_init();
printf("-- Audio\n");
init_resources();
draw_loading_screen();
load_resources();
gamepad_init();
stage_init_array();
progress_load(); // stage_init_array goes first!
// Order DOES matter: init_global, then sfx/bgm, then load_resources.
init_sfx();
init_bgm();
load_resources();
set_transition(TransLoader, 0, FADE_TIME*2);
printf("initialization complete.\n");
@ -172,13 +170,7 @@ int main(int argc, char **argv) {
#ifdef DEBUG
if(argc >= 2 && argv[1] && !strcmp(argv[1], "dumprestables")) {
hashtable_print_stringkeys(resources.textures);
hashtable_print_stringkeys(resources.animations);
hashtable_print_stringkeys(resources.sounds);
hashtable_print_stringkeys(resources.music);
hashtable_print_stringkeys(resources.shaders);
hashtable_print_stringkeys(resources.models);
hashtable_print_stringkeys(resources.bgm_descriptions);
print_resource_hashtables();
return 0;
}

View file

@ -36,17 +36,15 @@ void create_ingame_menu(MenuData *m) {
void draw_ingame_menu_bg(float f) {
float rad = f*IMENU_BLUR;
if(!config_get_int(CONFIG_NO_SHADER)) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Shader *shader = get_shader("ingame_menu");
glUseProgram(shader->prog);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Shader *shader = get_shader("ingame_menu");
glUseProgram(shader->prog);
glUniform1f(uniloc(shader, "rad"), rad);
glUniform1f(uniloc(shader, "rad"), rad);
draw_fbo_viewport(&resources.fsec);
draw_fbo_viewport(&resources.fsec);
glUseProgram(0);
}
glUseProgram(0);
}
void draw_ingame_menu(MenuData *menu) {

View file

@ -218,20 +218,20 @@ bool bind_isactive(OptionBinding *b) {
// --- Shared binding callbacks --- //
int bind_common_onoffget(void *b) {
return !config_get_int(((OptionBinding*)b)->configentry);
int bind_common_onoffget(OptionBinding *b) {
return !config_get_int(b->configentry);
}
int bind_common_onoffset(void *b, int v) {
return !config_set_int(((OptionBinding*)b)->configentry, !v);
int bind_common_onoffset(OptionBinding *b, int v) {
return !config_set_int(b->configentry, !v);
}
int bind_common_onoffget_inverted(void *b) {
return config_get_int(((OptionBinding*)b)->configentry);
int bind_common_onoffget_inverted(OptionBinding *b) {
return config_get_int(b->configentry);
}
int bind_common_onoffset_inverted(void *b, int v) {
return config_set_int(((OptionBinding*)b)->configentry, v);
int bind_common_onoffset_inverted(OptionBinding *b, int v) {
return config_set_int(b->configentry, v);
}
#define bind_common_intget bind_common_onoffget_inverted
@ -243,33 +243,29 @@ bool bind_stagebg_fpslimit_dependence(void) {
return config_get_int(CONFIG_NO_STAGEBG) == 2;
}
bool bind_sfxvol_dependence(void) {
return !config_get_int(CONFIG_NO_AUDIO);
}
bool bind_bgmvol_dependence(void) {
return !config_get_int(CONFIG_NO_MUSIC);
bool bind_audio_dependence(void) {
return audio_backend_initialized();
}
bool bind_resizable_dependence(void) {
return !config_get_int(CONFIG_FULLSCREEN);
}
int bind_saverpy_get(void *b) {
int v = config_get_int(((OptionBinding*)b)->configentry);
int bind_saverpy_get(OptionBinding *b) {
int v = config_get_int(b->configentry);
if(v > 1)
return v;
return !v;
}
int bind_saverpy_set(void *b, int v) {
int bind_saverpy_set(OptionBinding *b, int v) {
if(v > 1)
return config_set_int(((OptionBinding*)b)->configentry, v);
return !config_set_int(((OptionBinding*)b)->configentry, !v);
return config_set_int(b->configentry, v);
return !config_set_int(b->configentry, !v);
}
int bind_resolution_set(void *b, int v) {
int bind_resolution_set(OptionBinding *b, int v) {
if(v >= 0) {
VideoMode *m = video.modes + v;
config_set_int(CONFIG_VID_WIDTH, m->width);
@ -357,12 +353,6 @@ void options_sub_video(MenuData *parent, void *arg) {
add_menu_separator(m);
#ifdef DEBUG
add_menu_entry(m, "Shaders", do_nothing,
b = bind_option(CONFIG_NO_SHADER, bind_common_onoffget_inverted, bind_common_onoffset_inverted)
); bind_onoff(b);
#endif
add_menu_entry(m, "Stage background", do_nothing,
b = bind_option(CONFIG_NO_STAGEBG, bind_common_intget, bind_common_intset)
); bind_addvalue(b, "on");
@ -614,27 +604,13 @@ void create_options_menu(MenuData *m) {
add_menu_separator(m);
add_menu_entry(m, "Sound effects", do_nothing,
b = bind_option(CONFIG_NO_AUDIO, bind_common_onoffget_inverted,
bind_common_onoffset_inverted)
); bind_onoff(b);
add_menu_entry(m, "Volume", do_nothing,
add_menu_entry(m, "SFX Volume", do_nothing,
b = bind_scale(CONFIG_SFX_VOLUME, 0, 1, 0.1)
); bind_setdependence(b, bind_sfxvol_dependence);
b->pad++;
); bind_setdependence(b, bind_audio_dependence);
add_menu_separator(m);
add_menu_entry(m, "Background music", do_nothing,
b = bind_option(CONFIG_NO_MUSIC, bind_common_onoffget_inverted,
bind_common_onoffset_inverted)
); bind_onoff(b);
add_menu_entry(m, "Volume", do_nothing,
add_menu_entry(m, "BGM Volume", do_nothing,
b = bind_scale(CONFIG_BGM_VOLUME, 0, 1, 0.1)
); bind_setdependence(b, bind_bgmvol_dependence);
b->pad++;
); bind_setdependence(b, bind_audio_dependence);
add_menu_separator(m);
add_menu_entry(m, "Video options…", options_sub_video, NULL);

View file

@ -16,8 +16,10 @@ void draw_options_menu(MenuData *m);
#define OPTIONS_TEXT_INPUT_BUFSIZE 50
typedef int (*BindingGetter)(void*);
typedef int (*BindingSetter)(void*, int);
typedef struct OptionBinding OptionBinding;
typedef int (*BindingGetter)(OptionBinding*);
typedef int (*BindingSetter)(OptionBinding*, int);
typedef bool (*BindingDependence)(void);
typedef enum BindingType {

View file

@ -196,7 +196,7 @@ int asymptotic(Projectile *p, int t) { // v = a[0]*(a[1] + 1); a[1] -> 0
}
void _ProjDraw(Projectile *proj, int t) {
if(proj->clr && !config_get_int(CONFIG_NO_SHADER)) {
if(proj->clr) {
static GLfloat clr[4];
Shader *shader = get_shader("bullet_color");
glUseProgram(shader->prog);
@ -204,16 +204,11 @@ void _ProjDraw(Projectile *proj, int t) {
glUniform4fv(uniloc(shader, "color"), 1, clr);
}
if(!proj->clr && config_get_int(CONFIG_NO_SHADER))
glColor3f(0,0,0);
draw_texture_p(0,0, proj->tex);
if(proj->clr && config_get_int(CONFIG_NO_SHADER))
glColor3f(1,1,1);
if(!config_get_int(CONFIG_NO_SHADER))
if(proj->clr) {
glUseProgram(0);
}
}
void ProjDraw(Projectile *proj, int t) {

View file

@ -6,66 +6,91 @@
*/
#include "animation.h"
#include "texture.h"
#include "global.h"
#include "resource.h"
#include "list.h"
#include "taisei_err.h"
Animation *init_animation(const char *filename) {
Animation *buf = malloc(sizeof(Animation));
char* animation_path(const char *name) {
// stub
// we could scan the gfx/ directory and find the first matching animation here
// ...or we could describe animations with simple text files instead of encoding them in texture file names
return NULL;
}
char *beg = strstr(filename, "gfx/") + 4;
char *end = strrchr(filename, '.');
bool check_animation_path(const char *path) {
char *base = strjoin(get_prefix(), TEX_PATH_PREFIX, "ani_", NULL);
bool result = strstartswith(path, base) && strendswith(path, TEX_EXTENSION);
free(base);
return result;
}
int sz = end - beg + 1;
char name[end - beg + 1];
strlcpy(name, beg, sz);
char* animation_name(const char *filename) {
char *name = resource_util_basename(ANI_PATH_PREFIX, filename);
char *c = name, *newname;
char* tok;
while(c = strchr(c, '_')) {
newname = ++c;
}
char buf[strlen(newname)+1];
strcpy(buf, newname);
free(name);
newname = NULL;
stralloc(&newname, buf);
return newname;
}
void* load_animation(const char *filename) {
Animation *ani = malloc(sizeof(Animation));
char *basename = resource_util_basename(ANI_PATH_PREFIX, filename);
char name[strlen(basename) + 1];
strcpy(name, basename);
char *tok;
strtok(name, "_");
if((tok = strtok(NULL, "_")) == NULL)
errx(-1, "init_animation():\n!- bad 'rows' in filename '%s'", name);
buf->rows = atoi(tok);
if((tok = strtok(NULL, "_")) == NULL)
errx(-1, "init_animation():\n!- bad 'cols' in filename '%s'", name);
buf->cols = atoi(tok);
if((tok = strtok(NULL, "_")) == NULL)
errx(-1, "init_animation():\n!- bad 'speed' in filename '%s'", name);
buf->speed = atoi(tok);
if(buf->speed == 0)
errx(-1, "animation speed of %s == 0. relativity?", name);
#define ANIFAIL(what) { warnx("load_animation(): bad '" what "' in filename '%s'", basename); free(basename); return NULL; }
if((tok = strtok(NULL, "_")) == NULL)
errx(-1, "init_animation():\n!- bad 'name' in filename '%s'", name);
ANIFAIL("rows")
ani->rows = atoi(tok);
if((tok = strtok(NULL, "_")) == NULL)
ANIFAIL("cols")
ani->cols = atoi(tok);
if((tok = strtok(NULL, "_")) == NULL)
ANIFAIL("speed")
ani->speed = atoi(tok);
char refname[strlen(tok)+1];
memset(refname, 0, strlen(tok)+1);
strcpy(refname, tok);
if(ani->speed == 0) {
warnx("load_animation(): animation speed of %s == 0. relativity?", name);
free(basename);
return NULL;
}
buf->tex = load_texture(filename);
buf->w = buf->tex->w/buf->cols;
buf->h = buf->tex->h/buf->rows;
ani->tex = get_tex(basename);
hashtable_set_string(resources.animations, refname, buf);
if(!ani->tex) {
warnx("load_animation(): couldn't get texture '%s'", basename);
free(basename);
return NULL;
}
printf("-- initialized animation '%s'\n", refname);
return buf;
ani->w = ani->tex->w / ani->cols;
ani->h = ani->tex->h / ani->rows;
free(basename);
return (void*)ani;
#undef ANIFAIL
}
Animation *get_ani(const char *name) {
Animation *res = hashtable_get_string(resources.animations, name);
if(res == NULL)
errx(-1,"get_ani():\n!- cannot load animation '%s'", name);
return res;
}
void delete_animations(void) {
resources_delete_and_unset_all(resources.animations, hashtable_iter_free_data, NULL);
return get_resource(RES_ANIM, name, RESF_REQUIRED)->animation;
}
void draw_animation(float x, float y, int row, const char *name) {

View file

@ -24,10 +24,16 @@ typedef struct Animation {
Texture *tex;
} Animation;
Animation *init_animation(const char *filename);
char* animation_path(const char *name);
bool check_animation_path(const char *path);
void* load_animation(const char *filename);
char* animation_name(const char *filename);
Animation *get_ani(const char *name);
void delete_animations(void);
void draw_animation(float x, float y, int row, const char *name);
void draw_animation_p(float x, float y, int row, Animation *ani);
#define ANI_PATH_PREFIX TEX_PATH_PREFIX
#endif

View file

@ -1,219 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
*/
#include "audio.h"