From 31f6147d74258242469a55a8e2e6480c4bd26faf Mon Sep 17 00:00:00 2001 From: dwk Date: Mon, 30 Jul 2007 22:45:04 +0000 Subject: [PATCH] [All of these changes were wrought on the Dell.] Nearly completely wrote the settings-file loading. It is able to determine which section a line belongs to, check for correct variables, and determine the data type of a value (null, int, double, char, string, string_t, etc). Error reporting always indicates which line number messed it up, but the messages are often terse. Implemented a varargs log_printf() (which writes to xuni.log as well as stderr), to make error reporting from settings.c easier. The default settings file can be written out again, but so far that's the only settings-file-writing code. Removed the text-blitting code from paint_box(). It's now much cleaner and does what its name suggests. The text blitting has been moved to paint_centred_text() and paint_textbox_text(). As a result of this, textboxes now display the right size cursor (and not just always 16 pixels tall); they display a cursor even when there is no text in the textbox; and they only display the cursor if the textbox is selected. Also, pressing inside a textbox unselects it. (It's the same as pressing escape right now.) menuhover images are now being loaded from gui/menuhover. This needs to change, or the themes need to go into a separate folder, otherwise you can't have a theme called menuhover. "Load existing game" was changed to "Load saved game". Much effort was put into debugging the app-active bugs. So far only tests have been completed on the Dell, but it seems very random. (This is, however, SDL 1.2.8.) It's been fixed by checking the active status every time an event is received. This is slow, however, and hopefully can be avoided in the future. After another annoying buffer-overrun bug which was fixed by make clean ; make run, the Makefiles were updated and a gccmm target added (plus automm.bat and automm.sh), which will assist in keeping the Makefiles updated. I should really look into automake. --- Makefile | 10 +- Makewin | 10 +- game.c | 4 +- graphics.c | 126 +++++++++++++++++------ graphics.h | 22 ++-- loop.c | 52 +++++++++- menu.c | 14 +-- options.c | 19 +++- settings.c | 340 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- xuni.c | 35 +++++-- xuni.h | 1 + 11 files changed, 540 insertions(+), 93 deletions(-) diff --git a/Makefile b/Makefile index 3ccc936..eb3e2d8 100644 --- a/Makefile +++ b/Makefile @@ -23,9 +23,9 @@ game.o: game.c game.h graphics.h loop.h menu.h xuni.h graphics.o: graphics.c graphics.h settings.h xuni.h loop.o: loop.c game.h graphics.h loop.h menu.h options.h xuni.h menu.o: menu.c graphics.h loop.h menu.h xuni.h -options.o: options.c graphics.h loop.h menu.h options.h xuni.h +options.o: options.c options.h graphics.h loop.h menu.h xuni.h settings.o: settings.c graphics.h settings.h xuni.h -xuni.o: xuni.c graphics.h loop.h menu.h xuni.h +xuni.o: xuni.c graphics.h loop.h menu.h settings.h xuni.h # Other targets run: $(TARGET) @@ -34,8 +34,14 @@ runa: $(TARGET) ./$(TARGET) $(ARGS) debug: $(TARGET) gdb ./$(TARGET) +gccmm: + gcc -MM $(CFLAGS) *.c | perl -pe 's@\s+\S+/\S+@@g' | tr -d \\ \ + | perl -pe 'chomp;print "\n" if(/:/)' | tr -s " " | tee gccmm.out memcheck: valgrind --leak-check=full -v ./$(TARGET) 2>&1\ | tee valgrind.output | less +permissions: + chmod 644 `find -type f` + chmod 755 `find -type d` xuni xuni.exe clean: -rm $(TARGET) $(OBJ) diff --git a/Makewin b/Makewin index df4a0d6..4033c8b 100644 --- a/Makewin +++ b/Makewin @@ -17,16 +17,16 @@ all: $(TARGET) # Executable files #$(CC) $(CFLAGS) -o $(TARGET) $(OBJ) $(CLINK) $(TARGET): $(OBJ) - $(CC) -L$(SDLLIBPATH) -o $(TARGET) $(OBJ) -lSDL_gfx -lSDLmain -lSDL.dll -lSDL -lSDL_image -lSDL_ttf + $(CC) -L$(SDLLIBPATH) -o $(TARGET) *.o -lSDL_gfx -lSDLmain -lSDL.dll -lSDL -lSDL_image -lSDL_ttf # Source files -game.o: game.c game.h graphics.h loop.h xuni.h +game.o: game.c game.h graphics.h loop.h menu.h xuni.h graphics.o: graphics.c graphics.h settings.h xuni.h -loop.o: loop.c loop.h menu.h xuni.h +loop.o: loop.c game.h graphics.h loop.h menu.h options.h xuni.h menu.o: menu.c graphics.h loop.h menu.h xuni.h -options.o: options.c graphics.h loop.h options.h xuni.h +options.o: options.c options.h graphics.h loop.h menu.h xuni.h settings.o: settings.c graphics.h settings.h xuni.h -xuni.o: xuni.c graphics.h menu.h xuni.h +xuni.o: xuni.c graphics.h loop.h menu.h settings.h xuni.h # Other targets run: $(TARGET) diff --git a/game.c b/game.c index c836fd1..dcadf99 100644 --- a/game.c +++ b/game.c @@ -124,14 +124,14 @@ void paint_game(struct game_data_t *data, struct smode_t *smode, /*blit_surface(screen, data->text.menu, (smode->width - data->text.menu->w) / 2, smode->height / 10);*/ - paint_widget_array(smode, theme, data->gui.widget, + paint_widget_array(smode, font, theme, data->gui.widget, sizeof(data->gui.widget)/sizeof(*data->gui.widget)); if(data->mode == GMODE_MENU) { for(x = 0; x < sizeof(data->gui.menu.widget) / sizeof(*data->gui.menu.widget); x ++) { - paint_widget(smode, theme, &data->gui.menu.widget[x]); /* !!! */ + paint_widget(smode, font, theme, &data->gui.menu.widget[x]); /* !!! allow clicking? have a "enabled" member? */ } } diff --git a/graphics.c b/graphics.c index dd4c6c8..87681cf 100644 --- a/graphics.c +++ b/graphics.c @@ -536,11 +536,10 @@ int is_box_active(enum boxtype_t bt) { return bt == BOX_IN_ACTIVE || bt == BOX_OUT_ACTIVE; } -enum boxtype_t paint_box(SDL_Surface *screen, - struct theme_t *theme, SDL_Surface *text, SDL_Rect *b, - enum boxtype_t bt, int centre) { +enum boxtype_t paint_box(SDL_Surface *screen, struct theme_t *theme, + SDL_Rect *b, enum boxtype_t bt) { - int x, y, cw, ch, a; + int /*x, y,*/ cw, ch/*, a*/; /*if(!b->text) return BOX_ERROR;*/ @@ -597,17 +596,18 @@ enum boxtype_t paint_box(SDL_Surface *screen, b->x + theme->boxw, b->y + theme->boxh, cw, ch, theme->boxw, theme->boxh, 1, 1); - + +#if 0 if(text) { if(text->w <= b->w && text->h <= b->h) { /* text */ if(centre) { - x = b->x + theme->boxw + (cw - text->w) / 2; + /*x = b->x + theme->boxw + (cw - text->w) / 2; y = b->y + theme->boxh + (ch - text->h) / 2; if(is_box_in(bt)) x ++, y ++; - blit_surface(screen, text, x, y); + blit_surface(screen, text, x, y);*/ } else { x = b->x + theme->boxh + (ch - text->h) / 2; @@ -630,6 +630,7 @@ enum boxtype_t paint_box(SDL_Surface *screen, print_warning("Text too small for button", __FILE__, __LINE__); */ } } +#endif /*SDL_SaveBMP(theme->box[bt].corners.image, "temp/corners.bmp");*/ } @@ -646,6 +647,55 @@ enum boxtype_t paint_box(SDL_Surface *screen, return bt; } +void paint_centred_text(SDL_Surface *screen, struct theme_t *theme, + SDL_Surface *text, SDL_Rect *b, enum boxtype_t bt) { + + int x, y; + + if(!text) return; + + x = b->x + theme->boxw + ((int)(b->w - theme->boxw * 2) - text->w) / 2; + y = b->y + theme->boxh + ((int)(b->h - theme->boxh * 2) - text->h) / 2; + + if(is_box_in(bt)) x ++, y ++; + + blit_surface(screen, text, x, y); +} + +void paint_textbox_text(SDL_Surface *screen, struct font_t *font, + struct theme_t *theme, SDL_Surface *text, SDL_Rect *b, enum boxtype_t bt, + int cursor) { + + int x, y, a; + int cw, ch; + + cw = b->w - theme->boxw * 2; + ch = b->h - theme->boxh * 2; + + if(text) { + x = b->x + theme->boxh + (ch - text->h) / 2; + y = b->y + theme->boxh + (ch - text->h) / 2; + + a = text->w > cw ? text->w - cw : 0; + + if(is_box_in(bt)) x ++, y ++; + + blit_surface_area(screen, text, x, y, a, 0, text->w - a, text->h); + + x += (text->w - a); + } + else { + x = b->x + theme->boxh; + y = b->y + theme->boxh; + } + + if(cursor) { + y = TTF_FontHeight(font->sans); + vlineRGBA(screen, x + 1, + b->y + (b->h - y) / 2, b->y + (b->h + y) / 2, 255, 255, 255, 255); + } +} + void init_button(struct widget_t *widget, TTF_Font *font, const char *text, int x, int y, int w, int h) { @@ -681,6 +731,8 @@ void init_textbox(struct widget_t *widget, TTF_Font *font, const char *data, static const SDL_Color white = {255, 255, 255, 0}; + /* !!! text isn't wrapped here: there needs to be a function for it */ + widget->type = WIDGET_TEXTBOX; widget->p.textbox = malloc(sizeof(*widget->p.textbox)); widget->p.textbox->data = duplicate_string(data, len); @@ -726,7 +778,7 @@ int set_widget_sel(struct widget_sel_t *sel, int xp, int yp, int click, v = 1; } - if(v && !sel->clickin && !sel->wasin) clear_sel(sel); + if(/*v &&*/ !sel->clickin && !sel->wasin) clear_sel(sel); } if(!sel->p.widget) { x = in_widget_array(xp, yp, widget, n); @@ -762,8 +814,8 @@ int set_widget_sel(struct widget_sel_t *sel, int xp, int yp, int click, return 1;*/ } -enum boxtype_t paint_widget(struct smode_t *smode, struct theme_t *theme, - struct widget_t *widget) { +enum boxtype_t paint_widget(struct smode_t *smode, struct font_t *font, + struct theme_t *theme, struct widget_t *widget) { enum boxtype_t bt = BOX_ERROR, t; SDL_Rect r; @@ -771,8 +823,9 @@ enum boxtype_t paint_widget(struct smode_t *smode, struct theme_t *theme, switch(widget->type) { case WIDGET_BUTTON: t = get_box_type(smode, widget); - bt = paint_box(smode->screen, theme, - widget->p.button->text, &widget->pos, t, 1); + bt = paint_box(smode->screen, theme, &widget->pos, t); + paint_centred_text(smode->screen, theme, widget->p.button->text, + &widget->pos, t); break; case WIDGET_CHECKBOX: r = widget->pos; @@ -780,13 +833,19 @@ enum boxtype_t paint_widget(struct smode_t *smode, struct theme_t *theme, if(!widget->p.checkbox->checked) { t = get_box_type(smode, widget); - bt = paint_box(smode->screen, theme, - t == BOX_IN_ACTIVE ? theme->check.image : 0, &r, t, 1); + bt = paint_box(smode->screen, theme, &r, t); + if(t == BOX_IN_ACTIVE) { + paint_centred_text(smode->screen, theme, + theme->check.image, &r, t); + } } else { t = get_inv_box_type(smode, widget); - bt = paint_box(smode->screen, theme, - t != BOX_OUT_ACTIVE ? theme->check.image : 0, &r, t, 1); + bt = paint_box(smode->screen, theme, &r, t); + if(t != BOX_OUT_ACTIVE) { + paint_centred_text(smode->screen, theme, + theme->check.image, &r, t); + } } blit_surface(smode->screen, widget->p.checkbox->text, r.x + r.w * 1.3, @@ -794,8 +853,9 @@ enum boxtype_t paint_widget(struct smode_t *smode, struct theme_t *theme, break; case WIDGET_TEXTBOX: t = get_perm_in_box_type(smode, widget); - bt = paint_box(smode->screen, theme, - widget->p.textbox->text, &widget->pos, t, 0); + bt = paint_box(smode->screen, theme, &widget->pos, t); + paint_textbox_text(smode->screen, font, theme, widget->p.textbox->text, + &widget->pos, t, smode->active.widget == widget); break; default: break; @@ -841,17 +901,8 @@ int render_restricted_text(SDL_Surface **text, const char *data, } int widget_process_character(struct widget_t *widget, SDL_keysym *sym, - struct font_t *font, struct theme_t *theme) { - - /*if(sym->mod & KMOD_SHIFT) { - if(isalpha(c)) c = toupper(c); - else if(c) { - if((p = strchr(from, c))) { - c = to[p - from]; - } - } - }*/ - + struct smode_t *smode, struct font_t *font, struct theme_t *theme) { + if(!(sym->unicode >> 7)) { if(isprint(sym->unicode)) { switch(widget->type) { @@ -891,6 +942,17 @@ int widget_process_character(struct widget_t *widget, SDL_keysym *sym, return 1; } + else if(sym->unicode == '\r') { + switch(widget->type) { + case WIDGET_TEXTBOX: + clear_active(&smode->active); + break; + default: + break; + } + + return 1; + } } return 0; @@ -910,13 +972,13 @@ void enable_unicode(enum widget_type_t wt) { if(SDL_EnableUNICODE(-1) != v) SDL_EnableUNICODE(v); } -void paint_widget_array(struct smode_t *smode, struct theme_t *theme, - struct widget_t *widget, size_t n) { +void paint_widget_array(struct smode_t *smode, struct font_t *font, + struct theme_t *theme, struct widget_t *widget, size_t n) { size_t x; for(x = 0; x < n; x ++) { - paint_widget(smode, theme, widget + x); + paint_widget(smode, font, theme, widget + x); } } diff --git a/graphics.h b/graphics.h index c0f968a..e0c7f2f 100644 --- a/graphics.h +++ b/graphics.h @@ -126,6 +126,8 @@ struct smode_t { int focus; SDL_GrabMode restrictfocus; + int main_menu_quality; + struct widget_sel_t sel; struct widget_p_t active; }; @@ -167,9 +169,13 @@ int is_box_out(enum boxtype_t bt); int is_box_normal(enum boxtype_t bt); int is_box_hover(enum boxtype_t bt); int is_box_active(enum boxtype_t bt); -enum boxtype_t paint_box(SDL_Surface *screen, - struct theme_t *theme, SDL_Surface *text, SDL_Rect *b, - enum boxtype_t bt, int centre); +enum boxtype_t paint_box(SDL_Surface *screen, struct theme_t *theme, + SDL_Rect *b, enum boxtype_t bt); +void paint_centred_text(SDL_Surface *screen, struct theme_t *theme, + SDL_Surface *text, SDL_Rect *b, enum boxtype_t bt); +void paint_textbox_text(SDL_Surface *screen, struct font_t *font, + struct theme_t *theme, SDL_Surface *text, SDL_Rect *b, enum boxtype_t bt, + int cursor); void init_button(struct widget_t *widget, TTF_Font *font, const char *text, int x, int y, int w, int h); void init_checkbox(struct widget_t *widget, TTF_Font *font, const char *text, @@ -179,15 +185,15 @@ void init_textbox(struct widget_t *widget, TTF_Font *font, const char *data, size_t in_widget_array(int xp, int yp, struct widget_t *widget, size_t n); int set_widget_sel(struct widget_sel_t *sel, int xp, int yp, int click, size_t offset, struct widget_t *widget, size_t n); -enum boxtype_t paint_widget(struct smode_t *smode, struct theme_t *theme, - struct widget_t *widget); +enum boxtype_t paint_widget(struct smode_t *smode, struct font_t *font, + struct theme_t *theme, struct widget_t *widget); void perform_widget_click(struct widget_t *widget); int render_restricted_text(SDL_Surface **text, const char *data, TTF_Font *font, int w); int widget_process_character(struct widget_t *widget, SDL_keysym *sym, - struct font_t *font, struct theme_t *theme); -void paint_widget_array(struct smode_t *smode, struct theme_t *theme, - struct widget_t *widget, size_t n); + struct smode_t *smode, struct font_t *font, struct theme_t *theme); +void paint_widget_array(struct smode_t *smode, struct font_t *font, + struct theme_t *theme, struct widget_t *widget, size_t n); int widget_can_be_active(enum widget_type_t wt); void enable_unicode(enum widget_type_t wt); void free_widget_array(struct widget_t *widget, size_t n); diff --git a/loop.c b/loop.c index f8d6e2f..c58297d 100644 --- a/loop.c +++ b/loop.c @@ -147,6 +147,12 @@ static int process_event(SDL_Event *event, int repaint = 0, handled = 0; + /*puts("Handling event ..."); + + if(event->type == SDL_VIDEORESIZE) printf("!!!*** SDL_VIDEORESIZE event recieved: %i\n", SDL_VIDEORESIZE); + else if(event->type == SDL_VIDEOEXPOSE) printf("SDL_VIDEOEXPOSE event recieved: %i\n", SDL_VIDEOEXPOSE); + else puts("Unhandled event recieved");*/ + switch(event->type) { case SDL_MOUSEBUTTONDOWN: /*smode->button.button = event->button.button; @@ -182,7 +188,7 @@ static int process_event(SDL_Event *event, if(smode->active.widget) { repaint = widget_process_character(smode->active.widget, - &event->key.keysym, font, theme); + &event->key.keysym, smode, font, theme); if(repaint) { handled = 1; break; @@ -219,9 +225,31 @@ static int process_event(SDL_Event *event, printf("keyboard: %s; ", event->active.state & SDL_APPINPUTFOCUS ? temp : " "); printf("active: %s\n", event->active.state & SDL_APPACTIVE ? temp : " ");*/ + printf("SDL_GetAppState() = %i: %i %i %i", SDL_GetAppState(), + SDL_GetAppState() & SDL_APPMOUSEFOCUS, + SDL_GetAppState() & SDL_APPINPUTFOCUS, + SDL_GetAppState() & SDL_APPACTIVE); + + printf(" [%s of", event->active.gain ? "GAIN" : "loss"); + event->active.state & SDL_APPINPUTFOCUS && printf(" input"); + event->active.state & SDL_APPMOUSEFOCUS && printf(" mouse"); + event->active.state & SDL_APPACTIVE && printf(" active"); + putchar(']'); + +#if 0 + if(SDL_GetAppState() & SDL_APPACTIVE + && SDL_GetAppState() & SDL_APPINPUTFOCUS) { + + if(!smode->focus) { + smode->focus = 1; + repaint = 1; + } + } + else smode->focus = 0; +#elif 0 if((event->active.state & SDL_APPACTIVE /*|| event->active.state == SDL_APPINPUTFOCUS*/) - || smode->fullscreen || smode->restrictfocus == SDL_GRAB_ON) { + /*|| smode->fullscreen || smode->restrictfocus == SDL_GRAB_ON*/) { if(event->active.gain) { if(!smode->focus) smode->focus = 1; @@ -231,6 +259,10 @@ static int process_event(SDL_Event *event, smode->focus = 0; } } +#endif + + /*printf(" focus = %i\n", smode->focus);*/ + /*else if(smode->focus) { if(event->active.gain) { smode->focus = 2; @@ -245,6 +277,22 @@ static int process_event(SDL_Event *event, break; } + if(SDL_GetAppState() & SDL_APPACTIVE + && SDL_GetAppState() & SDL_APPINPUTFOCUS) { + + if(!smode->focus) { + puts("Focus gained"); + smode->focus = 1; + repaint = 1; + } + } + else { + if(smode->focus) { + puts("Focus lost"); + smode->focus = 0; + } + } + if(!handled) { if(*mode == MODE_MENU) { repaint = menu_event(mode, event, menu_data, smode, font, theme); diff --git a/menu.c b/menu.c index 3070cc5..5ea08fb 100644 --- a/menu.c +++ b/menu.c @@ -17,20 +17,20 @@ void init_menu(struct menu_data_t *data, struct smode_t *smode, size_t x; /* gui/default/menuhover/X.png\0 */ - char fn[GUI_PATH_LEN + 7 + 1 + 9 + 1 + 1+1+3 + 1] - = GUI_PATH "default/menuhover/"; + char fn[GUI_PATH_LEN + /*7 + 1*/ + 9 + 1 + 1+1+3 + 1] + = GUI_PATH /*"default/"*/"menuhover/"; /*data->text.title = TTF_RenderText_Blended(font->sans, VERSION, white);*/ load_resize_image(&data->title, GUI_PATH "title.png"); - data->frame_based = 0; + data->frame_based = smode->main_menu_quality; for(x = 0; x < sizeof(data->gui.hover)/sizeof(*data->gui.hover); x ++) { - sprintf(fn + GUI_PATH_LEN + 7 + 1 + 9 + 1, "%i.png", (int)x); + sprintf(fn + GUI_PATH_LEN + /*7 + 1*/ + 9 + 1, "%i.png", (int)x); load_resize_image(&data->gui.hover[x], fn); } - load_resize_image(&data->gui.hoverback, GUI_PATH "default/menuhover/back.png"); + load_resize_image(&data->gui.hoverback, GUI_PATH /*"default/"*/"menuhover/back.png"); data->back0.image = load_image(GUI_PATH "back0.png"); data->back1.image = load_image(GUI_PATH "back1.png"); @@ -58,7 +58,7 @@ static void resize_menu_images(struct menu_data_t *data, init_button(&data->gui.widget[1], font->sans, "Join multi-player game", smode->width / 16, smode->height * .7 + smode->height / 12, smode->width / 3, smode->height / 15); - init_button(&data->gui.widget[2], font->sans, "Load existing game", + init_button(&data->gui.widget[2], font->sans, "Load saved game", smode->width / 16, smode->height * .7 + smode->height / 12 * 2, smode->width / 3, smode->height / 15); @@ -253,7 +253,7 @@ void paint_menu(struct menu_data_t *data, struct smode_t *smode, smode->height * .69); for(x = 0; x < sizeof(data->gui.widget)/sizeof(*data->gui.widget); x ++) { - switch(paint_widget(smode, theme, &data->gui.widget[x])) { + switch(paint_widget(smode, font, theme, &data->gui.widget[x])) { case BOX_OUT_HOVER: case BOX_IN_ACTIVE: blit_surface(smode->screen, data->gui.hover[x].image, diff --git a/options.c b/options.c index 6cfaffc..21b4560 100644 --- a/options.c +++ b/options.c @@ -101,6 +101,8 @@ static void resize_theme_fontsample(struct options_data_t *data, void start_options(struct options_data_t *data) { set_caption("Options and preferences"); + + data->mode = OMODE_NORMAL; } int options_event(enum loop_mode_t *mode, SDL_Event *event, @@ -279,16 +281,16 @@ void paint_options(struct options_data_t *data, struct smode_t *smode, SDL_MapRGB(smode->screen->format, 0, 0, 0)); if(data->mode == OMODE_NORMAL) { - paint_widget_array(smode, theme, data->gui.widget, + paint_widget_array(smode, font, theme, data->gui.widget, sizeof(data->gui.widget) / sizeof(*data->gui.widget)); } else if(data->mode == OMODE_GRAPHICS) { - paint_widget_array(smode, theme, data->gui.graphics.widget, + paint_widget_array(smode, font, theme, data->gui.graphics.widget, sizeof(data->gui.graphics.widget) / sizeof(*data->gui.graphics.widget)); } else if(data->mode == OMODE_THEME) { - paint_widget_array(smode, theme, data->gui.theme.widget, + paint_widget_array(smode, font, theme, data->gui.theme.widget, sizeof(data->gui.theme.widget) / sizeof(*data->gui.theme.widget)); @@ -304,12 +306,21 @@ static void paint_theme_fontsample(struct smode_t *smode, struct options_data_t *data) { size_t x; + int maxw = 0; for(x = 0; x < sizeof(data->gui.theme.fontsample) / sizeof(*data->gui.theme.fontsample); x ++) { + + if(data->gui.theme.fontsample[x]->w > maxw) { + maxw = data->gui.theme.fontsample[x]->w; + } + } + + for(x = 0; x < sizeof(data->gui.theme.fontsample) + / sizeof(*data->gui.theme.fontsample); x ++) { blit_surface(smode->screen, data->gui.theme.fontsample[x], - (smode->width - data->gui.theme.fontsample[x]->w) / 2, + (smode->width - maxw) / 2, 200 + data->gui.theme.fontsample[x]->h * x); } } diff --git a/settings.c b/settings.c index d4b3d68..30df13b 100644 --- a/settings.c +++ b/settings.c @@ -1,4 +1,6 @@ #include +#include +#include #include #include "graphics.h" #include "settings.h" @@ -8,7 +10,8 @@ static const char *default_settings[] = { "# Settings for " VERSION, "", "# Main menu", - "mainmenu.quality = 'static' # One of: 'static', 'scrolling', or 'parallax'", + "mainmenu.quality = \"static\" # One of: \"static\", \"scrolling\", or " + "\"parallax\"", "", "# Screen mode", "screen.x = 640", @@ -31,7 +34,28 @@ enum section_t { SECTION_MAINMENU, SECTION_SCREEN, SECTION_FONT, - SECTION_THEME + SECTION_THEME, + SECTIONS +}; + +enum sec_data_type_t { + SECTYPE_ERROR = -1, + SECTYPE_NULL, + SECTYPE_INT, + SECTYPE_FLOAT, + SECTYPE_CHAR, + SECTYPE_STRING +}; + +struct sec_data_t { + enum sec_data_type_t type; + + union { + long i; + double f; + char c; + const char *s; + } p; }; static void default_smode(struct smode_t *smode); @@ -39,8 +63,16 @@ static void default_font(struct smode_t *smode, struct font_t *font); static void default_theme(struct smode_t *smode, struct theme_t *theme); static int load_settings_file(struct smode_t *smode, struct font_t *font, struct theme_t *theme); -static enum section_t find_section(const char *p, const char *q); +static void parse_line(struct smode_t *smode, struct font_t *font, + struct theme_t *theme, char *p, int count); +static enum section_t find_section(const char *p); +static void get_value(struct sec_data_t *data, char *begin, char *end, + int line); +static void handle_expression(struct smode_t *smode, struct font_t *font, + struct theme_t *theme, enum section_t section, const char *var, + struct sec_data_t *data, int line); static char *skip_ws(char *p); +static char *before_ws_end(char *p); static char *skip_alnum(char *p); static void write_default_settings_file(struct smode_t *smode, struct font_t *font, struct theme_t *theme); @@ -53,6 +85,8 @@ static void default_smode(struct smode_t *smode) { smode->fullscreen = 0; smode->focus = 2; /* !!! might not be true in windowed mode, not that the main menu cares */ smode->restrictfocus = SDL_GRAB_OFF; + + smode->main_menu_quality = 0; clear_smode(smode); } @@ -124,9 +158,9 @@ static int load_settings_file(struct smode_t *smode, struct font_t *font, struct theme_t *theme) { FILE *fp = fopen(SETTINGS_FILE, "rt"); - char *line = 0, *p, *q; - enum section_t section; + char *line = 0; size_t len = 0, alen = 0, tlen; + unsigned long count = 0; if(!fp) { print_warning("Creating settings file \"" SETTINGS_FILE "\"", @@ -136,20 +170,9 @@ static int load_settings_file(struct smode_t *smode, struct font_t *font, while((tlen = get_string(&line, len, &alen, fp)) != len) { tlen = len; + count ++; - if((p = strpbrk(line, "#\n"))) *p = 0; - - p = skip_ws(line); - if(*p) { - q = skip_alnum(p); - section = find_section(p, q); - - p = skip_ws(q); - if(*p == '.') { - p = skip_ws(++p); - q = skip_alnum(p); - } - } + parse_line(smode, font, theme, line, count); } fclose(fp); @@ -157,25 +180,298 @@ static int load_settings_file(struct smode_t *smode, struct font_t *font, return 0; } -static enum section_t find_section(const char *p, const char *q) { +static void parse_line(struct smode_t *smode, struct font_t *font, + struct theme_t *theme, char *p, int count) { + + char *q, *r, *t; + enum section_t section; + struct sec_data_t data; + + if((t = strpbrk(p, "#\n"))) *t = 0; /* Strip comments and newlines. */ + + /* Explanatory comments refer to an imaginary line: + section.variable = value + */ + + p = skip_ws(p); + if(*p) { /* the line isn't empty */ + q = skip_alnum(p); /* skip to the end of "section" */ + + q = skip_ws(q); + if(*q == '.') { + *q = 0; + section = find_section(p); + + p = skip_ws(q + 1); + q = skip_alnum(p); /* skip to the end of "variable" */ + r = skip_ws(q); + if(*r == '=' || *r == ':') { + *q = 0; + r = skip_ws(r + 1); /* skip to the beginning of "value" */ + t = before_ws_end(r); + if(*t) *(t + 1) = 0; /* remove trailing whitespace */ + + get_value(&data, r, t, count); + + if(data.type != SECTYPE_ERROR) { + /* the line is syntactically valid */ + handle_expression(smode, font, theme, section, p, &data, + count); + } + } + else { + settings_error("Invalid assignment character in settings file", + SETTINGS_FILE, count); + } + } + else { + settings_error("Missing '.' separator", SETTINGS_FILE, count); + } + } +} + +static enum section_t find_section(const char *p) { const char *section[] = { "mainmenu", "screen", "font", "theme" }; enum section_t x; for(x = 0; x < (enum section_t)(sizeof(section)/sizeof(*section)); x ++) { - if(!strncmp(section[x], p, q-p)) return x; + if(!strcmp(section[x], p)) return x; } return SECTION_ERROR; } +static void get_value(struct sec_data_t *data, char *begin, char *end, + int line) { + + char *numend; + + data->type = SECTYPE_ERROR; + + if(!*begin) { + data->type = SECTYPE_NULL; + } + else if(*begin == '"' || *begin == '\'') { + if(end == begin || *end != *begin) { + settings_error("Unterminated character literal", + SETTINGS_FILE, line); + } + + if(*begin == '\'') { + if(begin + 2 == end) { + data->type = SECTYPE_CHAR; + data->p.c = *(begin + 1); + } + else { + settings_error("Single quotes around multiple characters", + SETTINGS_FILE, line); + } + } + else { + *end = 0; + + data->type = SECTYPE_STRING; + data->p.s = begin + 1; + } + } + else { + if(!strcmp(begin, "true") || !strcmp(begin, "yes") + || !strcmp(begin, "on")) { + + data->type = SECTYPE_INT; + data->p.i = 1; + } + else if(!strcmp(begin, "false") || !strcmp(begin, "no") + || !strcmp(begin, "off")) { + + data->type = SECTYPE_INT; + data->p.i = 0; + } + else { + if(strchr(begin, '.')) { + data->type = SECTYPE_FLOAT; + data->p.f = strtod(begin, &numend); + } + else { + data->type = SECTYPE_INT; + data->p.i = strtol(begin, &numend, 0); + } + + if(*numend) { + settings_error("Excess characters after number", + SETTINGS_FILE, line); + data->type = SECTYPE_ERROR; + } + } + } +} + +static void handle_expression(struct smode_t *smode, struct font_t *font, + struct theme_t *theme, enum section_t section, const char *var, + struct sec_data_t *data, int line) { + + /* Meaning of letters: + b: bool (stored in an int) + s: string + t: string_t + i: int + u: unsigned int + */ + struct { + enum section_t type; + const char *name; + int *assign; + struct { + const char *from; + int to; + } value[3]; + } data_si_3[] = { + { + SECTION_MAINMENU, "quality", 0, + { + {"static", 0}, + {"scrolling", 1}, + {"parallax", 2} + } + } + }; + + struct { + enum section_t type; + const char *name; + struct string_t *assign; + } data_st[] = { + { + SECTION_FONT, "normal", 0 + }, + { + SECTION_FONT, "special", 0 + }, + { + SECTION_THEME, "name", 0 + } + }; + + struct { + enum section_t type; + const char *name; + int *assign; + } data_ui[] = { + { + SECTION_SCREEN, "x", 0 + }, + { + SECTION_SCREEN, "y", 0 + }, + { + SECTION_SCREEN, "bpp", 0 + } + }; + + struct { + enum section_t type; + const char *name; + int *assign; + } data_bi[] = { + { + SECTION_SCREEN, "fullscreen", 0 + }, + { + SECTION_SCREEN, "restrict", 0 + } + }; + + size_t x, y; + + data_si_3[0].assign = &smode->main_menu_quality; + + data_ui[0].assign = &smode->width; + data_ui[1].assign = &smode->height; + data_ui[2].assign = &smode->depth; + + data_st[0].assign = &font->sansname; + data_st[1].assign = &font->mononame; + data_st[2].assign = &theme->name; + + data_bi[0].assign = &smode->fullscreen; + data_bi[1].assign = &smode->restrictfocus; + + if(data->type == SECTYPE_STRING) { + for(x = 0; x < sizeof(data_si_3)/sizeof(*data_si_3); x ++) { + if(data_si_3[x].type == section + && !strcmp(data_si_3[x].name, var)) { + + for(y = 0; y < 3; y ++) { + if(!strcmp(data_si_3[x].value[y].from, data->p.s)) { + *data_si_3[x].assign = data_si_3[x].value[y].to; + return; + } + } + + settings_error("Invalid value for setting", + SETTINGS_FILE, line); + return; + } + } + + for(x = 0; x < sizeof(data_st)/sizeof(*data_st); x ++) { + if(data_st[x].type == section && !strcmp(data_st[x].name, var)) { + init_string(data_st[x].assign, data->p.s); + return; + } + } + } + + if(data->type == SECTYPE_INT) { + for(x = 0; x < sizeof(data_ui)/sizeof(*data_ui); x ++) { + if(data_ui[x].type == section && !strcmp(data_ui[x].name, var)) { + if(data->p.i >= 0) { + *data_ui[x].assign = data->p.i; + return; + } + + settings_error("Invalid value for setting", + SETTINGS_FILE, line); + return; + } + } + + for(x = 0; x < sizeof(data_bi)/sizeof(*data_bi); x ++) { + if(data_bi[x].type == section && !strcmp(data_bi[x].name, var)) { + if(data->p.i == 0 || data->p.i == 1) { + *data_bi[x].assign = data->p.i; + return; + } + + settings_error("Invalid value for setting", + SETTINGS_FILE, line); + return; + } + } + } + + settings_error("Unknown variable", SETTINGS_FILE, line); +} + static char *skip_ws(char *p) { while(isspace(*p)) p ++; return p; } +static char *before_ws_end(char *p) { + char *q = p; + + while(*q) q ++; + if(q != p) q --; + + while(q != p && isspace(*q)) q --; + + return q; +} + static char *skip_alnum(char *p) { while(isalnum(*p)) p ++; @@ -185,7 +481,7 @@ static char *skip_alnum(char *p) { void write_settings(struct smode_t *smode, struct font_t *font, struct theme_t *theme) { - write_default_settings_file(smode, font, theme); + /*write_default_settings_file(smode, font, theme);*/ } static void write_default_settings_file(struct smode_t *smode, @@ -215,7 +511,7 @@ void init_string(struct string_t *string, const char *data) { } void init_string_len(struct string_t *string, const char *data, size_t len) { - string->len = strlen(data); + string->len = len; string->data = malloc(string->len + 1); strcpy(string->data, data); } diff --git a/xuni.c b/xuni.c index b9f68ea..f47022c 100644 --- a/xuni.c +++ b/xuni.c @@ -1,5 +1,6 @@ #include #include +#include #include #include "SDL.h" #include "graphics.h" @@ -9,6 +10,7 @@ #include "xuni.h" static const char *get_ctime(void); +static void log_printf(const char *format, ...); static void print_message(const char *type, const char *message, const char *file, int line); @@ -53,25 +55,40 @@ static const char *get_ctime(void) { return ctime(&tt); } +static void log_printf(const char *format, ...) { + FILE *log = fopen(LOG_FILE, "at"); + va_list arg; + + va_start(arg, format); + + vfprintf(stderr, format, arg); + vfprintf(log, format, arg); + + va_end(arg); + + fclose(log); +} + static void print_message(const char *type, const char *message, const char *file, int line) { const char *sdlerror = SDL_GetError(), *t = get_ctime(); - FILE *log = fopen(LOG_FILE, "at"); - - fprintf(stderr, "xuni (%s:%i): %s at %s %s\n SDL: %s\n", - file, line, type, t, message, sdlerror); - if(log) { - fprintf(log, "xuni (%s:%i): %s at %s %s\n SDL: %s\n", - file, line, type, t, message, sdlerror); - fclose(log); - } + + log_printf("xuni: %s from %s:%i at %s %s\n SDL: %s\n", + type, file, line, t, message, sdlerror); } void print_warning(const char *message, const char *file, int line) { print_message("Warning", message, file, line); } +void settings_error(const char *message, const char *sfile, int sline) { + const char *t = get_ctime(); + + log_printf("xuni: Error in settings file %s:%i at %s %s\n", + sfile, sline, t, message); +} + void fatal_error(const char *message, const char *file, int line) { print_message("Fatal error", message, file, line); diff --git a/xuni.h b/xuni.h index db5f581..f8ffe12 100644 --- a/xuni.h +++ b/xuni.h @@ -3,6 +3,7 @@ char *duplicate_string(const char *str, size_t len); void print_warning(const char *message, const char *file, int line); +void settings_error(const char *message, const char *sfile, int sline); void fatal_error(const char *message, const char *file, int line); #endif -- 2.11.4.GIT