From 14a81b0f2a739be835057aa33c0e5d10cdc4810d Mon Sep 17 00:00:00 2001 From: Sara Date: Sat, 15 Jul 2023 20:15:25 +0200 Subject: [PATCH 1/9] removed preset initialization functions --- src/game.c | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/src/game.c b/src/game.c index 37f1f8e..9c5d0ed 100644 --- a/src/game.c +++ b/src/game.c @@ -7,35 +7,8 @@ #include "SDL2/SDL_mouse.h" #include "SDL2/SDL_scancode.h" -void on_quit_key(int down) { - // Q is pressed or released - if(down) { - // stop running when Q is pressed - g_context.running = 0; - } -} - -void on_horizontal(int axis) { - // when A or D is pressed or released -} - -void on_vertical(int axis) { - // W or S is pressed or released -} - -void on_click(int down) { - // when the left mouse button is pressed down - // float mouse_x, mouse_y; - // mouse_world_position(&mouse_x, &mouse_y); -} - void start_game() { // called when the game is done initializing the backend and ready to start - g_active_view.width = 21; - add_key_listener(SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_Q, &on_quit_key); - add_key_listener(SDL_SCANCODE_S, SDL_SCANCODE_W, &on_horizontal); - add_key_listener(SDL_SCANCODE_A, SDL_SCANCODE_D, &on_vertical); - add_mouse_button_listener(SDL_BUTTON_LMASK, &on_click); } void update_game() { @@ -48,4 +21,6 @@ void update_ui() { // render calls made in this function will be in *screen* space } -void game_exit() {} +void game_exit() { + // called when the game shuts down +} From 33c1a5e9d24c542e05e2c7ee6067108682d4a200 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 16 Jul 2023 00:31:13 +0200 Subject: [PATCH 2/9] changed the way objects are notified of destruction, simplified game.c preset --- src/corelib/object.c | 9 ++++----- src/corelib/object.h | 6 +++--- src/corelib/physics.c | 2 +- src/corelib/render.h | 1 - src/corelib/world.c | 17 +++++++++++++++-- src/game.c | 2 -- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/corelib/object.c b/src/corelib/object.c index 5664fbc..93ecb72 100644 --- a/src/corelib/object.c +++ b/src/corelib/object.c @@ -3,11 +3,10 @@ object_t object_default() { return (object_t){ .active = 1, - .enabled = 1, - .physics = physics_default(), - .evt_draw = &object_draw_sprite, - .evt_update = NULL, - .sprite = sprite_default(), + .physics = physics_default(), + .evt_draw = &object_draw_sprite, + .evt_update = NULL, + .sprite = sprite_default(), }; } diff --git a/src/corelib/object.h b/src/corelib/object.h index ea38c6e..09a78f9 100644 --- a/src/corelib/object.h +++ b/src/corelib/object.h @@ -11,8 +11,8 @@ typedef void(*draw_fn)(struct object_t*); struct object_t { sprite_t sprite; - int active; // 1 if this object is in use and should not be overriden. - int enabled; // 1 if this object's events should be triggered. + int active; // 1 if this object's events should be triggered by the game world update. + int wants_to_be_deleted; physics_t physics; // the collider to use for this object's physics interaction. uintptr_t timer; // free to use for whatever @@ -26,7 +26,7 @@ object_t object_default(); void object_draw_sprite(object_t* object); static inline int object_is_valid(const object_t* object) { - return object != NULL && object->active != 0; + return object != NULL && object->wants_to_be_deleted == 0; } #endif /* _object_h */ diff --git a/src/corelib/physics.c b/src/corelib/physics.c index 6ac2844..f9ee2b4 100644 --- a/src/corelib/physics.c +++ b/src/corelib/physics.c @@ -24,7 +24,7 @@ void object_broadcast_collision(object_t* this, object_t* other) { } } short can_collide(const object_t* this) { - return object_is_valid(this) && this->enabled; + return object_is_valid(this); } static inline diff --git a/src/corelib/render.h b/src/corelib/render.h index d2fc2e4..6b32700 100644 --- a/src/corelib/render.h +++ b/src/corelib/render.h @@ -104,7 +104,6 @@ extern sprite_t make_sprite(const char* file, float x, float y); extern sprite_t sprite_from_spritesheet(spritesheet_t* sheet, int index); extern text_style_t make_text_style(const char* font, SDL_Color color, int dpi, float size); extern SDL_Rect get_srcrect_from(spritesheet_t* sheet, int index); -extern void set_active_view(const view_t* view); #define no_sprite (sprite_t){NULL, 0.0, 0.0, {0.0, 0.0}, 0.0, 0.0, 0.0, 0, {0, 0, 0, 0}} diff --git a/src/corelib/world.c b/src/corelib/world.c index 7eb4ddb..4a91a23 100644 --- a/src/corelib/world.c +++ b/src/corelib/world.c @@ -47,23 +47,36 @@ size_t _find_free_object() { } void world_clear() { + // free all allocated objects for(int i = 0; i < _world_objects.num; ++i) { - _world_objects.objects[i]->active = 0; - _world_objects.objects[i]->enabled = 0; + object_t* object = _world_objects.objects[i]; + if(object != NULL) { + free(object); + } } + // free world array + free(_world_objects.objects); + // reset world objects struct + _world_objects.objects = NULL; + _world_objects.num = 0; } object_t* make_object() { + // acquire pointer to empty slot size_t index = _find_free_object(); + // allocate new object if(_world_objects.objects[index] == NULL) { _world_objects.objects[index] = malloc(sizeof(object_t)); } + // initialize object to default object_t* o = _world_objects.objects[index]; *o = object_default(); + return o; } object_t* instantiate_object(const object_t *original) { + // create new object with default settings object_t* obj = make_object(); *obj = *original; obj->active = 1; diff --git a/src/game.c b/src/game.c index 9c5d0ed..2d1f110 100644 --- a/src/game.c +++ b/src/game.c @@ -4,8 +4,6 @@ #include "corelib/render.h" #include "corelib/assets.h" #include "corelib/layers.h" -#include "SDL2/SDL_mouse.h" -#include "SDL2/SDL_scancode.h" void start_game() { // called when the game is done initializing the backend and ready to start From 6085dbaed0dfbd7864d639a23e250f45389e97c6 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 16 Jul 2023 00:31:20 +0200 Subject: [PATCH 3/9] added scene.h --- src/corelib/scene.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/corelib/scene.h diff --git a/src/corelib/scene.h b/src/corelib/scene.h new file mode 100644 index 0000000..164b3ed --- /dev/null +++ b/src/corelib/scene.h @@ -0,0 +1,9 @@ +#ifndef _corelib_scene_h +#define _corelib_scene_h + +#include "memory.h" + + + +#endif /* _corelib_scene_h */ + From f0d76713c28e10d773e98a0d41c4f681f6dce820 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 16 Jul 2023 00:38:06 +0200 Subject: [PATCH 4/9] renamed function delegates ending with _t to end with _fn --- src/corelib/assets.c | 2 +- src/corelib/assets.h | 8 ++++---- src/corelib/input.c | 8 ++++---- src/corelib/input.h | 24 ++++++++++++------------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/corelib/assets.c b/src/corelib/assets.c index 537f864..2c32ed2 100644 --- a/src/corelib/assets.c +++ b/src/corelib/assets.c @@ -18,7 +18,7 @@ static resource_t* insert_asset(const resource_t* resource) { return inserted; } -void add_arbitrary_asset(void* memory, deleter_t deleter) { +void add_arbitrary_asset(void* memory, deleter_fn deleter) { char name_buf[99]; // generate the name of the arbitrary block based on adresses sprintf(name_buf, "%p%p", memory, deleter); diff --git a/src/corelib/assets.h b/src/corelib/assets.h index 89360ef..85a727a 100644 --- a/src/corelib/assets.h +++ b/src/corelib/assets.h @@ -17,7 +17,7 @@ typedef enum resourcetype_t { RESOURCETYPE_MAX } resourcetype_t; -typedef void(*deleter_t)(void* target); +typedef void(*deleter_fn)(void* target); typedef struct resource_t { resourcetype_t type; @@ -31,13 +31,13 @@ typedef struct resource_t { } font; struct { void* memory; - deleter_t deleter; + deleter_fn deleter; } arbitrary_type; }; } resource_t; -extern void add_arbitrary_asset(void* memory, deleter_t deleter); -extern void add_arbitrary_asset_by_name(void* memory, deleter_t deleter, const char* name); +extern void add_arbitrary_asset(void* memory, deleter_fn deleter); +extern void add_arbitrary_asset_by_name(void* memory, deleter_fn deleter, const char* name); extern SDL_Texture* load_texture(const char* file); extern TTF_Font* load_font(const char* file, int size); extern SDL_Texture* get_texture(const char* file); diff --git a/src/corelib/input.c b/src/corelib/input.c index df5c666..fd1e771 100644 --- a/src/corelib/input.c +++ b/src/corelib/input.c @@ -17,7 +17,7 @@ static uint32_t _mouse_left_seconds = 0; static float _scroll_delta = 0; void add_key_listener(SDL_Scancode negative, SDL_Scancode positive, - input_axis_delegate_t delegate) { + input_axis_fn delegate) { memset(g_key_listeners_endptr, 0x0, sizeof(input_listener_t)); g_key_listeners_endptr->type = INPUT_LISTENER_AXIS; g_key_listeners_endptr->axis.delegate = delegate; @@ -28,14 +28,14 @@ void add_key_listener(SDL_Scancode negative, SDL_Scancode positive, ++g_key_listeners_endptr; } -void add_mouse_listener(input_mouse_delegate_t delegate) { +void add_mouse_listener(input_mouse_fn delegate) { memset(g_key_listeners_endptr, 0x0, sizeof(input_listener_t)); g_key_listeners_endptr->type = INPUT_LISTENER_MOUSE; g_key_listeners_endptr->mouse.delegate = delegate; ++g_key_listeners_endptr; } -void add_mouse_button_listener(uint32_t button, input_button_delegate_t delegate) { +void add_mouse_button_listener(uint32_t button, input_button_fn delegate) { memset(g_key_listeners_endptr, 0x0, sizeof(input_listener_t)); g_key_listeners_endptr->type = INPUT_LISTENER_BUTTON; g_key_listeners_endptr->button.delegate = delegate; @@ -44,7 +44,7 @@ void add_mouse_button_listener(uint32_t button, input_button_delegate_t delegate ++g_key_listeners_endptr; } -void add_scroll_listener(input_scroll_delegate_t delegate) { +void add_scroll_listener(input_scroll_fn delegate) { memset(g_key_listeners_endptr, 0x0, sizeof(input_listener_t)); g_key_listeners_endptr->type = INPUT_LISTENER_SCROLL; g_key_listeners_endptr->scroll.delegate = delegate; diff --git a/src/corelib/input.h b/src/corelib/input.h index 9be54d6..aadc16c 100644 --- a/src/corelib/input.h +++ b/src/corelib/input.h @@ -7,11 +7,6 @@ extern "C" { #include "SDL2/SDL.h" -typedef void(*input_axis_delegate_t)(int value); -typedef void(*input_mouse_delegate_t)(float dx, float dy); -typedef void(*input_button_delegate_t)(int down); -typedef void(*input_scroll_delegate_t)(float delta); - enum INPUT_LISTENER_TYPE_T { INPUT_LISTENER_MOUSE, INPUT_LISTENER_AXIS, @@ -40,23 +35,28 @@ typedef struct input_listener_t { } scroll; }; } input_listener_t; +typedef void(*input_axis_fn)(int value); +typedef void(*input_mouse_fn)(float dx, float dy); +typedef void(*input_button_fn)(int down); +typedef void(*input_scroll_fn)(float delta); extern const Uint8* g_key_states; extern void add_key_listener(SDL_Scancode negative, SDL_Scancode positive, - input_axis_delegate_t delegate); -extern void add_mouse_listener(input_mouse_delegate_t delegate); -extern void add_mouse_button_listener(uint32_t button, input_button_delegate_t delegate); -extern void add_scroll_listener(input_scroll_delegate_t delegate); -extern void remove_listener_at(size_t index); + input_axis_fn delegate); +extern void add_mouse_listener(input_mouse_fn delegate); +extern void add_mouse_button_listener(uint32_t button, input_button_fn delegate); +extern void add_scroll_listener(input_scroll_fn delegate); + extern void mouse_screen_position(float* ox, float* oy); extern void mouse_world_position(float* ox, float* oy); +extern int input_get_keydown(SDL_Scancode scancode); +extern int input_get_mousedown(int mousebtn); + extern void input_init(); extern void update_input(); extern void input_notify_event(SDL_Event event); -extern int input_get_keydown(SDL_Scancode scancode); -extern int input_get_mousedown(int mousebtn); #ifdef __cplusplus } From f9236e418228a0c855fd1b5a2c815da5ff13aa14 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 16 Jul 2023 00:38:36 +0200 Subject: [PATCH 5/9] moved definitions of INPUT_LISTENER_TYPE_T and input_listener_t to input.c --- src/corelib/input.c | 28 ++++++++++++++++++++++++++++ src/corelib/input.h | 28 ---------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/corelib/input.c b/src/corelib/input.c index fd1e771..ab769cc 100644 --- a/src/corelib/input.c +++ b/src/corelib/input.c @@ -6,6 +6,34 @@ #include "SDL2/SDL_render.h" #include +enum INPUT_LISTENER_TYPE_T { + INPUT_LISTENER_MOUSE, + INPUT_LISTENER_AXIS, + INPUT_LISTENER_SCROLL, + INPUT_LISTENER_BUTTON, +}; + +typedef struct input_listener_t { + enum INPUT_LISTENER_TYPE_T type; + union { + struct { + input_axis_fn delegate; + SDL_Scancode positive, negative; + int last_positive, last_negative; + } axis; + struct { + input_mouse_fn delegate; + } mouse; + struct { + input_button_fn delegate; + uint32_t button; + int last; + } button; + struct { + input_scroll_fn delegate; + } scroll; + }; +} input_listener_t; const Uint8* g_key_states = NULL; input_listener_t g_key_listeners[24]; diff --git a/src/corelib/input.h b/src/corelib/input.h index aadc16c..3cca76d 100644 --- a/src/corelib/input.h +++ b/src/corelib/input.h @@ -7,34 +7,6 @@ extern "C" { #include "SDL2/SDL.h" -enum INPUT_LISTENER_TYPE_T { - INPUT_LISTENER_MOUSE, - INPUT_LISTENER_AXIS, - INPUT_LISTENER_SCROLL, - INPUT_LISTENER_BUTTON, -}; - -typedef struct input_listener_t { - enum INPUT_LISTENER_TYPE_T type; - union { - struct { - input_axis_delegate_t delegate; - SDL_Scancode positive, negative; - int last_positive, last_negative; - } axis; - struct { - input_mouse_delegate_t delegate; - } mouse; - struct { - input_button_delegate_t delegate; - uint32_t button; - int last; - } button; - struct { - input_scroll_delegate_t delegate; - } scroll; - }; -} input_listener_t; typedef void(*input_axis_fn)(int value); typedef void(*input_mouse_fn)(float dx, float dy); typedef void(*input_button_fn)(int down); From 351c9ba713bae57a1e6e18e11dc3bba028919b24 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 16 Jul 2023 00:38:44 +0200 Subject: [PATCH 6/9] removed undefined declaration of physics_update --- src/corelib/physics.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/corelib/physics.h b/src/corelib/physics.h index 5a00e40..9cbe73f 100644 --- a/src/corelib/physics.h +++ b/src/corelib/physics.h @@ -34,15 +34,9 @@ typedef struct physics_t { } physics_t; extern physics_t physics_default(); - extern void object_broadcast_evt_collision(object_t* this, object_t* other); - -extern void physics_update(); - extern void physics_move(object_t* this, float delta_time); - extern short can_collide(const object_t* this); - extern void solve_collision_slide(object_t* left, object_t* right); extern float get_solve_force(const object_t* this, const object_t* other, float* solve_x, float* solve_y); From 25c5f83fe444b3b5878fdd947aa43374dc446e4b Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 16 Jul 2023 02:23:15 +0200 Subject: [PATCH 7/9] added scene parsing --- src/corelib/scene.c | 160 ++++++++++++++++++++++++++++++++++++++++++++ src/corelib/scene.h | 13 ++++ 2 files changed, 173 insertions(+) create mode 100644 src/corelib/scene.c diff --git a/src/corelib/scene.c b/src/corelib/scene.c new file mode 100644 index 0000000..c2ce3e9 --- /dev/null +++ b/src/corelib/scene.c @@ -0,0 +1,160 @@ +#include "scene.h" + +#include "ctype.h" +#include "stdint.h" +#include "stddef.h" +#include "stdlib.h" +#include "string.h" +#include "stdio.h" +#include "hash.h" +#include "malloc.h" +#include "math.h" +#include "world.h" + +static +struct type_handler_t { + uintptr_t hash; + char* type; + type_handler_fn handler; +} _type_handlers[99]; + +static +int _type_handler_num = 0; + +static +struct type_handler_t* _find_handler_for(const char* type) { + uintptr_t hash = hashstr(type); + for(int i = 0; i < 99; ++i) { + if(_type_handlers[i].hash == hash && strcmp(type, _type_handlers[i].type) == 0) { + return _type_handlers + i; + } + } + return NULL; +} + +static +struct type_handler_t* _new_handler_for(const char* type) { + _type_handlers[_type_handler_num].type = malloc(strlen(type) * sizeof(char)); + strcpy(_type_handlers[_type_handler_num].type, type); + _type_handlers[_type_handler_num].hash = hashstr(type); + struct type_handler_t* ptr = _type_handlers + _type_handler_num; + ++_type_handler_num; + return ptr; +} + +void set_type_handler(const char* type, type_handler_fn handler) { + struct type_handler_t* ptr = _find_handler_for(type); + if(ptr == NULL) { + ptr = _new_handler_for(type); + } + ptr->handler = handler; +} + +static +int fpeekc(FILE* file) { + int c = fgetc(file); + ungetc(c, file); + return c; +} + +static +int nextnw(FILE* file) { + int next; + char c; + do { + next = fgetc(file); + c = (char)next; + } while(isspace(c)); + return next; +} + +static +void _parse_key(FILE* file, char* out) { + char c; + do { + c = fgetc(file); + if(c == ':') { + *out = '\0'; + } else if(!isspace(c)) { + *out = c; + ++out; + } + } while(c != ':'); +} + +static +void _parse_value(FILE* file, char* out, int* _argc, char** argv) { + char c; + int argc = 0; + argv[argc] = out; + ++argc; + do { + c = fgetc(file); + switch(c) { + case ';': + *out = '\0'; + break; + case ',': + *out = '\0'; + ++out; + argv[argc] = out; + ++argc; + ungetc(nextnw(file), file); + break; + default: + *out = c; + ++out; + break; + } + } while(c != ';'); + *_argc = argc; +} + +static +void _parse_config(FILE* file) { + char key[24]; + char value[128]; + int argc = 0; + char* argv[24]; + + char begin = nextnw(file); + ungetc(begin, file); + + _parse_key(file, key); + ungetc(nextnw(file), file); + _parse_value(file, value, &argc, argv); + + struct type_handler_t* handler = _find_handler_for(key); + + if(handler != NULL) { + printf("found handler\n"); + if(begin == '!') { + handler->handler(NULL, argc, argv); + } else { + handler->handler(make_object(), argc, argv); + } + } + if(fpeekc(file) == EOF) return; +} + +static +void _parse_scene(const char* filename) { + FILE* file = fopen(filename, "r"); + int next; + do { + _parse_config(file); + next = nextnw(file); + ungetc(next, file); + } while(next != EOF); +} + +void load_scene(const char* file) { + world_clear(); + _parse_scene(file); +} + +void load_scene_additive(const char* file) { + printf("parsing scene\n"); + _parse_scene(file); + printf("parsed scene\n"); +} diff --git a/src/corelib/scene.h b/src/corelib/scene.h index 164b3ed..5fef139 100644 --- a/src/corelib/scene.h +++ b/src/corelib/scene.h @@ -3,7 +3,20 @@ #include "memory.h" +typedef struct object_t object_t; +typedef void(*type_handler_fn)(object_t* this, int argc, char** argv); + +// Add or replace the type handler for [Object] entries of a given key. +// Will delete the handler entry if handler == NULL. +void set_type_handler(const char* type, type_handler_fn handler); + +// Load a scene exclusively. Clears the current scene before parsing file. +void load_scene(const char* file); + +// Load a scene additively. +// Will not clear the currently active objects before parsing file. +void load_scene_additive(const char* file); #endif /* _corelib_scene_h */ From 19401f1f47e516c6dbbcd3042bd3a069cfaf6d32 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 16 Jul 2023 13:33:47 +0200 Subject: [PATCH 8/9] added scene file validation --- src/corelib/scene.c | 70 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/src/corelib/scene.c b/src/corelib/scene.c index c2ce3e9..239cca3 100644 --- a/src/corelib/scene.c +++ b/src/corelib/scene.c @@ -57,6 +57,14 @@ int fpeekc(FILE* file) { return c; } +static +void freadto(FILE* file, char c) { + char read; + do { + read = fgetc(file); + } while(read != c); +} + static int nextnw(FILE* file) { int next; @@ -75,6 +83,8 @@ void _parse_key(FILE* file, char* out) { c = fgetc(file); if(c == ':') { *out = '\0'; + } else if(c == '#') { + freadto(file, '\n'); } else if(!isspace(c)) { *out = c; ++out; @@ -91,6 +101,9 @@ void _parse_value(FILE* file, char* out, int* _argc, char** argv) { do { c = fgetc(file); switch(c) { + case '#': + freadto(file, '\n'); + break; case ';': *out = '\0'; break; @@ -138,8 +151,7 @@ void _parse_config(FILE* file) { } static -void _parse_scene(const char* filename) { - FILE* file = fopen(filename, "r"); +void _parse_scene(FILE* file) { int next; do { _parse_config(file); @@ -148,13 +160,53 @@ void _parse_scene(const char* filename) { } while(next != EOF); } -void load_scene(const char* file) { - world_clear(); - _parse_scene(file); +static +int _validate_config(FILE* file) { + char c; + int colon_count = 0; + do { + c = nextnw(file); + switch(c) { + default: break; + case ':': + colon_count++; + break; + } + } while(c != ';'); + + return colon_count == 1; } -void load_scene_additive(const char* file) { - printf("parsing scene\n"); - _parse_scene(file); - printf("parsed scene\n"); +static +int _validate_scene(FILE* file) { + if(file == NULL) return 0; + + int next; + int validated; + do { + validated = _validate_config(file); + next = nextnw(file); + ungetc(next, file); + } while(validated == 1 && next != EOF); + + rewind(file); + + return validated; +} + +void load_scene(const char* filename) { + FILE* file = fopen(filename, "r"); + if(_validate_scene(file)) { + world_clear(); + _parse_scene(file); + fclose(file); + } +} + +void load_scene_additive(const char* filename) { + FILE* file = fopen(filename, "r"); + if(_validate_scene(file)) { + _parse_scene(file); + fclose(file); + } } From b387dee9c6a245f72adccdb392922489eea58da5 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 16 Jul 2023 13:46:53 +0200 Subject: [PATCH 9/9] first whitespace of a line is now skipped when processing arguments --- src/corelib/scene.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/corelib/scene.c b/src/corelib/scene.c index 239cca3..61090c2 100644 --- a/src/corelib/scene.c +++ b/src/corelib/scene.c @@ -103,6 +103,9 @@ void _parse_value(FILE* file, char* out, int* _argc, char** argv) { switch(c) { case '#': freadto(file, '\n'); + // fall through + case '\n': + ungetc(nextnw(file), file); break; case ';': *out = '\0';