├── runtime ├── pal.png ├── ball.png ├── larry.mid ├── Jambala8.mid ├── stranded │ ├── logo.png │ ├── palette.png │ ├── AweROMGM.sf2 │ ├── stranded.mid │ ├── background.png │ ├── sprites │ │ ├── boat.png │ │ ├── face.png │ │ ├── guy.png │ │ ├── hut1.png │ │ ├── hut2.png │ │ ├── chair.png │ │ ├── hut_tv.png │ │ ├── tree2.png │ │ ├── guy_idle.png │ │ ├── guychair.png │ │ ├── hut_book.png │ │ ├── palmtree.png │ │ ├── penguin.png │ │ ├── radio_spr.png │ │ ├── treeline.png │ │ ├── hut_inside.png │ │ ├── radio_bulb.png │ │ ├── radio_dish.png │ │ ├── radio_full.png │ │ ├── radio_none.png │ │ ├── guy_jump_0001.png │ │ ├── guy_jump_0002.png │ │ ├── guy_jump_0003.png │ │ ├── guy_jump_0004.png │ │ ├── guy_jump_0005.png │ │ ├── guy_jump_0006.png │ │ ├── guy_jump_0007.png │ │ ├── guy_jump_0008.png │ │ ├── guy_jump_0009.png │ │ ├── guy_jump_0010.png │ │ ├── guy_jump_0011.png │ │ ├── guy_jump_0012.png │ │ ├── hut_tv_nodish.png │ │ ├── radio_battery.png │ │ ├── hut_book_nobulb.png │ │ ├── penguin_battery.png │ │ ├── radio_bulb_dish.png │ │ ├── guy_walk_left_0001.png │ │ ├── guy_walk_left_0002.png │ │ ├── guy_walk_left_0003.png │ │ ├── guy_walk_left_0004.png │ │ ├── guy_walk_left_0005.png │ │ ├── guy_walk_left_0006.png │ │ ├── guy_walk_left_0007.png │ │ ├── guy_walk_left_0008.png │ │ ├── guy_walk_left_0009.png │ │ ├── guy_walk_left_0010.png │ │ ├── guy_walk_left_0011.png │ │ ├── guy_walk_left_0012.png │ │ ├── guy_walk_right_0001.png │ │ ├── guy_walk_right_0002.png │ │ ├── guy_walk_right_0003.png │ │ ├── guy_walk_right_0004.png │ │ ├── guy_walk_right_0005.png │ │ ├── guy_walk_right_0006.png │ │ ├── guy_walk_right_0007.png │ │ ├── guy_walk_right_0008.png │ │ ├── guy_walk_right_0009.png │ │ ├── guy_walk_right_0010.png │ │ ├── guy_walk_right_0011.png │ │ ├── guy_walk_right_0012.png │ │ ├── penguin_walk_0001.png │ │ ├── penguin_walk_0002.png │ │ ├── penguin_walk_0003.png │ │ ├── penguin_walk_0004.png │ │ ├── penguin_walk_0005.png │ │ ├── penguin_walk_0006.png │ │ ├── penguin_walk_0007.png │ │ ├── penguin_walk_0008.png │ │ ├── penguin_walk_0009.png │ │ ├── penguin_walk_0010.png │ │ ├── penguin_walk_0011.png │ │ ├── penguin_walk_0012.png │ │ ├── radio_battery_dish.png │ │ └── radio_bulb_battery.png │ ├── Volter__28Goldfish_29.ttf │ └── dialog.dlg ├── Jambala8.txt └── larry.txt ├── source ├── pixie.h ├── main.c ├── pixie │ ├── mmap.h │ ├── dir.h │ ├── frametimer.h │ ├── ease.h │ ├── palrle.h │ ├── mid.h │ ├── pixelfont.h │ └── tml.h ├── img.h └── stranded.c ├── readme.md ├── pixie.sln ├── .github └── workflows │ └── main.yml ├── pixie.vcxproj.filters ├── .gitignore └── pixie.vcxproj /runtime/pal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/pal.png -------------------------------------------------------------------------------- /runtime/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/ball.png -------------------------------------------------------------------------------- /runtime/larry.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/larry.mid -------------------------------------------------------------------------------- /runtime/Jambala8.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/Jambala8.mid -------------------------------------------------------------------------------- /runtime/stranded/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/logo.png -------------------------------------------------------------------------------- /runtime/stranded/palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/palette.png -------------------------------------------------------------------------------- /runtime/stranded/AweROMGM.sf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/AweROMGM.sf2 -------------------------------------------------------------------------------- /runtime/stranded/stranded.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/stranded.mid -------------------------------------------------------------------------------- /runtime/stranded/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/background.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/boat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/boat.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/face.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/hut1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/hut1.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/hut2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/hut2.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/chair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/chair.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/hut_tv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/hut_tv.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/tree2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/tree2.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_idle.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guychair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guychair.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/hut_book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/hut_book.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/palmtree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/palmtree.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_spr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_spr.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/treeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/treeline.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/hut_inside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/hut_inside.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_bulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_bulb.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_dish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_dish.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_full.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_none.png -------------------------------------------------------------------------------- /runtime/stranded/Volter__28Goldfish_29.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/Volter__28Goldfish_29.ttf -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0001.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0002.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0003.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0004.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0005.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0006.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0007.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0008.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0009.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0010.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0011.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_jump_0012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_jump_0012.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/hut_tv_nodish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/hut_tv_nodish.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_battery.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/hut_book_nobulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/hut_book_nobulb.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_battery.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_bulb_dish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_bulb_dish.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0001.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0002.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0003.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0004.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0005.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0006.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0007.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0008.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0009.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0010.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0011.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_left_0012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_left_0012.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0001.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0002.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0003.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0004.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0005.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0006.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0007.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0008.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0009.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0010.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0011.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/guy_walk_right_0012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/guy_walk_right_0012.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0001.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0002.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0003.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0004.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0005.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0006.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0007.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0008.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0009.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0010.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0011.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/penguin_walk_0012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/penguin_walk_0012.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_battery_dish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_battery_dish.png -------------------------------------------------------------------------------- /runtime/stranded/sprites/radio_bulb_battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasgustavsson/newpixie/HEAD/runtime/stranded/sprites/radio_bulb_battery.png -------------------------------------------------------------------------------- /runtime/Jambala8.txt: -------------------------------------------------------------------------------- 1 | Added by: Mike Newman 2 | Date Added: 2000-07-06 3 | 4 | File URL is http://www.vgmusic.com/new-files/Jambala8.mid 5 | 6 | Uploaded by oedipus@islandia.is (Oedipus) 7 | 8 | File Type: MIDI 9 | Game System: Atari Computers 10 | Game Name: 7 Gates Of Jambala 11 | Song Title: Ingame (B) 12 | Sequenced by: Oedipus 13 | Song Originally Composed by: Jochen Hippel 14 | 15 | Other Information: 16 | 17 | 18 | 19 | Midi TrackName Analysis: 20 | 21 | 22 | 7 Gates of Jambala - ingame 23 | BY: Jochen Hippel 24 | RE: Oedipus (oedipus@islandia.is) 25 | CPU: Atari ST, Amiga. 26 | -------------------------------------------------------------------------------- /source/pixie.h: -------------------------------------------------------------------------------- 1 | /* 2 | Pixie include file wrapper 3 | -------------------------- 4 | 5 | Used wherever pixie/pixie.h would be included, 6 | to ensure we provide consistent configuration 7 | options (#defines) for every instance it is 8 | included. 9 | */ 10 | 11 | //#define PIXIE_NO_MATH 12 | //#define PIXIE_NO_MAIN 13 | //#define PIXIE_NO_BUILD 14 | //#define PIXIE_WIN_SDL 15 | //#define PIXIE_ASSERT_IN_RELEASE_BUILD 16 | //#define PIXIE_MAX_STRING_LENGTH 256 17 | 18 | //#define _CRT_NONSTDC_NO_DEPRECATE 19 | //#define _CRT_SECURE_NO_WARNINGS 20 | //#include 21 | //#define PIXIE_ASSERT( expression, message ) assert( expression ) 22 | 23 | //#define _CRT_NONSTDC_NO_DEPRECATE 24 | //#define _CRT_SECURE_NO_WARNINGS 25 | //#include 26 | //#define PIXIE_I8 int8_t 27 | //#define PIXIE_I16 int16_t 28 | //#define PIXIE_I32 int32_t 29 | //#define PIXIE_I64 int64_t 30 | //#define PIXIE_U8 uint8_t 31 | //#define PIXIE_U16 uint16_t 32 | //#define PIXIE_U32 uint32_t 33 | //#define PIXIE_U64 uint64_t 34 | 35 | #include "pixie/pixie.h" -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![build](https://github.com/mattiasgustavsson/newpixie/actions/workflows/main.yml/badge.svg) 2 | 3 | Pixie Game Engine 4 | ================= 5 | 6 | Experimenting with new version of my game engine Pixie 7 | 8 | 9 | Build instructions 10 | ------------------ 11 | 12 | ### Windows 13 | From a Visual Studio command prompt, go to the `runtime` folder and do 14 | ``` 15 | cl ..\source\main.c 16 | ``` 17 | 18 | ### Mac 19 | Go to the `runtime` folder and do 20 | ``` 21 | clang ../source/main.c -lSDL2 -lGLEW -framework OpenGL 22 | ``` 23 | 24 | SDL2 and GLEW are required - if you don't have then installed you can do so by running 25 | ``` 26 | brew install sdl2 glew 27 | ``` 28 | 29 | ### Linux 30 | Go to the `runtime` folder and do 31 | ``` 32 | gcc ../source/main.c -lSDL2 -lGLEW -lGL -lm -lpthread 33 | ``` 34 | 35 | SDL2 and GLEW are required - if you don't have them installed you can do so by running 36 | ``` 37 | sudo apt-get install libsdl2-dev 38 | sudo apt-get install libglew-dev 39 | ``` 40 | 41 | Demo game 42 | --------- 43 | Alternatively, compile `stranded.c` instead of `main.c` for a more comprehensive demo project. 44 | 45 | 46 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | #include "pixie.h" 2 | 3 | ASSETS_BEGIN( "data.dat" ) 4 | ASSET_PALETTE( PAL, "pal.png" ) 5 | ASSET_SPRITE( BALL, "ball.png" ) 6 | ASSET_SONG( JAMBALA8, "jambala8.mid" ) 7 | ASSET_SONG( LARRY, "larry.mid" ) 8 | ASSETS_END() 9 | 10 | int pixmain( int argc, char** argv ) { 11 | (void) argc, (void) argv; 12 | 13 | if( load_assets() != 0 ) return 1; 14 | 15 | print( "Hello world!" ); 16 | 17 | load_palette( PAL ); 18 | for( int i = 1; i <= 8; ++i ) sprite( i, 100,100, BALL ); 19 | 20 | play_song( JAMBALA8 ); 21 | 22 | int c = 0; 23 | 24 | LOOP { 25 | wait_vbl(); 26 | ++c; 27 | if( c == 1000 ) play_song( LARRY ); 28 | if( c > 2000 ) end( 0 ); 29 | for( int i = 1; i <= 8; ++i ) { 30 | int x = (int)( sin( ( c + 6 * i ) * 0.04f ) * cos( ( c + 6 * i ) * 0.027f ) * 150 + 160 ); 31 | int y = (int)( sin( ( c + 6 * i ) * 0.052f ) * cos( ( c + 6 * i ) * 0.017f ) * 90 + 110 ); 32 | sprite_pos( i, x, y ); 33 | } 34 | } 35 | } 36 | 37 | #define PIXIE_IMPLEMENTATION 38 | #include "pixie.h" 39 | 40 | -------------------------------------------------------------------------------- /pixie.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.25928.0 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pixie", "pixie.vcxproj", "{EF3E8E6B-3FE9-4B35-879C-65B355B2D531}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|x64 = Debug|x64 10 | Debug|x86 = Debug|x86 11 | Release|x64 = Release|x64 12 | Release|x86 = Release|x86 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531}.Debug|x64.ActiveCfg = Debug|x64 16 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531}.Debug|x64.Build.0 = Debug|x64 17 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531}.Debug|x86.ActiveCfg = Debug|Win32 18 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531}.Debug|x86.Build.0 = Debug|Win32 19 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531}.Release|x64.ActiveCfg = Release|x64 20 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531}.Release|x64.Build.0 = Release|x64 21 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531}.Release|x86.ActiveCfg = Release|Win32 22 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531}.Release|x86.Build.0 = Release|Win32 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | EndGlobal 28 | -------------------------------------------------------------------------------- /runtime/larry.txt: -------------------------------------------------------------------------------- 1 | SIERRA ON-LINE, INC. 2 | 3-D Animated Adventure Game Soundtrack Series 3 | =============================================== 4 | LEISURE SUIT LARRY III: PASSIONATE PATTI- 5 | IN PURSUIT OF THE PULSATING PECTORALS 6 | 7 | 8 | "TAWNI AT THE BEACH" 9 | Mike Dana 10 | =============================================== 11 | Copyright (c)1989 Sierra On-Line, Inc. 12 | =============================================== 13 | 14 | GENERAL MIDI VERSION 15 | 16 | System Requirements: 17 | 18 | - MIDI Playback Software capable of reading Type 1 Standard 19 | MIDI File format 20 | - General MIDI sound device (Wave Table or better recommended) 21 | 22 | 23 | This Standard MIDI File was recorded directly from Sierra's "Leisure Suit 24 | Larry 3" adventure game. It has been converted from the MT-32 version for 25 | playback on General MIDI sound cards. A Wave Table or better sound card is 26 | highly recommended for optimal playback. 27 | 28 | Recorded/converted for General MIDI by Tom Lewandowski. 29 | Address questions or comments to: 30 | 31 | QUEST STUDIOS 32 | Tom Lewandowski 33 | tom@queststudios.com 34 | 35 | www.QuestStudios.com 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-windows-msvc: 7 | runs-on: windows-2019 8 | steps: 9 | - uses: actions/checkout@v1 10 | # this runs vcvarsall for us, so we get the MSVC toolchain in PATH. 11 | - uses: seanmiddleditch/gha-setup-vsdevenv@master 12 | - name: build pixie 13 | run: | 14 | cd runtime 15 | cl ../source/main.c 16 | - name: build stranded 17 | run: | 18 | cd runtime 19 | cl ../source/stranded.c 20 | build-macos: 21 | runs-on: macOS-latest 22 | steps: 23 | - uses: actions/checkout@v1 24 | - name: install dependencies 25 | run: brew install sdl2 glew 26 | - name: build pixie 27 | run: | 28 | cd runtime 29 | clang ../source/main.c -lSDL2 -lGLEW -framework OpenGL 30 | - name: build stranded 31 | run: | 32 | cd runtime 33 | clang ../source/stranded.c -lSDL2 -lGLEW -framework OpenGL 34 | build-linux-gcc: 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v1 38 | - name: install dependencies 39 | run: | 40 | sudo apt-get update 41 | sudo apt-get install -qq libsdl2-dev 42 | sudo apt-get install -qq libglew-dev 43 | - name: build pixie 44 | run: | 45 | cd runtime 46 | gcc ../source/main.c -lSDL2 -lGLEW -lGL -lm -lpthread 47 | - name: build stranded 48 | run: | 49 | cd runtime 50 | gcc ../source/stranded.c -lSDL2 -lGLEW -lGL -lm -lpthread 51 | -------------------------------------------------------------------------------- /runtime/stranded/dialog.dlg: -------------------------------------------------------------------------------- 1 | [lets_go_home] 2 | Hmmm... maybe three and a half years on this island is enough... 3 | I've enjoyed being stranded here, but now I think I'd like to go home. 4 | Last time I checked, the radio was working fine. I think I'll call for help now. 5 | #SetFlag:may_stand# 6 | 7 | [radio_first] 8 | Oh no! There are parts missing! 9 | Someone have grabbed the ANTENNA, the BATTERY, and the BULB. 10 | I *have* to find them! 11 | #SetFlag:looked_at_radio# 12 | 13 | [radio_none] 14 | It is missing the ANTENNA, the BATTERY, and the BULB. 15 | 16 | [radio_bulb] 17 | It is missing the ANTENNA and the BATTERY. 18 | 19 | [radio_dish] 20 | It is missing the BULB and the BATTERY. 21 | 22 | [radio_battery] 23 | It is missing the ANTENNA and the BULB. 24 | 25 | [radio_bulb_dish] 26 | It is missing the BATTERY. 27 | 28 | [radio_battery_dish] 29 | It is missing the BULB. 30 | 31 | [radio_bulb_battery] 32 | It is missing the ANTENNA. 33 | 34 | [radio_all] 35 | The radio is complete! Now I can go home. 36 | MAYDAY! MAYDAY! I AM STRANDED ON AN ISLAND. SEND HELP! 37 | #SetFlag:rescued# 38 | 39 | 40 | [hut_tv] 41 | Hey, that's my antenna! 42 | Huh, what? No, it's my antenna now. I found it. 43 | I need it for my TV. To watch my shows. 44 | But *I* need it for my radio. So I can call for help and get home. 45 | Uh... I *am* very tired, I need to sleep. But I can't stop watching. 46 | You can have the antenna back later, I only need it to make the transmission. 47 | Ok, go ahead then. 48 | #ClearFlag:at_hut_tv# 49 | 50 | 51 | [hut_tv_sleep] 52 | Looks like he's sleeping. I better not disturb him. 53 | 54 | 55 | [hut_book] 56 | That's my bulb! For my radio. 57 | Oh, yes yes, quite. I need it so I can read my books. 58 | But *I* need it for my radio. So I can call for help and get home. 59 | I see, I see. Well I am really rather sleepy. But I can not put this book down. Quite the page-turner it is. 60 | You can have the bulb back later, I only need it to make the transmission. 61 | Ah, very well, very well. 62 | #ClearFlag:at_hut_bulb# 63 | 64 | [hut_book_sleep] 65 | Looks like he's sleeping. I better not disturb him. 66 | 67 | 68 | [penguin] 69 | Waddya want!? 70 | Uh, is that my battery you have there? 71 | Don't know! Don't care! 72 | But you're not even using it! 73 | Sure I do! I lick it. 74 | You *lick* it? 75 | Makes my tongue tingle. 76 | But *I* need it for my radio. So I can call for help and get home. 77 | What are you even doing here? Aren't you supposed to be on the south pole or something? 78 | Vacation. 79 | It's about time I got back anyway. 80 | Don't touch my battery while I'm gone! 81 | #SetFlag:penguin_running# 82 | Yeah, right. 83 | #SetFlag:penguin_gone# 84 | 85 | [grab_battery] 86 | I'm having this! 87 | -------------------------------------------------------------------------------- /pixie.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {6e988e67-6239-42d1-85ef-a1c6d8e8323a} 6 | 7 | 8 | 9 | 10 | pixie 11 | 12 | 13 | pixie 14 | 15 | 16 | pixie 17 | 18 | 19 | pixie 20 | 21 | 22 | pixie 23 | 24 | 25 | pixie 26 | 27 | 28 | pixie 29 | 30 | 31 | pixie 32 | 33 | 34 | pixie 35 | 36 | 37 | pixie 38 | 39 | 40 | pixie 41 | 42 | 43 | 44 | pixie 45 | 46 | 47 | pixie 48 | 49 | 50 | pixie 51 | 52 | 53 | pixie 54 | 55 | 56 | pixie 57 | 58 | 59 | pixie 60 | 61 | 62 | 63 | 64 | pixie 65 | 66 | 67 | pixie 68 | 69 | 70 | pixie 71 | 72 | 73 | pixie 74 | 75 | 76 | pixie 77 | 78 | 79 | 80 | pixie 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | runtime 7 | .build_temp 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Dd]ebugPublic/ 21 | [Rr]elease/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | **/Properties/launchSettings.json 59 | 60 | # StyleCop 61 | StyleCopReport.xml 62 | 63 | # Files built by Visual Studio 64 | *_i.c 65 | *_p.c 66 | *_i.h 67 | *.ilk 68 | *.meta 69 | *.obj 70 | *.iobj 71 | *.pch 72 | *.pdb 73 | *.ipdb 74 | *.pgc 75 | *.pgd 76 | *.rsp 77 | *.sbr 78 | *.tlb 79 | *.tli 80 | *.tlh 81 | *.tmp 82 | *.tmp_proj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | 259 | # Microsoft Fakes 260 | FakesAssemblies/ 261 | 262 | # GhostDoc plugin setting file 263 | *.GhostDoc.xml 264 | 265 | # Node.js Tools for Visual Studio 266 | .ntvs_analysis.dat 267 | node_modules/ 268 | 269 | # Visual Studio 6 build log 270 | *.plg 271 | 272 | # Visual Studio 6 workspace options file 273 | *.opt 274 | 275 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 276 | *.vbw 277 | 278 | # Visual Studio LightSwitch build output 279 | **/*.HTMLClient/GeneratedArtifacts 280 | **/*.DesktopClient/GeneratedArtifacts 281 | **/*.DesktopClient/ModelManifest.xml 282 | **/*.Server/GeneratedArtifacts 283 | **/*.Server/ModelManifest.xml 284 | _Pvt_Extensions 285 | 286 | # Paket dependency manager 287 | .paket/paket.exe 288 | paket-files/ 289 | 290 | # FAKE - F# Make 291 | .fake/ 292 | 293 | # JetBrains Rider 294 | .idea/ 295 | *.sln.iml 296 | 297 | # CodeRush 298 | .cr/ 299 | 300 | # Python Tools for Visual Studio (PTVS) 301 | __pycache__/ 302 | *.pyc 303 | 304 | # Cake - Uncomment if you are using it 305 | # tools/** 306 | # !tools/packages.config 307 | 308 | # Tabs Studio 309 | *.tss 310 | 311 | # Telerik's JustMock configuration file 312 | *.jmconfig 313 | 314 | # BizTalk build output 315 | *.btp.cs 316 | *.btm.cs 317 | *.odx.cs 318 | *.xsd.cs 319 | 320 | # OpenCover UI analysis results 321 | OpenCover/ 322 | 323 | # Azure Stream Analytics local run output 324 | ASALocalRun/ 325 | 326 | # MSBuild Binary and Structured Log 327 | *.binlog 328 | 329 | # NVidia Nsight GPU debugger configuration file 330 | *.nvuser 331 | 332 | # MFractors (Xamarin productivity tool) working folder 333 | .mfractor/ 334 | -------------------------------------------------------------------------------- /source/pixie/mmap.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | typedef struct mmap_t mmap_t; 5 | 6 | mmap_t* mmap_create( char const* filename, size_t size ); 7 | 8 | mmap_t* mmap_open( char const* filename, size_t size ); 9 | 10 | mmap_t* mmap_open_read_only( char const* filename, size_t size ); 11 | 12 | void mmap_close( mmap_t* map ); 13 | 14 | void* mmap_data( mmap_t* map ); 15 | 16 | size_t mmap_size( mmap_t* map ); 17 | 18 | char const* mmap_filename( mmap_t* map ); 19 | 20 | 21 | #ifdef MMAP_IMPLEMENTATION 22 | 23 | #ifdef _WIN32 24 | #pragma warning( push ) 25 | #pragma warning( disable: 4365 ) 26 | #pragma warning( disable: 4619 ) 27 | #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' 28 | #pragma warning( disable: 4768 ) // __declspec attributes before linkage specification are ignored 29 | #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' 30 | #define _NTDDSCM_H_ 31 | #include 32 | #pragma warning( pop ) 33 | #else 34 | #include 35 | #include 36 | #include 37 | #endif 38 | 39 | 40 | struct mmap_t 41 | { 42 | char filename[ 256 ]; 43 | void* data; 44 | size_t size; 45 | 46 | #ifdef _WIN32 47 | HANDLE file_handle; 48 | HANDLE mmap_handle; 49 | #else 50 | int file_descriptor; 51 | #endif 52 | }; 53 | 54 | 55 | mmap_t* mmap_create( char const* filename, size_t size ) 56 | { 57 | mmap_t* map = (mmap_t*) malloc( sizeof( mmap_t ) ); 58 | memset( map, 0, sizeof( *map ) ); 59 | strcpy( map->filename, filename ); 60 | map->size = size; 61 | 62 | 63 | #ifdef _WIN32 64 | map->file_handle = CreateFileA( filename, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 65 | if( map->file_handle == (HANDLE) INVALID_HANDLE_VALUE ) 66 | { 67 | free( map ); 68 | return 0; 69 | } 70 | 71 | DWORD size_high = (DWORD)( ( (DWORD64) size ) >> 32 ); 72 | DWORD size_low = (DWORD)( ( (DWORD64) size ) & 0xffffffff ); 73 | map->mmap_handle = CreateFileMappingA( map->file_handle, NULL, PAGE_READWRITE, size_high, size_low, NULL ); 74 | if( !map->mmap_handle ) 75 | { 76 | CloseHandle( map->file_handle ); 77 | free( map ); 78 | return 0; 79 | } 80 | 81 | map->data = MapViewOfFile( map->mmap_handle, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); 82 | if( !map->data ) 83 | { 84 | CloseHandle( map->mmap_handle ); 85 | CloseHandle( map->file_handle ); 86 | free( map ); 87 | return 0; 88 | } 89 | #else 90 | map->file_descriptor = open( filename, O_RDWR | O_CREAT, 0 ); 91 | if( !map->file_descriptor ) 92 | { 93 | free( map ); 94 | return 0; 95 | } 96 | map->data = mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map->file_descriptor, 0 ); 97 | if( !map->data ) 98 | { 99 | close( map->file_descriptor ); 100 | free( map ); 101 | return 0; 102 | } 103 | #endif 104 | 105 | return map; 106 | } 107 | 108 | 109 | mmap_t* mmap_open( char const* filename, size_t size ) 110 | { 111 | mmap_t* map = (mmap_t*) malloc( sizeof( mmap_t ) ); 112 | memset( map, 0, sizeof( *map ) ); 113 | strcpy( map->filename, filename ); 114 | map->size = size; 115 | 116 | #ifdef _WIN32 117 | map->file_handle = CreateFileA( filename, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 118 | if( map->file_handle == (HANDLE) INVALID_HANDLE_VALUE ) 119 | { 120 | free( map ); 121 | return 0; 122 | } 123 | 124 | DWORD size_high = (DWORD)( ( (DWORD64) size ) >> 32 ); 125 | DWORD size_low = (DWORD)( ( (DWORD64) size ) & 0xffffffff ); 126 | map->mmap_handle = CreateFileMappingA( map->file_handle, NULL, PAGE_READWRITE, size_high, size_low, NULL ); 127 | if( !map->mmap_handle ) 128 | { 129 | CloseHandle( map->file_handle ); 130 | free( map ); 131 | return 0; 132 | } 133 | 134 | map->data = MapViewOfFile( map->mmap_handle, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); 135 | if( !map->data ) 136 | { 137 | CloseHandle( map->mmap_handle ); 138 | CloseHandle( map->file_handle ); 139 | free( map ); 140 | return 0; 141 | } 142 | #else 143 | map->file_descriptor = open( filename, O_RDWR, 0 ); 144 | if( !map->file_descriptor ) 145 | { 146 | free( map ); 147 | return 0; 148 | } 149 | map->data = mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map->file_descriptor, 0 ); 150 | if( !map->data ) 151 | { 152 | close( map->file_descriptor ); 153 | free( map ); 154 | return 0; 155 | } 156 | #endif 157 | 158 | return map; 159 | } 160 | 161 | 162 | mmap_t* mmap_open_read_only( char const* filename, size_t size ) 163 | { 164 | mmap_t* map = (mmap_t*) malloc( sizeof( mmap_t ) ); 165 | memset( map, 0, sizeof( *map ) ); 166 | strcpy( map->filename, filename ); 167 | map->size = size; 168 | 169 | #ifdef _WIN32 170 | map->file_handle = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 171 | if( map->file_handle == (HANDLE) INVALID_HANDLE_VALUE ) 172 | { 173 | free( map ); 174 | return 0; 175 | } 176 | 177 | DWORD size_high = (DWORD)( ( (DWORD64) size ) >> 32 ); 178 | DWORD size_low = (DWORD)( ( (DWORD64) size ) & 0xffffffff ); 179 | map->mmap_handle = CreateFileMappingA( map->file_handle, NULL, PAGE_READONLY, size_high, size_low, NULL ); 180 | if( !map->mmap_handle ) 181 | { 182 | CloseHandle( map->file_handle ); 183 | free( map ); 184 | return 0; 185 | } 186 | 187 | map->data = MapViewOfFile( map->mmap_handle, FILE_MAP_READ, 0, 0, 0); 188 | if( !map->data ) 189 | { 190 | CloseHandle( map->mmap_handle ); 191 | CloseHandle( map->file_handle ); 192 | free( map ); 193 | return 0; 194 | } 195 | #else 196 | map->file_descriptor = open( filename, O_RDONLY, 0 ); 197 | if( !map->file_descriptor ) 198 | { 199 | free( map ); 200 | return 0; 201 | } 202 | map->data = mmap( NULL, size, PROT_READ, MAP_SHARED, map->file_descriptor, 0 ); 203 | if( !map->data ) 204 | { 205 | close( map->file_descriptor ); 206 | free( map ); 207 | return 0; 208 | } 209 | #endif 210 | 211 | return map; 212 | } 213 | 214 | 215 | void mmap_close( mmap_t* map ) 216 | { 217 | if( !map ) return; 218 | #ifdef _WIN32 219 | UnmapViewOfFile( map->data ); 220 | CloseHandle( map->mmap_handle ); 221 | CloseHandle( map->file_handle ); 222 | #else 223 | munmap( map->data, map->size ); 224 | close( map->file_descriptor ); 225 | #endif 226 | free( map ); 227 | } 228 | 229 | 230 | void* mmap_data( mmap_t* map ) 231 | { 232 | if( !map ) return NULL; 233 | return map->data; 234 | } 235 | 236 | 237 | size_t mmap_size( mmap_t* map ) 238 | { 239 | if( !map ) return 0; 240 | return map->size; 241 | } 242 | 243 | 244 | char const* mmap_filename( mmap_t* map ) 245 | { 246 | if( !map ) return NULL; 247 | return map->filename; 248 | } 249 | 250 | 251 | #endif /* MMAP_IMPLEMENTATION */ -------------------------------------------------------------------------------- /source/pixie/dir.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | ------------------------------------------------------------------------------ 4 | Licensing information can be found at the end of the dir. 5 | ------------------------------------------------------------------------------ 6 | 7 | dir.h - v0.1 - Directory listing functions for C/C++ 8 | 9 | Do this: 10 | #define DIR_IMPLEMENTATION 11 | before you include this file in *one* C/C++ dir to create the implementation. 12 | */ 13 | 14 | #ifndef dir_h 15 | #define dir_h 16 | 17 | typedef struct dir_t dir_t; 18 | typedef struct dir_entry_t dir_entry_t; 19 | 20 | dir_t* dir_open( char const* path ); 21 | void dir_close( dir_t* dir ); 22 | char const* dir_path( dir_t* dir ); 23 | dir_entry_t* dir_read( dir_t* dir ); 24 | 25 | char const* dir_name( dir_entry_t* entry ); 26 | int dir_is_file( dir_entry_t* entry ); 27 | int dir_is_folder( dir_entry_t* entry ); 28 | 29 | #endif /* dir_h */ 30 | 31 | /* 32 | ---------------------- 33 | IMPLEMENTATION 34 | ---------------------- 35 | */ 36 | 37 | #ifdef DIR_IMPLEMENTATION 38 | #undef DIR_IMPLEMENTATION 39 | 40 | #ifndef DIR_MALLOC 41 | #define _CRT_NONSTDC_NO_DEPRECATE 42 | #define _CRT_SECURE_NO_WARNINGS 43 | #include 44 | #if defined(__cplusplus) 45 | #define DIR_MALLOC( size ) ( ::malloc( size ) ) 46 | #define DIR_FREE( ptr ) ( ::free( ptr ) ) 47 | #else 48 | #define DIR_MALLOC( size ) ( malloc( size ) ) 49 | #define DIR_FREE( ptr ) ( free( ptr ) ) 50 | #endif 51 | #endif 52 | 53 | 54 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 55 | // WINDOWS 56 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 57 | 58 | #if defined( DIR_WINDOWS ) 59 | 60 | #define _CRT_NONSTDC_NO_DEPRECATE 61 | #define _CRT_SECURE_NO_WARNINGS 62 | #if !defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0501 63 | #undef _WIN32_WINNT 64 | #define _WIN32_WINNT 0x0501 // requires Windows XP minimum 65 | #endif 66 | // 0x0400=Windows NT 4.0, 0x0500=Windows 2000, 0x0501=Windows XP, 0x0502=Windows Server 2003, 0x0600=Windows Vista, 67 | // 0x0601=Windows 7, 0x0602=Windows 8, 0x0603=Windows 8.1, 0x0A00=Windows 10, 68 | #define _WINSOCKAPI_ 69 | #pragma warning( push ) 70 | #pragma warning( disable: 4365 ) 71 | #pragma warning( disable: 4619 ) 72 | #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' 73 | #pragma warning( disable: 4768 ) // __declspec attributes before linkage specification are ignored 74 | #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' 75 | #define _NTDDSCM_H_ 76 | #include 77 | #pragma warning( pop ) 78 | 79 | struct dir_entry_t 80 | { 81 | char name[ MAX_PATH ]; 82 | BOOL is_folder; 83 | }; 84 | 85 | 86 | struct dir_t 87 | { 88 | char path[ MAX_PATH ]; 89 | HANDLE handle; 90 | WIN32_FIND_DATAA data; 91 | dir_entry_t entry; 92 | }; 93 | 94 | 95 | dir_t* dir_open( char const* path ) 96 | { 97 | if( !path ) return 0; 98 | 99 | size_t path_len = strlen( path ); 100 | BOOL trailing_path_separator = path[ path_len - 1 ] == '\\' || path[ path_len - 1 ] == '/'; 101 | const char* string_to_append = "*.*"; 102 | if( path_len + strlen( string_to_append ) + ( trailing_path_separator ? 0 : 1 ) >= MAX_PATH ) return NULL; 103 | char search_pattern[ MAX_PATH ]; 104 | strcpy( search_pattern, path ); 105 | if( !trailing_path_separator ) strcat( search_pattern, "\\" ); 106 | strcat( search_pattern, string_to_append ); 107 | 108 | WIN32_FIND_DATAA data; 109 | HANDLE handle = FindFirstFileA( search_pattern, &data ); 110 | if( handle == INVALID_HANDLE_VALUE ) return NULL; 111 | 112 | dir_t* dir = (dir_t*) DIR_MALLOC( sizeof( dir_t ) ); 113 | strcpy( dir->path, path ); 114 | dir->handle = handle; 115 | dir->data = data; 116 | 117 | return dir; 118 | } 119 | 120 | 121 | void dir_close( dir_t* dir ) 122 | { 123 | if( !dir ) return; 124 | 125 | if( dir->handle != INVALID_HANDLE_VALUE ) FindClose( dir->handle ); 126 | DIR_FREE( dir ); 127 | } 128 | 129 | 130 | char const* dir_path( dir_t* dir ) 131 | { 132 | if( !dir ) return NULL; 133 | return dir->path; 134 | } 135 | 136 | 137 | dir_entry_t* dir_read( dir_t* dir ) 138 | { 139 | if( !dir ) return NULL; 140 | if( dir->handle == INVALID_HANDLE_VALUE ) return NULL; 141 | 142 | strcpy( dir->entry.name, dir->data.cFileName ); 143 | dir->entry.is_folder = ( dir->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0; 144 | 145 | BOOL result = FindNextFileA( dir->handle, &dir->data ); 146 | if( !result ) 147 | { 148 | FindClose( dir->handle ); 149 | dir->handle = INVALID_HANDLE_VALUE; 150 | } 151 | 152 | return &dir->entry; 153 | } 154 | 155 | 156 | char const* dir_name( dir_entry_t* entry ) 157 | { 158 | if( !entry ) return NULL; 159 | return entry->name; 160 | } 161 | 162 | 163 | int dir_is_file( dir_entry_t* entry ) 164 | { 165 | if( !entry ) return 0; 166 | return entry->is_folder == FALSE; 167 | } 168 | 169 | 170 | int dir_is_folder( dir_entry_t* entry ) 171 | { 172 | if( !entry ) return 0; 173 | return entry->is_folder == TRUE; 174 | } 175 | 176 | 177 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 178 | // POSIX 179 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 180 | #elif defined( DIR_POSIX ) 181 | 182 | #include 183 | 184 | 185 | dir_t* dir_open( char const* path ) 186 | { 187 | DIR* dir = opendir( path ); 188 | return (dir_t*) dir; 189 | } 190 | 191 | 192 | void dir_close( dir_t* dir ) 193 | { 194 | if( !dir ) return; 195 | closedir( (DIR*) dir ); 196 | } 197 | 198 | 199 | dir_entry_t* dir_read( dir_t* dir ) 200 | { 201 | if( !dir ) return NULL; 202 | return (dir_entry_t*)readdir( (DIR*) dir ); 203 | } 204 | 205 | 206 | char const* dir_name( dir_entry_t* entry ) 207 | { 208 | if( !entry ) return NULL; 209 | return ( (struct dirent*)entry )->d_name; 210 | } 211 | 212 | 213 | int dir_is_file( dir_entry_t* entry ) 214 | { 215 | if( !entry ) return 0; 216 | return ( (struct dirent*)entry )->d_type == DT_REG; 217 | } 218 | 219 | 220 | int dir_is_folder( dir_entry_t* entry ) 221 | { 222 | if( !entry ) return 0; 223 | return ( (struct dirent*)entry )->d_type == DT_DIR; 224 | } 225 | 226 | 227 | 228 | 229 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 230 | // PLATFORM ERROR 231 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 232 | #else 233 | 234 | #error Undefined platform. Define DIR_WINDOWS or DIR_POSIX. 235 | 236 | #endif 237 | 238 | 239 | #endif /* DIR_IMPLEMENTATION */ 240 | 241 | /* 242 | ------------------------------------------------------------------------------ 243 | 244 | This software is available under 2 licenses - you may choose the one you like. 245 | 246 | ------------------------------------------------------------------------------ 247 | 248 | ALTERNATIVE A - MIT License 249 | 250 | Copyright (c) 2017 Mattias Gustavsson 251 | 252 | Permission is hereby granted, free of charge, to any person obtaining a copy of 253 | this software and associated documentation files (the "Software"), to deal in 254 | the Software without restriction, including without limitation the rights to 255 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 256 | of the Software, and to permit persons to whom the Software is furnished to do 257 | so, subject to the following conditions: 258 | 259 | The above copyright notice and this permission notice shall be included in all 260 | copies or substantial portions of the Software. 261 | 262 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 263 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 264 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 265 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 266 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 267 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 268 | SOFTWARE. 269 | 270 | ------------------------------------------------------------------------------ 271 | 272 | ALTERNATIVE B - Public Domain (www.unlicense.org) 273 | 274 | This is free and unencumbered software released into the public domain. 275 | 276 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 277 | software, either in source code form or as a compiled binary, for any purpose, 278 | commercial or non-commercial, and by any means. 279 | 280 | In jurisdictions that recognize copyright laws, the author or authors of this 281 | software dedicate any and all copyright interest in the software to the public 282 | domain. We make this dedication for the benefit of the public at large and to 283 | the detriment of our heirs and successors. We intend this dedication to be an 284 | overt act of relinquishment in perpetuity of all present and future rights to 285 | this software under copyright law. 286 | 287 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 288 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 289 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 290 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 291 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 292 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 293 | 294 | ------------------------------------------------------------------------------ 295 | */ 296 | -------------------------------------------------------------------------------- /source/pixie/frametimer.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | frametimer.h - v0.1 - Framerate timer functionality. 7 | 8 | Do this: 9 | #define FRAMETIMER_IMPLEMENTATION 10 | before you include this file in *one* C/C++ file to create the implementation. 11 | */ 12 | 13 | #ifndef frametimer_h 14 | #define frametimer_h 15 | 16 | typedef struct frametimer_t frametimer_t; 17 | 18 | frametimer_t* frametimer_create( void* memxtx ); 19 | 20 | void frametimer_destroy( frametimer_t* frametimer ); 21 | 22 | void frametimer_lock_rate( frametimer_t* frametimer, int fps ); 23 | 24 | float frametimer_update( frametimer_t* frametimer ); 25 | 26 | float frametimer_delta_time( frametimer_t* frametimer ); 27 | 28 | int frametimer_frame_counter( frametimer_t* frametimer ); 29 | 30 | #endif /* frametimer_h */ 31 | 32 | /* 33 | ---------------------- 34 | IMPLEMENTATION 35 | ---------------------- 36 | */ 37 | 38 | #ifdef FRAMETIMER_IMPLEMENTATION 39 | #undef FRAMETIMER_IMPLEMENTATION 40 | 41 | #define _CRT_NONSTDC_NO_DEPRECATE 42 | #ifndef _CRT_SECURE_NO_WARNINGS 43 | #define _CRT_SECURE_NO_WARNINGS 44 | #endif 45 | #ifdef _WIN32 46 | #if !defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0501 47 | #undef _WIN32_WINNT 48 | #define _WIN32_WINNT 0x0501 // requires Windows XP minimum 49 | #endif 50 | // 0x0400=Windows NT 4.0, 0x0500=Windows 2000, 0x0501=Windows XP, 0x0502=Windows Server 2003, 0x0600=Windows Vista, 51 | // 0x0601=Windows 7, 0x0602=Windows 8, 0x0603=Windows 8.1, 0x0A00=Windows 10, 52 | #define _WINSOCKAPI_ 53 | #pragma warning( push ) 54 | #pragma warning( disable: 4619 ) 55 | #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' 56 | #pragma warning( disable: 4768 ) // __declspec attributes before linkage specification are ignored 57 | #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' 58 | #include 59 | #pragma warning( pop ) 60 | #ifndef __TINYC__ 61 | #pragma comment(lib, "winmm.lib") 62 | #endif 63 | #elif defined( __APPLE__ ) 64 | #include 65 | #else 66 | #include 67 | #endif 68 | 69 | #ifndef FRAMETIMER_MALLOC 70 | #include 71 | #if defined(__cplusplus) 72 | #define FRAMETIMER_MALLOC( ctx, size ) ( ::malloc( size ) ) 73 | #define FRAMETIMER_FREE( ctx, ptr ) ( ::free( ptr ) ) 74 | #else 75 | #define FRAMETIMER_MALLOC( ctx, size ) ( malloc( size ) ) 76 | #define FRAMETIMER_FREE( ctx, ptr ) ( free( ptr ) ) 77 | #endif 78 | #endif 79 | 80 | #ifndef FRAMETIMER_U64 81 | #define FRAMETIMER_U64 unsigned long long 82 | #endif 83 | 84 | 85 | struct frametimer_t 86 | { 87 | FRAMETIMER_U64 clock_freq; 88 | FRAMETIMER_U64 prev_clock; 89 | void* memctx; 90 | float delta_time; 91 | int initialized; 92 | int frame_counter; 93 | int frame_rate_lock; 94 | #ifdef _WIN32 95 | HANDLE waitable_timer; 96 | #endif 97 | }; 98 | 99 | 100 | frametimer_t* frametimer_create( void* memctx ) 101 | { 102 | frametimer_t* frametimer = (frametimer_t*) FRAMETIMER_MALLOC( memctx, sizeof( frametimer_t ) ); 103 | #ifdef _WIN32 104 | #ifndef __TINYC__ 105 | TIMECAPS tc; 106 | if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) 107 | timeBeginPeriod( tc.wPeriodMin ); 108 | #endif 109 | frametimer->waitable_timer = CreateWaitableTimer(NULL, TRUE, NULL); 110 | #endif 111 | 112 | frametimer->memctx = memctx; 113 | frametimer->initialized = 0; 114 | 115 | #ifdef _WIN32 116 | LARGE_INTEGER f; 117 | QueryPerformanceFrequency( &f ); 118 | frametimer->clock_freq = (FRAMETIMER_U64) f.QuadPart; 119 | #else 120 | frametimer->clock_freq = 1000000000ull; 121 | #endif 122 | 123 | frametimer->prev_clock = 0; 124 | frametimer->delta_time = 0.0f; 125 | frametimer->frame_counter = 0; 126 | frametimer->frame_rate_lock = 0; 127 | return frametimer; 128 | } 129 | 130 | 131 | void frametimer_destroy( frametimer_t* frametimer ) 132 | { 133 | #ifdef _WIN32 134 | CloseHandle( frametimer->waitable_timer ); 135 | #ifndef __TINYC__ 136 | TIMECAPS tc; 137 | if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) 138 | timeEndPeriod( tc.wPeriodMin ); 139 | #endif 140 | #endif 141 | FRAMETIMER_FREE( frametimer->memctx, frametimer ); 142 | } 143 | 144 | 145 | void frametimer_lock_rate( frametimer_t* frametimer, int fps ) 146 | { 147 | frametimer->frame_rate_lock = ( fps > 0 && fps < 5 ) ? 5 : fps < 0 ? 0 : fps; 148 | } 149 | 150 | 151 | float frametimer_update( frametimer_t* frametimer ) 152 | { 153 | if( !frametimer->initialized ) 154 | { 155 | #ifdef _WIN32 156 | LARGE_INTEGER c; 157 | QueryPerformanceCounter( &c ); 158 | frametimer->prev_clock = (FRAMETIMER_U64) c.QuadPart; 159 | #elif __APPLE__ 160 | frametimer->prev_clock = clock_gettime_nsec_np( CLOCK_UPTIME_RAW ); 161 | #else 162 | struct timespec t; 163 | clock_gettime( CLOCK_MONOTONIC_RAW, &t ); 164 | frametimer->prev_clock = (FRAMETIMER_U64)t.tv_sec; 165 | frametimer->prev_clock *= 1000000000ull; 166 | frametimer->prev_clock += (FRAMETIMER_U64)t.tv_nsec; 167 | #endif 168 | frametimer->initialized = 1; 169 | } 170 | 171 | ++frametimer->frame_counter; 172 | 173 | 174 | FRAMETIMER_U64 curr_clock = 0ULL; 175 | #ifdef _WIN32 176 | LARGE_INTEGER c; 177 | QueryPerformanceCounter( &c ); 178 | curr_clock = (FRAMETIMER_U64) c.QuadPart; 179 | #elif __APPLE__ 180 | curr_clock = (FRAMETIMER_U64) clock_gettime_nsec_np( CLOCK_UPTIME_RAW ); 181 | #else 182 | struct timespec t; 183 | clock_gettime( CLOCK_MONOTONIC_RAW, &t ); 184 | curr_clock = (FRAMETIMER_U64)t.tv_sec; 185 | curr_clock *= 1000000000ull; 186 | curr_clock += (FRAMETIMER_U64)t.tv_nsec; 187 | #endif 188 | 189 | if( frametimer->frame_rate_lock > 0 ) 190 | { 191 | FRAMETIMER_U64 delta = 0ULL; 192 | if( curr_clock > frametimer->prev_clock ) 193 | delta = curr_clock - frametimer->prev_clock - 1ULL; 194 | if( delta < frametimer->clock_freq / frametimer->frame_rate_lock ) 195 | { 196 | FRAMETIMER_U64 wait = ( frametimer->clock_freq / frametimer->frame_rate_lock ) - delta; 197 | #ifdef _WIN32 198 | if( wait > 0 ) 199 | { 200 | LARGE_INTEGER due_time; 201 | due_time.QuadPart = - (LONGLONG) ( ( 10000000ULL * wait ) / frametimer->clock_freq ) ; 202 | 203 | SetWaitableTimer( frametimer->waitable_timer, &due_time, 0, 0, 0, FALSE ); 204 | WaitForSingleObject( frametimer->waitable_timer, 200 ); // wait long enough for timer to trigger ( 200ms == 5fps ) 205 | CancelWaitableTimer( frametimer->waitable_timer ); // in case we timed out 206 | } 207 | #else 208 | struct timespec t = { 0, 0 }; 209 | t.tv_nsec = wait; 210 | while( t.tv_nsec > 0 ) 211 | { 212 | struct timespec r = { 0, 0 }; 213 | if( nanosleep( &t, &r ) >= 0 ) break; 214 | t = r; 215 | } 216 | #endif 217 | curr_clock += wait; 218 | } 219 | } 220 | 221 | FRAMETIMER_U64 delta_clock = 0ULL; 222 | if( curr_clock > frametimer->prev_clock ) delta_clock = curr_clock - frametimer->prev_clock; 223 | 224 | // Cap delta time if it is too high (running at less than 5 fps ) or things will jump 225 | // like crazy on occasional long stalls. 226 | if( delta_clock > frametimer->clock_freq / 5ULL ) delta_clock = frametimer->clock_freq / 5ULL; // Cap to 5 fps 227 | 228 | float delta_time = (float) ( ( (double) delta_clock ) / ( (double) frametimer->clock_freq ) ); 229 | 230 | frametimer->delta_time = delta_time; 231 | frametimer->prev_clock = curr_clock; 232 | 233 | return delta_time; 234 | } 235 | 236 | 237 | float frametimer_delta_time( frametimer_t* frametimer ) 238 | { 239 | return frametimer->delta_time; 240 | } 241 | 242 | 243 | int frametimer_frame_counter( frametimer_t* frametimer ) 244 | { 245 | return frametimer->frame_counter; 246 | } 247 | 248 | 249 | #endif /* FRAMETIMER_IMPLEMENTATION */ 250 | 251 | 252 | /* 253 | ------------------------------------------------------------------------------ 254 | 255 | This software is available under 2 licenses - you may choose the one you like. 256 | 257 | ------------------------------------------------------------------------------ 258 | 259 | ALTERNATIVE A - MIT License 260 | 261 | Copyright (c) 2015 Mattias Gustavsson 262 | 263 | Permission is hereby granted, free of charge, to any person obtaining a copy of 264 | this software and associated documentation files (the "Software"), to deal in 265 | the Software without restriction, including without limitation the rights to 266 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 267 | of the Software, and to permit persons to whom the Software is furnished to do 268 | so, subject to the following conditions: 269 | 270 | The above copyright notice and this permission notice shall be included in all 271 | copies or substantial portions of the Software. 272 | 273 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 274 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 275 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 276 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 277 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 278 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 279 | SOFTWARE. 280 | 281 | ------------------------------------------------------------------------------ 282 | 283 | ALTERNATIVE B - Public Domain (www.unlicense.org) 284 | 285 | This is free and unencumbered software released into the public domain. 286 | 287 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 288 | software, either in source code form or as a compiled binary, for any purpose, 289 | commercial or non-commercial, and by any means. 290 | 291 | In jurisdictions that recognize copyright laws, the author or authors of this 292 | software dedicate any and all copyright interest in the software to the public 293 | domain. We make this dedication for the benefit of the public at large and to 294 | the detriment of our heirs and successors. We intend this dedication to be an 295 | overt act of relinquishment in perpetuity of all present and future rights to 296 | this software under copyright law. 297 | 298 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 299 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 300 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 301 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 302 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 303 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 304 | 305 | ------------------------------------------------------------------------------ 306 | */ 307 | -------------------------------------------------------------------------------- /source/pixie/ease.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | ease.h - v0.1 - ease in/out interpolation functions for C/C++. 7 | 8 | Do this: 9 | #define EASE_IMPLEMENTATION 10 | before you include this file in *one* C/C++ file to create the implementation. 11 | 12 | ------------------------------------------------------------------------------ 13 | 14 | Based on http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf 15 | Note that in this code, input and output is in normalized 0.0 to 1.0 range. 16 | 17 | */ 18 | 19 | #ifndef ease_h 20 | #define ease_h 21 | 22 | typedef float (*ease_func_t)( float ); 23 | 24 | float ease_linear( float t ); 25 | float ease_smoothstep( float t ); 26 | float ease_smootherstep( float t ); 27 | 28 | float ease_out_quad( float t ); 29 | float ease_out_back( float t ); 30 | float ease_out_bounce( float t ); 31 | float ease_out_sine( float t ); 32 | float ease_out_elastic( float t ); 33 | float ease_out_expo( float t ); 34 | float ease_out_cubic( float t ); 35 | float ease_out_quart( float t ); 36 | float ease_out_quint( float t ); 37 | float ease_out_circle( float t ); 38 | 39 | float ease_in_quad( float t ); 40 | float ease_in_back( float t ); 41 | float ease_in_bounce( float t ); 42 | float ease_in_sine( float t ); 43 | float ease_in_elastic( float t ); 44 | float ease_in_expo( float t ); 45 | float ease_in_cubic( float t ); 46 | float ease_in_quart( float t ); 47 | float ease_in_quint( float t ); 48 | float ease_in_circle( float t ); 49 | 50 | float ease_in_out_quad( float t ); 51 | float ease_in_out_back( float t ); 52 | float ease_in_out_bounce( float t ); 53 | float ease_in_out_sine( float t ); 54 | float ease_in_out_elastic( float t ); 55 | float ease_in_out_expo( float t ); 56 | float ease_in_out_cubic( float t ); 57 | float ease_in_out_quart( float t ); 58 | float ease_in_out_quint( float t ); 59 | float ease_in_out_circle( float t ); 60 | 61 | #endif /* ease_h */ 62 | 63 | 64 | /* 65 | ---------------------- 66 | IMPLEMENTATION 67 | ---------------------- 68 | */ 69 | 70 | #ifdef EASE_IMPLEMENTATION 71 | #undef EASE_IMPLEMENTATION 72 | 73 | #if !defined( EASE_ACOS ) || !defined( EASE_POW ) || !defined( EASE_SIN ) || !defined( EASE_SQRT ) 74 | #define _CRT_NONSTDC_NO_DEPRECATE 75 | #define _CRT_SECURE_NO_WARNINGS 76 | #pragma warning( push ) 77 | #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' 78 | #include 79 | #pragma warning( pop ) 80 | #endif 81 | 82 | #ifndef EASE_ACOS 83 | #ifdef __TINYC__ 84 | #define EASE_ACOS( x ) ( (float) acos( (double) x ) ) 85 | #else 86 | #define EASE_ACOS( x ) acosf( x ) 87 | #endif 88 | #endif 89 | 90 | #ifndef EASE_COS 91 | #ifdef __TINYC__ 92 | #define EASE_COS( x ) ( (float) cos( (double) x ) ) 93 | #else 94 | #define EASE_COS( x ) cosf( x ) 95 | #endif 96 | #endif 97 | 98 | #ifndef EASE_POW 99 | #ifdef __TINYC__ 100 | #define EASE_POW( x, y ) ( (float) pow( (double) x, (double) y ) ) 101 | #else 102 | #define EASE_POW( x, y ) powf( x, y ) 103 | #endif 104 | #endif 105 | 106 | #ifndef EASE_SIN 107 | #ifdef __TINYC__ 108 | #define EASE_SIN( x ) ( (float) sin( (double) x ) ) 109 | #else 110 | #define EASE_SIN( x ) sinf( x ) 111 | #endif 112 | #endif 113 | 114 | #ifndef EASE_SQRT 115 | #ifdef __TINYC__ 116 | #define EASE_SQRT( x ) ( (float) sqrt( (double) x ) ) 117 | #else 118 | #define EASE_SQRT( x ) sqrtf( x ) 119 | #endif 120 | #endif 121 | 122 | 123 | float ease_linear( float t ) { 124 | return t; 125 | } 126 | 127 | 128 | float ease_smoothstep( float t ) { 129 | return t * t * ( 3.0f - 2.0f * t ); 130 | } 131 | 132 | 133 | float ease_smootherstep( float t ) { 134 | return t * t * t * ( t * ( t * 6.0f - 15.0f ) + 10.0f ); 135 | } 136 | 137 | 138 | float ease_out_quad( float t ) { 139 | return -t * ( t - 2.0f ); 140 | } 141 | 142 | 143 | float ease_in_quad( float t ) { 144 | return t * t; 145 | } 146 | 147 | 148 | float ease_in_out_quad( float t ) { 149 | return ( ( t *= 2.0f ) < 1.0f ) 150 | ? ( 0.5f * t * t ) 151 | : ( -0.5f * ( ( t - 1.0f ) * ( t - 3.0f ) - 1.0f ) ); 152 | } 153 | 154 | 155 | float ease_out_back( float t ) { 156 | t -= 1.0f; 157 | return ( t * t * ( ( 1.70158f + 1.0f ) * t + 1.70158f ) + 1.0f ); 158 | } 159 | 160 | 161 | float ease_in_back( float t ) { 162 | return t * t * ( ( 1.70158f + 1.0f ) * t - 1.70158f ); 163 | } 164 | 165 | 166 | float ease_in_out_back( float t ) { 167 | float s = 1.70158f * 1.525f; 168 | t *= 2.0f; 169 | 170 | if( t < 1.0f ) { 171 | return 0.5f * ( t * t * ( ( s + 1.0f ) * t - s ) ); 172 | } else { 173 | t -= 2.0f; 174 | return 0.5f * ( t * t * ( ( s + 1.0f ) * t + s ) + 2.0f ); 175 | } 176 | } 177 | 178 | 179 | float ease_out_bounce( float t ) { 180 | if( t < ( 1.0f / 2.75f ) ) { 181 | return 7.5625f * t * t; 182 | } else if( t < ( 2.0f / 2.75f ) ) { 183 | t -= ( 1.50f / 2.75f ); 184 | return 7.5625f * t * t + 0.75f; 185 | } else if( t < ( 2.5f / 2.75f ) ) { 186 | t -= ( 2.25f / 2.75f ); 187 | return 7.5625f * t * t + 0.9375f; 188 | } else { 189 | t -= ( 2.625f / 2.75f ); 190 | return 7.5625f * t * t + 0.984375f; 191 | } 192 | } 193 | 194 | 195 | float ease_in_bounce( float t ) { 196 | return 1.0f - ease_out_bounce( 1.0f - t ); 197 | } 198 | 199 | 200 | float ease_in_out_bounce( float t ) { 201 | return ( t < 0.5f ) 202 | ? ( ease_in_bounce( t * 2.0f ) * 0.5f ) 203 | : ( ease_out_bounce( t * 2.0f - 1.0f ) * 0.5f + 0.5f ); 204 | } 205 | 206 | 207 | float ease_out_sine( float t ) { 208 | return EASE_SIN( t * EASE_ACOS( 0.0f ) ); 209 | } 210 | 211 | 212 | float ease_in_sine( float t ) { 213 | return 1.0f - EASE_COS( t * EASE_ACOS( 0.0f ) ); 214 | } 215 | 216 | 217 | float ease_in_out_sine( float t ) { 218 | return -( EASE_COS( EASE_ACOS( -1.0f ) * t ) - 1.0f ) / 2.0f; 219 | } 220 | 221 | 222 | float ease_out_elastic( float t ) { 223 | if( t == 0.0f ) { 224 | return 0.0f; 225 | } 226 | if( t == 1.0f ) { 227 | return 1.0f; 228 | } 229 | 230 | float p = 0.4f; 231 | float s = p / 4.0f; 232 | return ( EASE_POW( 2.0f, -10.0f * t ) * EASE_SIN( ( t - s ) * ( 2.0f * EASE_ACOS( -1.0f ) ) / p ) + 1.0f ); 233 | } 234 | 235 | 236 | float ease_in_elastic( float t ) { 237 | if( t == 0.0f ) { 238 | return 0.0f; 239 | } 240 | if( t == 1.0f ) { 241 | return 1.0f; 242 | } 243 | 244 | float p = 0.4f; 245 | float s = p / 4.0f; 246 | t -= 1.0f; 247 | return -( EASE_POW( 2.0f, 10.0f * t ) * EASE_SIN( ( t - s ) * ( 2.0f * EASE_ACOS( -1.0f ) ) / p ) ); 248 | } 249 | 250 | 251 | float ease_in_out_elastic( float t ) { 252 | if( t == 0.0f) { 253 | return 0.0f; 254 | } 255 | t *= 2.0f; 256 | if( t == 2.0f ) { 257 | return 1.0f; 258 | } 259 | 260 | float p = 0.3f * 1.5f; 261 | float s = p / 4.0f; 262 | if( t < 1.0f ) { 263 | t -= 1.0f; 264 | return -0.5f * ( EASE_POW( 2.0f, 10.0f * t ) * EASE_SIN( ( t - s ) * ( 2.0f * EASE_ACOS( -1.0f ) ) / p ) ); 265 | } else { 266 | t -= 1.0f; 267 | return EASE_POW( 2.0f, -10.0f * t ) * EASE_SIN( ( t - s ) * ( 2.0f * EASE_ACOS( -1.0f ) ) / p) * 0.5f + 1.0f; 268 | } 269 | } 270 | 271 | 272 | float ease_out_expo( float t ) { 273 | return t == 1.0f 274 | ? 1.0f 275 | : ( 1.0f - EASE_POW( 2.0f, -10.0f * t ) ); 276 | } 277 | 278 | 279 | float ease_in_expo( float t ) { 280 | return t == 0.0f 281 | ? 0.0f 282 | : EASE_POW( 2.0f, 10.0f * ( t - 1.0f ) ); 283 | } 284 | 285 | 286 | float ease_in_out_expo( float t ) { 287 | if( t == 0.0f ) { 288 | return 0.0f; 289 | } 290 | if( t == 1.0f ) { 291 | return 1.0f; 292 | } 293 | t *= 2.0f; 294 | if( t < 1.0f ) { 295 | return 0.5f * EASE_POW( 2.0f, 10.0f * ( t - 1.0f ) ); 296 | } else { 297 | return 0.5f * ( 2.0f - EASE_POW( 2.0f, -10.0f * --t ) ); 298 | } 299 | } 300 | 301 | 302 | float ease_out_cubic( float t ) { 303 | t -= 1.0f; 304 | return t * t * t + 1.0f; 305 | } 306 | 307 | 308 | float ease_in_cubic( float t ) { 309 | return t * t * t; 310 | } 311 | 312 | 313 | float ease_in_out_cubic( float t ) { 314 | t *= 2.0f; 315 | if( t < 1.0f ) { 316 | return 0.5f * t * t * t; 317 | } else { 318 | t -= 2.0f; 319 | return 0.5f * ( t * t * t + 2.0f ); 320 | } 321 | } 322 | 323 | 324 | float ease_out_quart( float t ) { 325 | t -= 1.0f; 326 | return -( t * t * t * t - 1.0f ); 327 | } 328 | 329 | 330 | float ease_in_quart( float t ) { 331 | return t * t * t * t; 332 | } 333 | 334 | 335 | float ease_in_out_quart( float t ) { 336 | t *= 2.0f; 337 | if( t < 1.0f ) { 338 | return 0.5f * t * t * t * t; 339 | } else { 340 | t -= 2.0f; 341 | return -0.5f * ( t * t * t * t - 2.0f ); 342 | } 343 | } 344 | 345 | 346 | float ease_out_quint( float t ) { 347 | t -= 1.0f; 348 | return t * t * t * t * t + 1.0f; 349 | } 350 | 351 | 352 | float ease_in_quint( float t ) { 353 | return t * t * t * t * t; 354 | } 355 | 356 | 357 | float ease_in_out_quint( float t ) { 358 | t *= 2.0f; 359 | if( t < 1.0f ) { 360 | return 0.5f * t * t * t * t * t; 361 | } else { 362 | t -= 2.0f; 363 | return 0.5f * ( t * t * t * t * t + 2.0f ); 364 | } 365 | } 366 | 367 | 368 | float ease_out_circle( float t ) { 369 | t -= 1.0f; 370 | return EASE_SQRT( 1.0f - t * t ); 371 | } 372 | 373 | 374 | float ease_in_circle( float t ) { 375 | return 1.0f - EASE_SQRT( 1.0f - t * t ); 376 | } 377 | 378 | 379 | float ease_in_out_circle( float t ) { 380 | t *= 2.0f; 381 | if( t < 1.0f ) { 382 | return 0.5f * (1.0f - EASE_SQRT( 1.0f - t * t ) ); 383 | } else { 384 | t -= 2.0f; 385 | return 0.5f * ( EASE_SQRT( 1.0f - t * t ) + 1.0f ); 386 | } 387 | } 388 | 389 | 390 | #endif /* EASE_IMPLEMENTATION */ 391 | 392 | /* 393 | ------------------------------------------------------------------------------ 394 | 395 | This software is available under 2 licenses - you may choose the one you like. 396 | 397 | ------------------------------------------------------------------------------ 398 | 399 | ALTERNATIVE A - MIT License 400 | 401 | Copyright (c) 2015 Mattias Gustavsson 402 | 403 | Permission is hereby granted, free of charge, to any person obtaining a copy of 404 | this software and associated documentation files (the "Software"), to deal in 405 | the Software without restriction, including without limitation the rights to 406 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 407 | of the Software, and to permit persons to whom the Software is furnished to do 408 | so, subject to the following conditions: 409 | 410 | The above copyright notice and this permission notice shall be included in all 411 | copies or substantial portions of the Software. 412 | 413 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 414 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 415 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 416 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 417 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 418 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 419 | SOFTWARE. 420 | 421 | ------------------------------------------------------------------------------ 422 | 423 | ALTERNATIVE B - Public Domain (www.unlicense.org) 424 | 425 | This is free and unencumbered software released into the public domain. 426 | 427 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 428 | software, either in source code form or as a compiled binary, for any purpose, 429 | commercial or non-commercial, and by any means. 430 | 431 | In jurisdictions that recognize copyright laws, the author or authors of this 432 | software dedicate any and all copyright interest in the software to the public 433 | domain. We make this dedication for the benefit of the public at large and to 434 | the detriment of our heirs and successors. We intend this dedication to be an 435 | overt act of relinquishment in perpetuity of all present and future rights to 436 | this software under copyright law. 437 | 438 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 439 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 440 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 441 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 442 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 443 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 444 | 445 | ------------------------------------------------------------------------------ 446 | */ 447 | -------------------------------------------------------------------------------- /source/img.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | img.h - v0.1 - Image processing functions for C/C++. 7 | 8 | Do this: 9 | #define IMG_IMPLEMENTATION 10 | before you include this file in *one* C/C++ file to create the implementation. 11 | */ 12 | 13 | #ifndef img_h 14 | #define img_h 15 | 16 | #ifndef IMG_U32 17 | #define IMG_U32 unsigned int 18 | #endif 19 | 20 | 21 | typedef struct img_rgba_t { 22 | float r; 23 | float g; 24 | float b; 25 | float a; 26 | } img_rgba_t; 27 | 28 | typedef struct img_t { 29 | int width; 30 | int height; 31 | img_rgba_t* pixels; 32 | } img_t; 33 | 34 | typedef struct img_hsva_t { 35 | float h; 36 | float s; 37 | float v; 38 | float a; 39 | } img_hsva_t; 40 | 41 | img_t img_from_abgr32( IMG_U32* abgr, int width, int height ); 42 | void img_free( img_t* img ); 43 | 44 | void img_to_argb32( img_t* img, IMG_U32* abgr ); 45 | img_rgba_t img_sample_clamp( img_t* img, float u, float v ); 46 | 47 | img_rgba_t img_rgba_lerp( img_rgba_t a, img_rgba_t b, float s ); 48 | img_hsva_t img_rgba_to_hsva( img_rgba_t rgb ); 49 | img_rgba_t img_hsva_to_rgba( img_hsva_t hsv ); 50 | 51 | void img_adjust_brightness( img_t* img, float value ); 52 | void img_adjust_contrast( img_t* img, float value ); 53 | void img_adjust_saturation( img_t* img, float value ); 54 | void img_sharpen( img_t* img, float radius, float blend ); 55 | 56 | 57 | #endif /* img_h */ 58 | 59 | /* 60 | ---------------------- 61 | IMPLEMENTATION 62 | ---------------------- 63 | */ 64 | 65 | #ifdef IMG_IMPLEMENTATION 66 | #undef IMG_IMPLEMENTATION 67 | 68 | #define _CRT_NONSTDC_NO_DEPRECATE 69 | #define _CRT_SECURE_NO_WARNINGS 70 | #include 71 | 72 | 73 | #if !defined( IMG_POW ) || !defined( IMG_FLOOR ) 74 | #define _CRT_NONSTDC_NO_DEPRECATE 75 | #define _CRT_SECURE_NO_WARNINGS 76 | #pragma warning( push ) 77 | #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' 78 | #include 79 | #pragma warning( pop ) 80 | #endif 81 | 82 | 83 | #ifndef IMG_POW 84 | #ifdef __TINYC__ 85 | #define IMG_POW( x, y ) ( (float) pow( (double) x, (double) y ) ) 86 | #else 87 | #define IMG_POW( x, y ) powf( x, y ) 88 | #endif 89 | #endif 90 | 91 | #ifndef IMG_FLOOR 92 | #ifdef __TINYC__ 93 | #define IMG_FLOOR( x ) ( (float) floor( (double) x ) ) 94 | #else 95 | #define IMG_FLOOR( x ) floorf( x ) 96 | #endif 97 | #endif 98 | 99 | 100 | img_t img_from_abgr32( IMG_U32* abgr, int width, int height ) { 101 | img_t img; 102 | img.width = width; 103 | img.height = height; 104 | img.pixels = (img_rgba_t*) malloc( sizeof( img_rgba_t ) * width * height ); 105 | for( int i = 0; i < width * height; ++i ) { 106 | IMG_U32 p = abgr[ i ]; 107 | IMG_U32 b = ( p ) & 0xff; 108 | IMG_U32 g = ( p >> 8 ) & 0xff; 109 | IMG_U32 r = ( p >> 16 ) & 0xff; 110 | IMG_U32 a = ( p >> 24 ) & 0xff; 111 | img_rgba_t* f = &img.pixels[ i ]; 112 | f->r = ( (float) r ) / 255.0f; 113 | f->g = ( (float) g ) / 255.0f; 114 | f->b = ( (float) b ) / 255.0f; 115 | f->a = ( (float) a ) / 255.0f; 116 | float const GAMMA_TO_LINEAR = 2.2f; 117 | f->r = IMG_POW( f->r, GAMMA_TO_LINEAR ); 118 | f->g = IMG_POW( f->g, GAMMA_TO_LINEAR ); 119 | f->b = IMG_POW( f->b, GAMMA_TO_LINEAR ); 120 | } 121 | return img; 122 | } 123 | 124 | 125 | void img_free( img_t* img ) { 126 | free( img->pixels ); 127 | img->pixels = NULL; 128 | } 129 | 130 | 131 | float img_clamp( float x, float low, float high ) { 132 | return x < low ? low : x > high ? high : x; 133 | } 134 | 135 | 136 | void img_to_argb32( img_t* img, IMG_U32* abgr ) { 137 | for( int i = 0; i < img->width * img->height; ++i ) { 138 | img_rgba_t p = img->pixels[ i ]; 139 | float const LINEAR_TO_GAMMA = 1.0f / 2.2f; 140 | p.r = IMG_POW( img_clamp( p.r, 0.0f, 1.0f ), LINEAR_TO_GAMMA ); 141 | p.g = IMG_POW( img_clamp( p.g, 0.0f, 1.0f ), LINEAR_TO_GAMMA ); 142 | p.b = IMG_POW( img_clamp( p.b, 0.0f, 1.0f ), LINEAR_TO_GAMMA ); 143 | p.a = img_clamp( p.a, 0.0f, 1.0f ); 144 | IMG_U32 r = (IMG_U32)( p.r * 256.0f ); 145 | IMG_U32 g = (IMG_U32)( p.g * 256.0f ); 146 | IMG_U32 b = (IMG_U32)( p.b * 256.0f ); 147 | IMG_U32 a = (IMG_U32)( p.a * 256.0f ); 148 | r = r > 255 ? 255 : r; 149 | g = g > 255 ? 255 : g; 150 | b = b > 255 ? 255 : b; 151 | a = a > 255 ? 255 : a; 152 | abgr[ i ] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b ); 153 | } 154 | } 155 | 156 | 157 | 158 | img_rgba_t img_rgba_lerp( img_rgba_t a, img_rgba_t b, float s ) { 159 | img_rgba_t c; 160 | c.r = a.r + ( b.r - a.r ) * s; 161 | c.g = a.g + ( b.g - a.g ) * s; 162 | c.b = a.b + ( b.b - a.b ) * s; 163 | c.a = a.a + ( b.a - a.a ) * s; 164 | return c; 165 | } 166 | 167 | 168 | img_rgba_t img_sample_clamp( img_t* img, float u, float v ) { 169 | float maxw = (float) ( img->width - 1 ); 170 | float maxh = (float) ( img->height - 1 ); 171 | 172 | float fu = IMG_FLOOR( u ); 173 | float du = u - fu; 174 | float fv = IMG_FLOOR( v ); 175 | float dv = v - fv; 176 | 177 | int x1 = (int) img_clamp( fu, 0.0f, maxw ); 178 | int y1 = (int) img_clamp( fv, 0.0f, maxh ); 179 | int x2 = (int) img_clamp( fu + 1.0f, 0.0f, maxw ); 180 | int y2 = (int) img_clamp( fv + 1.0f, 0.0f, maxh ); 181 | 182 | img_rgba_t c1 = img->pixels[ x1 + y1 * img->width ]; 183 | img_rgba_t c2 = img->pixels[ x2 + y1 * img->width ]; 184 | img_rgba_t c3 = img->pixels[ x1 + y2 * img->width ]; 185 | img_rgba_t c4 = img->pixels[ x2 + y2 * img->width ]; 186 | 187 | return img_rgba_lerp( img_rgba_lerp( c1, c2, du ), img_rgba_lerp( c3, c4, du ), dv ); 188 | } 189 | 190 | 191 | void img_adjust_brightness( img_t* img, float value ) { 192 | for( int i = 0; i < img->width * img->height; ++i ) { 193 | img_rgba_t* p = &img->pixels[ i ]; 194 | p->r = p->r + value; 195 | p->g = p->g + value; 196 | p->b = p->b + value; 197 | } 198 | } 199 | 200 | void img_adjust_contrast( img_t* img, float value ) { 201 | for( int i = 0; i < img->width * img->height; ++i ) { 202 | img_rgba_t* p = &img->pixels[ i ]; 203 | p->r = ( p->r - 0.5f ) * value + 0.5f; 204 | p->g = ( p->g - 0.5f ) * value + 0.5f; 205 | p->b = ( p->b - 0.5f ) * value + 0.5f; 206 | } 207 | } 208 | 209 | #define img_min( x, y ) ( (x) < (y) ? (x) : (y) ) 210 | #define img_max( x, y ) ( (x) > (y) ? (x) : (y) ) 211 | 212 | img_hsva_t img_rgba_to_hsva( img_rgba_t rgb ) { 213 | img_hsva_t hsv; 214 | hsv.a = rgb.a; 215 | 216 | float cmin = img_min( rgb.r, img_min( rgb.g, rgb.b ) ); //Min. value of RGB 217 | float cmax = img_max( rgb.r, img_max( rgb.g, rgb.b ) ); //Max. value of RGB 218 | float cdel = cmax - cmin; //Delta RGB value 219 | 220 | hsv.v = cmax; 221 | 222 | if( cdel == 0 ) { //This is a gray, no chroma... 223 | hsv.h = 0; //HSV results from 0 to 1 224 | hsv.s = 0; 225 | } else { //Chromatic data... 226 | hsv.s = cdel / cmax; 227 | 228 | float rdel = ( ( ( cmax - rgb.r ) / 6.0f ) + ( cdel / 2.0f ) ) / cdel; 229 | float gdel = ( ( ( cmax - rgb.g ) / 6.0f ) + ( cdel / 2.0f ) ) / cdel; 230 | float bdel = ( ( ( cmax - rgb.b ) / 6.0f ) + ( cdel / 2.0f ) ) / cdel; 231 | 232 | if( rgb.r == cmax ) { 233 | hsv.h = bdel - gdel; 234 | } else if( rgb.g == cmax ) { 235 | hsv.h = ( 1.0f / 3.0f ) + rdel - bdel; 236 | } else { 237 | hsv.h = ( 2.0f / 3.0f ) + gdel - rdel; 238 | } 239 | 240 | if ( hsv.h < 0.0f ) { 241 | hsv.h += 1.0f; 242 | } 243 | if ( hsv.h > 1.0f ) { 244 | hsv.h -= 1.0f; 245 | } 246 | } 247 | 248 | hsv.h = hsv.h; 249 | hsv.s = hsv.s; 250 | hsv.v = hsv.v; 251 | return hsv; 252 | } 253 | 254 | 255 | img_rgba_t img_hsva_to_rgba( img_hsva_t hsv ) { 256 | img_rgba_t rgb; 257 | rgb.a = hsv.a; 258 | 259 | if( hsv.s == 0.0f ) { // HSV from 0 to 1 260 | rgb.r = hsv.v; 261 | rgb.g = hsv.v; 262 | rgb.b = hsv.v; 263 | } else { 264 | float h = hsv.h * 6.0f; 265 | if( h == 6.0f ) { 266 | h = 0.0f; //H must be < 1 267 | } 268 | float i = IMG_FLOOR( h ); 269 | float v1 = hsv.v * ( 1.0f - hsv.s ); 270 | float v2 = hsv.v * ( 1.0f - hsv.s * ( h - i ) ); 271 | float v3 = hsv.v * ( 1.0f - hsv.s * ( 1.0f - ( h - i ) ) ); 272 | 273 | switch( (int) i ) { 274 | case 0: { rgb.r = hsv.v; rgb.g = v3 ; rgb.b = v1 ; } break; 275 | case 1: { rgb.r = v2 ; rgb.g = hsv.v; rgb.b = v1 ; } break; 276 | case 2: { rgb.r = v1 ; rgb.g = hsv.v; rgb.b = v3 ; } break; 277 | case 3: { rgb.r = v1 ; rgb.g = v2 ; rgb.b = hsv.v; } break; 278 | case 4: { rgb.r = v3 ; rgb.g = v1 ; rgb.b = hsv.v; } break; 279 | default:{ rgb.r = hsv.v; rgb.g = v1 ; rgb.b = v2 ; } break; 280 | } 281 | } 282 | 283 | rgb.r = rgb.r; 284 | rgb.g = rgb.g; 285 | rgb.b = rgb.b; 286 | return rgb; 287 | } 288 | 289 | 290 | void img_adjust_saturation( img_t* img, float value ) { 291 | for( int i = 0; i < img->width * img->height; ++i ) { 292 | img_rgba_t* p = &img->pixels[ i ]; 293 | img_hsva_t hsv = img_rgba_to_hsva( *p ); 294 | hsv.s = img_clamp( hsv.s + value, 0.0f, 1.0f ); 295 | *p = img_hsva_to_rgba( hsv ); 296 | } 297 | } 298 | 299 | 300 | void img_sharpen( img_t* img, float radius, float blend ) { 301 | float filter[ 9 ] = { 302 | -1.0f, -1.0f, -1.0f, 303 | -1.0f, 9.0f, -1.0f, 304 | -1.0f, -1.0f, -1.0f, 305 | }; 306 | 307 | img_rgba_t* result = (img_rgba_t*) malloc( img->width * img->height * sizeof( img_rgba_t ) ); 308 | 309 | img_rgba_t* src = img->pixels; 310 | img_rgba_t* dst = result; 311 | for( int y = 0; y < img->height; ++y ) { 312 | for( int x = 0; x < img->width; ++x ) { 313 | if( src->a > 0.0f ) { 314 | img_rgba_t acc = { 0.0f, 0.0f, 0.0f, 0.0f }; 315 | for( int fx = -1; fx <= 1; ++fx ) { 316 | for( int fy = -1; fy <= 1; ++fy ) { 317 | float u = ( (float) x ) + ( (float) fx ) * radius; 318 | float v = ( (float) y ) + ( (float) fy ) * radius; 319 | img_rgba_t s = img_sample_clamp( img, u, v ); 320 | if( s.a < 0.0001f ) { 321 | s = *src; 322 | } 323 | acc.r += s.r * ( filter[ ( 1 + fx ) + ( 1 + fy ) * 3 ] ); 324 | acc.g += s.g * ( filter[ ( 1 + fx ) + ( 1 + fy ) * 3 ] ); 325 | acc.b += s.b * ( filter[ ( 1 + fx ) + ( 1 + fy ) * 3 ] ); 326 | } 327 | } 328 | *dst = img_rgba_lerp( *src, acc, blend ); 329 | dst->a = src->a; 330 | } else { 331 | *dst = *src; 332 | } 333 | ++src; 334 | ++dst; 335 | } 336 | } 337 | 338 | free( img->pixels ); 339 | img->pixels = result; 340 | } 341 | 342 | #endif /* IMG_IMPLEMENTATION */ 343 | 344 | /* 345 | ------------------------------------------------------------------------------ 346 | 347 | This software is available under 2 licenses - you may choose the one you like. 348 | 349 | ------------------------------------------------------------------------------ 350 | 351 | ALTERNATIVE A - MIT License 352 | 353 | Copyright (c) 2019 Mattias Gustavsson 354 | 355 | Permission is hereby granted, free of charge, to any person obtaining a copy of 356 | this software and associated documentation files (the "Software"), to deal in 357 | the Software without restriction, including without limitation the rights to 358 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 359 | of the Software, and to permit persons to whom the Software is furnished to do 360 | so, subject to the following conditions: 361 | 362 | The above copyright notice and this permission notice shall be included in all 363 | copies or substantial portions of the Software. 364 | 365 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 366 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 367 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 368 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 369 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 370 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 371 | SOFTWARE. 372 | 373 | ------------------------------------------------------------------------------ 374 | 375 | ALTERNATIVE B - Public Domain (www.unlicense.org) 376 | 377 | This is free and unencumbered software released into the public domain. 378 | 379 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 380 | software, either in source code form or as a compiled binary, for any purpose, 381 | commercial or non-commercial, and by any means. 382 | 383 | In jurisdictions that recognize copyright laws, the author or authors of this 384 | software dedicate any and all copyright interest in the software to the public 385 | domain. We make this dedication for the benefit of the public at large and to 386 | the detriment of our heirs and successors. We intend this dedication to be an 387 | overt act of relinquishment in perpetuity of all present and future rights to 388 | this software under copyright law. 389 | 390 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 391 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 392 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 393 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 394 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 395 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 396 | 397 | ------------------------------------------------------------------------------ 398 | */ 399 | -------------------------------------------------------------------------------- /source/pixie/palrle.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | palrle.h - v0.1 - Run-length encoding of palettized bitmaps, for C/C++. 7 | */ 8 | 9 | #ifndef palrle_h 10 | #define palrle_h 11 | 12 | #ifndef PALRLE_U8 13 | #define PALRLE_U8 unsigned char 14 | #endif 15 | #ifndef PALRLE_U16 16 | #define PALRLE_U16 unsigned short 17 | #endif 18 | #ifndef PALRLE_U32 19 | #define PALRLE_U32 unsigned int 20 | #endif 21 | 22 | typedef struct palrle_data_t { 23 | PALRLE_U32 size; 24 | PALRLE_U16 width; 25 | PALRLE_U16 height; 26 | PALRLE_U16 xoffset; 27 | PALRLE_U16 yoffset; 28 | PALRLE_U16 hpitch; 29 | PALRLE_U16 vpitch; 30 | PALRLE_U16 palette_count; 31 | PALRLE_U8 data[ 1 ]; // "open" array 32 | } palrle_data_t; 33 | 34 | //palrle_data_t* palrle_encode( PALRLE_U8* pixels, int width, int height, PALRLE_U32* palette, int palette_count, void* memctx ); 35 | palrle_data_t* palrle_encode_mask( PALRLE_U8* pixels, PALRLE_U8* mask, int width, int height, PALRLE_U32* palette, int palette_count, void* memctx ); 36 | //palrle_data_t* palrle_encode_transparency_index( PALRLE_U8* pixels, PALRLE_U8 transparency_index, int width, int height, PALRLE_U32* palette, int palette_count, void* memctx ); 37 | 38 | void palrle_decode( palrle_data_t* rle_data, PALRLE_U8* pixels, PALRLE_U8* mask ); 39 | 40 | void palrle_blit( palrle_data_t* rle_data, int x, int y, PALRLE_U8* pixels, int width, int height ); 41 | 42 | void palrle_free( palrle_data_t* rle_data, void* memctx ); 43 | 44 | 45 | #endif /* palrle_h */ 46 | 47 | /* 48 | ---------------------- 49 | IMPLEMENTATION 50 | ---------------------- 51 | */ 52 | 53 | #ifdef PALRLE_IMPLEMENTATION 54 | #undef PALRLE_IMPLEMENTATION 55 | 56 | #ifndef PALRLE_MALLOC 57 | #define _CRT_NONSTDC_NO_DEPRECATE 58 | #define _CRT_SECURE_NO_WARNINGS 59 | #include 60 | #define PALRLE_MALLOC( ctx, size ) ( malloc( size ) ) 61 | #define PALRLE_FREE( ctx, ptr ) ( free( ptr ) ) 62 | #endif 63 | 64 | 65 | /* 66 | palrle_data_t* palrle_encode( PALRLE_U8* pixels, int width, int height, PALRLE_U32* palette, int palette_count, void* memctx ) { 67 | (void) pixels, width, height, memctx; 68 | return 0; 69 | } 70 | */ 71 | 72 | 73 | palrle_data_t* palrle_encode_mask( PALRLE_U8* pixels, PALRLE_U8* mask, int width, int height, PALRLE_U32* palette, int palette_count, void* memctx ) { 74 | (void) memctx; 75 | // Crop to smallest non-empty region 76 | int xmin = width; 77 | int xmax = 0; 78 | int ymin = height; 79 | int ymax = 0; 80 | for( int y = 0; y < height; ++y ) { 81 | for( int x = 0; x < width; ++x ) { 82 | if( mask[ x + y * width ] ) { 83 | if( x < xmin ) xmin = x; 84 | if( y < ymin ) ymin = y; 85 | if( x > xmax ) xmax = x; 86 | if( y > ymax ) ymax = y; 87 | } 88 | } 89 | } 90 | int hpitch = xmax - xmin + 1; 91 | int vpitch = ymax - ymin + 1; 92 | 93 | // Bitmap is completely empty, so just store the palette and dimensions 94 | if( hpitch <= 0 || vpitch <= 0 ) { 95 | size_t size = sizeof( palrle_data_t ) - sizeof( PALRLE_U8 ) + sizeof( PALRLE_U32 ) * palette_count; 96 | palrle_data_t* data = (palrle_data_t*) PALRLE_MALLOC( memctx, size ); 97 | data->size = (PALRLE_U32) size; 98 | data->width = (PALRLE_U16) width; 99 | data->height = (PALRLE_U16) height; 100 | data->xoffset = 0; 101 | data->yoffset = 0; 102 | data->hpitch = 0; 103 | data->vpitch = 0; 104 | data->palette_count = (PALRLE_U16) palette_count; 105 | if( palette ) memcpy( data->data, palette, sizeof( PALRLE_U32 ) * palette_count ); 106 | return data; 107 | } 108 | 109 | palrle_data_t* data = (palrle_data_t*) PALRLE_MALLOC( memctx, 110 | sizeof( palrle_data_t ) + // size for the struct itself 111 | sizeof( PALRLE_U32 ) * palette_count + // size for storing palette entries 112 | sizeof( PALRLE_U32 ) * vpitch + // size for storing the offset for each row 113 | hpitch * vpitch * 2 ); // assume worst case - we should never need more than twice the number of pixels 114 | memset( data, 0, sizeof( palrle_data_t ) + sizeof( PALRLE_U32 ) * palette_count + sizeof( PALRLE_U32 ) * vpitch + 115 | hpitch * vpitch * 2 ); 116 | 117 | 118 | data->size = (PALRLE_U32) ( sizeof( palrle_data_t ) - sizeof( PALRLE_U8 ) ); 119 | data->width = (PALRLE_U16) width; 120 | data->height = (PALRLE_U16) height; 121 | data->xoffset = (PALRLE_U16) xmin; 122 | data->yoffset = (PALRLE_U16) ymin; 123 | data->hpitch = (PALRLE_U16) hpitch; 124 | data->vpitch = (PALRLE_U16) vpitch; 125 | data->palette_count = (PALRLE_U16) palette_count; 126 | if( palette ) memcpy( data->data, palette, sizeof( PALRLE_U32 ) * palette_count ); 127 | 128 | int row_offset = (int)( sizeof( PALRLE_U32 ) * palette_count ); 129 | int rle_offset = (int)( row_offset + sizeof( PALRLE_U32 ) * vpitch ); 130 | for( int y = 0; y < vpitch; ++y ) { 131 | *( (PALRLE_U32*)&data->data[ row_offset ] ) = (PALRLE_U32) rle_offset; 132 | row_offset += sizeof( PALRLE_U32 ); 133 | int x = 0; 134 | while( x < hpitch ) { 135 | // add empty pixel count 136 | int empty = 0; 137 | while( x < hpitch ) { 138 | if( mask[ x + xmin + ( y + ymin ) * width ] != 0 ) break; 139 | if( empty >= 255 ) { 140 | data->data[ rle_offset++ ] = 255; // 255 empty pixels 141 | data->data[ rle_offset++ ] = 0; // 0 non-empty pixels 142 | empty = 0; 143 | } 144 | ++empty; 145 | ++x; 146 | } 147 | data->data[ rle_offset++ ] = (PALRLE_U8) empty; 148 | 149 | // add non-empty pixels 150 | int color = pixels[ x + xmin + ( y + ymin ) * width ]; 151 | int count = 0; 152 | int tx = x; 153 | while( tx < hpitch ) { 154 | if( mask[ tx + xmin + ( y + ymin ) * width ] == 0 ) break; 155 | if( pixels[ tx + xmin + ( y + ymin ) * width ] != color ) break; 156 | if( count >= 127 ) break; 157 | ++count; 158 | ++tx; 159 | } 160 | 161 | if( count == 0 ) { 162 | data->data[ rle_offset++ ] = 0; 163 | continue; 164 | } 165 | 166 | if( count > 2 ) { // store as a run 167 | data->data[ rle_offset++ ] = (PALRLE_U8) count; // number of pixels 168 | data->data[ rle_offset++ ] = (PALRLE_U8) color; // of palette index `color` 169 | x = tx; 170 | } else { // store as unique values 171 | PALRLE_U8* uniques_out = &data->data[ rle_offset++ ]; 172 | int uniques = 1; 173 | data->data[ rle_offset++ ] = (PALRLE_U8) color; 174 | ++x; 175 | 176 | // add pixexls until we encounter a run 177 | while( x < hpitch ) { 178 | // check the length of the next run 179 | color = pixels[ x + xmin + ( y + ymin ) * width ]; 180 | count = 0; 181 | tx = x; 182 | while( tx < hpitch ) { 183 | if( mask[ tx + xmin + ( y + ymin ) * width ] == 0 ) break; 184 | if( pixels[ tx + xmin + ( y + ymin ) * width ] != color ) break; 185 | if( count > 2 ) break; // No need to keep counting, we know this is a run 186 | ++count; 187 | ++tx; 188 | } 189 | if( count > 2 || count == 0 ) { 190 | break; 191 | } else { 192 | if( uniques >= 128 ) break; 193 | ++uniques; 194 | data->data[ rle_offset++ ] = (PALRLE_U8) color; 195 | ++x; 196 | } 197 | } 198 | *uniques_out = (PALRLE_U8)( (char) -uniques ); 199 | } 200 | } 201 | } 202 | data->size += rle_offset; 203 | 204 | return data; 205 | } 206 | 207 | 208 | /* 209 | palrle_data_t* palrle_encode_transparency_index( PALRLE_U8* pixels, PALRLE_U8 transparency_index, int width, int height, PALRLE_U32* palette, int palette_count, void* memctx ) { 210 | (void) pixels, transparency_index, width, height, memctx; 211 | return 0; 212 | } 213 | */ 214 | 215 | 216 | 217 | void palrle_decode( palrle_data_t* rle_data, PALRLE_U8* pixels, PALRLE_U8* mask ) { 218 | memset( pixels, 0, rle_data->width * rle_data->height * sizeof( PALRLE_U8 ) ); 219 | if( mask ) memset( mask, 0, rle_data->width * rle_data->height * sizeof( PALRLE_U8 ) ); 220 | PALRLE_U8* data = &rle_data->data[ sizeof( PALRLE_U32 ) * ( rle_data->palette_count + rle_data->vpitch ) ]; 221 | for( int y = 0; y < rle_data->vpitch; ++y ) { 222 | int x = 0; 223 | while( x < rle_data->hpitch ) { 224 | x += *data++; 225 | char count = (char)( *data++ ); 226 | if( count > 0 ) { 227 | PALRLE_U8 color = *data++; 228 | for( int i = 0; i < count; ++i ) { 229 | pixels[ rle_data->xoffset + x + ( rle_data->yoffset + y ) * rle_data->width ] = color; 230 | if( mask ) mask[ rle_data->xoffset + x + ( rle_data->yoffset + y ) * rle_data->width ] = 255; 231 | ++x; 232 | } 233 | } else { 234 | for( int i = 0; i < -count; ++i ) { 235 | pixels[ rle_data->xoffset + x + ( rle_data->yoffset + y ) * rle_data->width ] = *data++; 236 | if( mask ) mask[ rle_data->xoffset + x + ( rle_data->yoffset + y ) * rle_data->width ] = 255; 237 | ++x; 238 | } 239 | } 240 | } 241 | } 242 | } 243 | 244 | 245 | void palrle_blit( palrle_data_t* rle_data, int x, int y, PALRLE_U8* pixels, int width, int height ) { 246 | PALRLE_U8* data = &rle_data->data[ sizeof( PALRLE_U32 ) * ( rle_data->palette_count + rle_data->vpitch ) ]; 247 | for( int iy = 0; iy < rle_data->vpitch; ++iy ) { 248 | int ix = 0; 249 | while( ix < rle_data->hpitch ) { 250 | ix += *data++; 251 | char count = (char)( *data++ ); 252 | if( count > 0 ) { 253 | PALRLE_U8 color = *data++; 254 | for( int i = 0; i < count; ++i ) { 255 | int xp = rle_data->xoffset + ix + x; 256 | int yp = rle_data->yoffset + iy + y; 257 | if( xp > 0 && yp > 0 && xp < width && yp < height ) pixels[ xp + yp * width ] = color; 258 | ++ix; 259 | } 260 | } else { 261 | for( int i = 0; i < -count; ++i ) { 262 | PALRLE_U8 color = *data++; 263 | int xp = rle_data->xoffset + ix + x; 264 | int yp = rle_data->yoffset + iy + y; 265 | if( xp > 0 && yp > 0 && xp < width && yp < height ) pixels[ xp + yp * width ] = color; 266 | ++ix; 267 | } 268 | } 269 | } 270 | } 271 | } 272 | 273 | 274 | void palrle_free( palrle_data_t* rle_data, void* memctx ) { 275 | (void) rle_data, (void) memctx; 276 | PALRLE_FREE( memctx, rle_data ); 277 | } 278 | 279 | #endif /* PALRLE_IMPLEMENTATION */ 280 | 281 | 282 | /* 283 | ------------------------------------------------------------------------------ 284 | 285 | This software is available under 2 licenses - you may choose the one you like. 286 | 287 | ------------------------------------------------------------------------------ 288 | 289 | ALTERNATIVE A - MIT License 290 | 291 | Copyright (c) 2019 Mattias Gustavsson 292 | 293 | Permission is hereby granted, free of charge, to any person obtaining a copy of 294 | this software and associated documentation files (the "Software"), to deal in 295 | the Software without restriction, including without limitation the rights to 296 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 297 | of the Software, and to permit persons to whom the Software is furnished to do 298 | so, subject to the following conditions: 299 | 300 | The above copyright notice and this permission notice shall be included in all 301 | copies or substantial portions of the Software. 302 | 303 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 304 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 305 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 306 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 307 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 308 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 309 | SOFTWARE. 310 | 311 | ------------------------------------------------------------------------------ 312 | 313 | ALTERNATIVE B - Public Domain (www.unlicense.org) 314 | 315 | This is free and unencumbered software released into the public domain. 316 | 317 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 318 | software, either in source code form or as a compiled binary, for any purpose, 319 | commercial or non-commercial, and by any means. 320 | 321 | In jurisdictions that recognize copyright laws, the author or authors of this 322 | software dedicate any and all copyright interest in the software to the public 323 | domain. We make this dedication for the benefit of the public at large and to 324 | the detriment of our heirs and successors. We intend this dedication to be an 325 | overt act of relinquishment in perpetuity of all present and future rights to 326 | this software under copyright law. 327 | 328 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 329 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 330 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 331 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 332 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 333 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 334 | 335 | ------------------------------------------------------------------------------ 336 | */ 337 | -------------------------------------------------------------------------------- /pixie.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {EF3E8E6B-3FE9-4B35-879C-65B355B2D531} 23 | Win32Proj 24 | 10.0.18362.0 25 | 26 | 27 | 28 | Application 29 | v141 30 | MultiByte 31 | true 32 | 33 | 34 | Application 35 | v141 36 | MultiByte 37 | true 38 | 39 | 40 | Application 41 | v141 42 | MultiByte 43 | 44 | 45 | Application 46 | v141 47 | MultiByte 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | <_ProjectFileVersion>15.0.25909.0 67 | 68 | 69 | runtime/ 70 | .build_temp/x86/debug/ 71 | true 72 | $(ProjectName)_debug 73 | 74 | 75 | true 76 | $(ProjectName)_x64_debug 77 | runtime/ 78 | .build_temp/x64/debug/ 79 | 80 | 81 | runtime/ 82 | .build_temp/x86/release/ 83 | false 84 | 85 | 86 | false 87 | runtime/ 88 | .build_temp/x64/release/ 89 | $(ProjectName)_x64 90 | 91 | 92 | 93 | /Wall %(AdditionalOptions) 94 | Disabled 95 | _WIN32_WINNT=0x0501;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | true 97 | false 98 | EnableFastChecks 99 | MultiThreadedDebug 100 | 101 | EnableAllWarnings 102 | true 103 | ProgramDatabase 104 | 4505;4204;4514;4710;4711;4738;4820;5045;%(DisableSpecificWarnings) 105 | false 106 | Caret 107 | %PIXIE_DIR%;%PGS_DIR% 108 | 109 | 110 | $(OutDir)$(TargetName)$(TargetExt) 111 | true 112 | $(OutDir)$(TargetName).pdb 113 | Console 114 | 10000000 115 | 0 116 | MachineX86 117 | 118 | 119 | 120 | 121 | /Wall %(AdditionalOptions) 122 | Disabled 123 | _WIN32_WINNT=0x0600;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | false 125 | EnableFastChecks 126 | MultiThreadedDebug 127 | 128 | 129 | EnableAllWarnings 130 | true 131 | ProgramDatabase 132 | 4505;4204;4514;4710;4711;4738;4820;5045;%(DisableSpecificWarnings) 133 | false 134 | Caret 135 | %PIXIE_DIR%;%PGS_DIR% 136 | 137 | 138 | $(OutDir)$(TargetName)$(TargetExt) 139 | true 140 | $(OutDir)$(TargetName).pdb 141 | Console 142 | 10000000 143 | 0 144 | 145 | 146 | 147 | 148 | /Wall %(AdditionalOptions) 149 | Full 150 | AnySuitable 151 | true 152 | Speed 153 | true 154 | true 155 | _WIN32_WINNT=0x0501;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 156 | false 157 | MultiThreaded 158 | false 159 | StreamingSIMDExtensions2 160 | 161 | EnableAllWarnings 162 | true 163 | ProgramDatabase 164 | 4505;4204;4514;4710;4711;4738;4820;5045;%(DisableSpecificWarnings) 165 | Caret 166 | %PIXIE_DIR%;%PGS_DIR% 167 | false 168 | 169 | 170 | $(OutDir)$(TargetName)$(TargetExt) 171 | true 172 | Console 173 | true 174 | true 175 | MachineX86 176 | 177 | 178 | 179 | 180 | /Wall %(AdditionalOptions) 181 | Full 182 | AnySuitable 183 | true 184 | Speed 185 | true 186 | true 187 | _WIN32_WINNT=0x0600;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 188 | false 189 | MultiThreaded 190 | false 191 | AdvancedVectorExtensions2 192 | 193 | 194 | EnableAllWarnings 195 | true 196 | ProgramDatabase 197 | 4505;4204;4514;4710;4711;4738;4820;5045;%(DisableSpecificWarnings) 198 | Caret 199 | %PIXIE_DIR%;%PGS_DIR% 200 | false 201 | 202 | 203 | $(OutDir)$(TargetName)$(TargetExt) 204 | true 205 | Console 206 | true 207 | true 208 | 209 | 210 | 211 | 212 | true 213 | true 214 | true 215 | true 216 | 217 | 218 | false 219 | false 220 | false 221 | false 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | -------------------------------------------------------------------------------- /source/pixie/mid.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | mid.h - v0.1 - Midi playback library using the TinySoundFont library. 7 | 8 | Do this: 9 | #define MID_IMPLEMENTATION 10 | before you include this file in *one* C/C++ file to create the implementation. 11 | */ 12 | 13 | #ifndef mid_h 14 | #define mid_h 15 | 16 | #define _CRT_NONSTDC_NO_DEPRECATE 17 | #define _CRT_SECURE_NO_WARNINGS 18 | #include 19 | 20 | typedef struct mid_t mid_t; 21 | typedef struct tsf tsf; 22 | 23 | mid_t* mid_create( void const* midi_data, size_t midi_size, void* memctx ); 24 | void mid_destroy( mid_t* mid ); 25 | 26 | int mid_render_short( mid_t* mid, short* sample_pairs, int sample_pairs_count, tsf* sound_font ); 27 | int mid_render_float( mid_t* mid, float* sample_pairs, int sample_pairs_count, tsf* sound_font ); 28 | 29 | void mid_skip_leading_silence( mid_t* mid, tsf* sound_font ); 30 | 31 | #endif /* mid_h */ 32 | 33 | #ifdef MID_ENABLE_RAW 34 | 35 | #ifndef mid_raw_h 36 | #define mid_raw_h 37 | 38 | #ifndef MID_U8 39 | #define MID_U8 unsigned char 40 | #endif 41 | 42 | #ifndef MID_U16 43 | #define MID_U16 unsigned short 44 | #endif 45 | 46 | #ifndef MID_U32 47 | #define MID_U32 unsigned int 48 | #endif 49 | 50 | #ifndef MID_U64 51 | #define MID_U64 unsigned long long 52 | #endif 53 | 54 | typedef struct mid_event_t 55 | { 56 | MID_U32 delay_us; 57 | MID_U8 channel; 58 | MID_U8 type; 59 | union 60 | { 61 | struct { MID_U8 program; } program_change; 62 | struct { MID_U8 note; MID_U8 velocity; } note_on; 63 | struct { MID_U8 note; } note_off; 64 | struct { MID_U8 key; MID_U8 key_pressure; } key_pressure; 65 | struct { MID_U16 value; } pitch_bend; 66 | struct { MID_U8 control, control_value; } control_change; 67 | struct { MID_U8 channel_pressure; } channel_pressure; 68 | } data; 69 | } mid_event_t; 70 | 71 | 72 | typedef struct mid_song_t 73 | { 74 | int event_count; 75 | mid_event_t* events; 76 | } mid_song_t; 77 | 78 | 79 | struct mid_t 80 | { 81 | void* memctx; 82 | mid_song_t song; 83 | int percussion_preset; 84 | MID_U64 playback_accumulated_time_us; 85 | int playback_sample_pos; 86 | int playback_event_pos; 87 | }; 88 | 89 | int mid_init_raw( mid_t* mid, void const* raw_data, size_t raw_size ); 90 | 91 | size_t mid_save_raw( mid_t* mid, void* data, size_t capacity ); 92 | 93 | 94 | #endif /* MID_ENABLE_RAW */ 95 | 96 | #endif /* mid_raw_h */ 97 | 98 | /* 99 | ---------------------- 100 | IMPLEMENTATION 101 | ---------------------- 102 | */ 103 | 104 | #ifdef MID_IMPLEMENTATION 105 | #undef MID_IMPLEMENTATION 106 | 107 | #ifndef MID_U8 108 | #define MID_U8 unsigned char 109 | #endif 110 | 111 | #ifndef MID_U16 112 | #define MID_U16 unsigned short 113 | #endif 114 | 115 | #ifndef MID_U32 116 | #define MID_U32 unsigned int 117 | #endif 118 | 119 | #ifndef MID_U64 120 | #define MID_U64 unsigned long long 121 | #endif 122 | 123 | #ifndef MID_MALLOC 124 | #define _CRT_NONSTDC_NO_DEPRECATE 125 | #define _CRT_SECURE_NO_WARNINGS 126 | #include 127 | #if defined(_cplusplus) 128 | #define MID_MALLOC( ctx, size ) ( ::malloc( size ) ) 129 | #define MID_FREE( ctx, ptr ) ( ::free( ptr ) ) 130 | #else 131 | #define MID_MALLOC( ctx, size ) ( malloc( size ) ) 132 | #define MID_FREE( ctx, ptr ) ( free( ptr ) ) 133 | #endif 134 | #endif 135 | #include 136 | #define MID_LOG(...) (void) __VA_ARGS__ 137 | 138 | #include 139 | 140 | #pragma warning( push ) 141 | #pragma warning( disable: 4242 ) 142 | #pragma warning( disable: 4244 ) 143 | #pragma warning( disable: 4365 ) 144 | #pragma warning( disable: 4668 ) 145 | #pragma warning( disable: 4701 ) 146 | #pragma warning( disable: 4703 ) 147 | 148 | #ifndef MID_NO_TSF_IMPLEMENTATION 149 | #define TSF_NO_STDIO 150 | #define TSF_IMPLEMENTATION 151 | #endif 152 | #include "tsf.h" 153 | 154 | #pragma warning( disable: 4201 ) 155 | 156 | #ifndef MID_NO_TML_IMPLEMENTATION 157 | #define TML_NO_STDIO 158 | #define TML_IMPLEMENTATION 159 | #endif 160 | #include "tml.h" 161 | 162 | #pragma warning( pop ) 163 | 164 | 165 | 166 | 167 | #ifndef MID_ENABLE_RAW 168 | 169 | typedef struct mid_event_t 170 | { 171 | MID_U32 delay_us; 172 | MID_U8 channel; 173 | MID_U8 type; 174 | union 175 | { 176 | struct { MID_U8 program; } program_change; 177 | struct { MID_U8 note; MID_U8 velocity; } note_on; 178 | struct { MID_U8 note; } note_off; 179 | struct { MID_U8 key; MID_U8 key_pressure; } key_pressure; 180 | struct { MID_U16 value; } pitch_bend; 181 | struct { MID_U8 control, control_value; } control_change; 182 | struct { MID_U8 channel_pressure; } channel_pressure; 183 | } data; 184 | } mid_event_t; 185 | 186 | 187 | typedef struct mid_song_t 188 | { 189 | int event_count; 190 | mid_event_t* events; 191 | } mid_song_t; 192 | 193 | 194 | struct mid_t 195 | { 196 | void* memctx; 197 | mid_song_t song; 198 | int percussion_preset; 199 | MID_U64 playback_accumulated_time_us; 200 | int playback_sample_pos; 201 | int playback_event_pos; 202 | }; 203 | 204 | 205 | #endif /* MID_ENABLE_RAW */ 206 | 207 | 208 | mid_t* mid_create( void const* midi_data, size_t midi_size, void* memctx ) 209 | { 210 | tml_message* mid_file = tml_load_memory( midi_data, (int) midi_size ); 211 | if( !mid_file ) return NULL; 212 | int count = 0; 213 | tml_message* iter = mid_file; 214 | while( iter ) 215 | { 216 | if( iter->type == TML_PROGRAM_CHANGE || iter->type == TML_NOTE_ON || iter->type == TML_NOTE_OFF || 217 | iter->type == TML_PITCH_BEND || iter->type == TML_CONTROL_CHANGE ) 218 | { 219 | ++count; 220 | } 221 | iter = iter->next; 222 | } 223 | 224 | mid_event_t* events = (mid_event_t*) malloc( sizeof( mid_event_t ) * count ); 225 | int events_count = 0; 226 | unsigned int time = 0; 227 | tml_message* msg = mid_file; 228 | while( msg ) 229 | { 230 | if( msg->type == TML_PROGRAM_CHANGE || msg->type == TML_NOTE_ON || msg->type == TML_NOTE_OFF || 231 | msg->type == TML_PITCH_BEND || msg->type == TML_CONTROL_CHANGE ) 232 | { 233 | mid_event_t* event = &events[ events_count++ ]; 234 | event->delay_us = ( msg->time - time ) * 1000; 235 | time = msg->time; 236 | event->channel = msg->channel; 237 | event->type = msg->type; 238 | switch( msg->type ) 239 | { 240 | case TML_PROGRAM_CHANGE: 241 | event->data.program_change.program = (MID_U8) msg->program; 242 | break; 243 | case TML_NOTE_ON: //play a note 244 | event->data.note_on.note = (MID_U8) msg->key; 245 | event->data.note_on.velocity = (MID_U8) msg->velocity; 246 | break; 247 | case TML_NOTE_OFF: //stop a note 248 | event->data.note_off.note = (MID_U8) msg->key; 249 | break; 250 | case TML_PITCH_BEND: //pitch wheel modification 251 | event->data.pitch_bend.value = (MID_U16) msg->pitch_bend; 252 | break; 253 | case TML_CONTROL_CHANGE: //MIDI controller messages 254 | event->data.control_change.control = (MID_U8) msg->control; 255 | event->data.control_change.control_value = (MID_U8) msg->control_value; 256 | break; 257 | } 258 | } 259 | 260 | msg = msg->next; 261 | } 262 | 263 | tml_free( mid_file ); 264 | 265 | mid_t* mid = (mid_t*) MID_MALLOC( memctx, sizeof( mid_t ) ); 266 | mid->memctx = memctx; 267 | mid->song.event_count = events_count; 268 | mid->song.events = events; 269 | 270 | mid->playback_accumulated_time_us = 0ull; 271 | mid->playback_sample_pos = 0; 272 | mid->playback_event_pos = 0; 273 | 274 | return mid; 275 | } 276 | 277 | 278 | void mid_destroy( mid_t* mid ) 279 | { 280 | if( mid->song.events ) MID_FREE( mid->memctx, mid->song.events ); 281 | MID_FREE( mid->memctx, mid ); 282 | } 283 | 284 | 285 | int mid_init_raw( mid_t* mid, void const* raw_data, size_t raw_size ) 286 | { 287 | int events_count = *(int*)raw_data; 288 | if( sizeof( mid_event_t ) * events_count != raw_size - sizeof( int ) ) return 0; 289 | 290 | mid->memctx = NULL; 291 | 292 | mid->song.event_count = events_count; 293 | mid->song.events = (mid_event_t*)( ( (int*)raw_data ) + 1 ); 294 | 295 | mid->playback_accumulated_time_us = 0ull; 296 | mid->playback_sample_pos = 0; 297 | mid->playback_event_pos = 0; 298 | 299 | return 1; 300 | } 301 | 302 | 303 | size_t mid_save_raw( mid_t* mid, void* data, size_t capacity ) 304 | { 305 | size_t size = sizeof( mid_event_t ) * mid->song.event_count + sizeof( int ); 306 | if( data && capacity >= size ) 307 | { 308 | *(int*)data = mid->song.event_count; 309 | memcpy( ( (int*)data ) + 1, mid->song.events, sizeof( mid_event_t ) * mid->song.event_count ); 310 | } 311 | return size; 312 | } 313 | 314 | 315 | void mid_skip_leading_silence( mid_t* mid, tsf* sound_font ) 316 | { 317 | (void) sound_font; 318 | for( ; ; ) 319 | { 320 | MID_U64 next_event_delay_us = mid->song.events[ mid->playback_event_pos ].delay_us; 321 | MID_U64 playback_time_us = ( mid->playback_sample_pos * 1000000ull ) / 44100ull; 322 | MID_U64 next_event_time_us = mid->playback_accumulated_time_us + next_event_delay_us; 323 | assert( next_event_time_us >= playback_time_us ); 324 | MID_U64 time_until_next_event = next_event_time_us - playback_time_us; 325 | int samples_until_next_event = (int)( ( time_until_next_event * 44100ull ) / 1000000ull ); 326 | mid_event_t* event = &mid->song.events[ mid->playback_event_pos ]; 327 | switch( event->type ) 328 | { 329 | case TML_PROGRAM_CHANGE: 330 | tsf_channel_set_presetnumber( sound_font, event->channel, event->data.program_change.program, ( event->channel == 9 ) ); 331 | break; 332 | case TML_NOTE_ON: 333 | return; 334 | case TML_NOTE_OFF: //stop a note 335 | tsf_channel_note_off( sound_font, event->channel, event->data.note_off.note ); 336 | break; 337 | case TML_PITCH_BEND: //pitch wheel modification 338 | tsf_channel_set_pitchwheel( sound_font, event->channel, event->data.pitch_bend.value ); 339 | break; 340 | case TML_CONTROL_CHANGE: //MIDI controller messages 341 | tsf_channel_midi_control( sound_font, event->channel, event->data.control_change.control, event->data.control_change.control_value ); 342 | break; 343 | } 344 | mid->playback_sample_pos += samples_until_next_event; 345 | mid->playback_accumulated_time_us += next_event_delay_us; 346 | mid->playback_event_pos++; 347 | } 348 | } 349 | 350 | 351 | int mid_render_short( mid_t* mid, short* sample_pairs, int sample_pairs_count, tsf* sound_font ) 352 | { 353 | int samples_rendered = 0; 354 | memset( sample_pairs, 0, sample_pairs_count * sizeof( short ) * 2 ); 355 | while( samples_rendered < sample_pairs_count ) 356 | { 357 | MID_U64 next_event_delay_us = mid->song.events[ mid->playback_event_pos ].delay_us; 358 | MID_U64 playback_time_us = ( mid->playback_sample_pos * 1000000ull ) / 44100ull; 359 | MID_U64 next_event_time_us = mid->playback_accumulated_time_us + next_event_delay_us; 360 | assert( next_event_time_us >= playback_time_us ); 361 | MID_U64 time_until_next_event = next_event_time_us - playback_time_us; 362 | int samples_until_next_event = (int)( ( time_until_next_event * 44100ull ) / 1000000ull ); 363 | int samples_to_render = samples_until_next_event; 364 | if( samples_to_render > sample_pairs_count - samples_rendered ) 365 | { 366 | samples_to_render = sample_pairs_count - samples_rendered; 367 | tsf_render_short( sound_font, sample_pairs + samples_rendered * 2, 368 | samples_to_render, 1 ); 369 | samples_rendered += samples_to_render; 370 | mid->playback_sample_pos += samples_to_render; 371 | return samples_rendered; 372 | } 373 | else 374 | { 375 | tsf_render_short( sound_font, sample_pairs + samples_rendered * 2, 376 | samples_to_render, 1 ); 377 | samples_rendered += samples_to_render; 378 | mid->playback_sample_pos += samples_to_render; 379 | } 380 | 381 | 382 | mid->playback_accumulated_time_us += next_event_delay_us; 383 | mid_event_t* event = &mid->song.events[ mid->playback_event_pos++ ]; 384 | switch( event->type ) 385 | { 386 | case TML_PROGRAM_CHANGE: 387 | tsf_channel_set_presetnumber( sound_font, event->channel, event->data.program_change.program, ( event->channel == 9 ) ); 388 | break; 389 | case TML_NOTE_ON: 390 | tsf_channel_note_on( sound_font, event->channel, event->data.note_on.note, event->data.note_on.velocity / 127.0f ); 391 | break; 392 | case TML_NOTE_OFF: //stop a note 393 | tsf_channel_note_off( sound_font, event->channel, event->data.note_off.note ); 394 | break; 395 | case TML_PITCH_BEND: //pitch wheel modification 396 | tsf_channel_set_pitchwheel( sound_font, event->channel, event->data.pitch_bend.value ); 397 | break; 398 | case TML_CONTROL_CHANGE: //MIDI controller messages 399 | tsf_channel_midi_control( sound_font, event->channel, event->data.control_change.control, event->data.control_change.control_value ); 400 | break; 401 | } 402 | } 403 | 404 | return samples_rendered; 405 | } 406 | 407 | 408 | #endif /* MID_IMPLEMENTATION */ 409 | 410 | /* 411 | ------------------------------------------------------------------------------ 412 | 413 | This software is available under 2 licenses - you may choose the one you like. 414 | 415 | ------------------------------------------------------------------------------ 416 | 417 | ALTERNATIVE A - MIT License 418 | 419 | Copyright (c) 2016 Mattias Gustavsson 420 | 421 | Permission is hereby granted, free of charge, to any person obtaining a copy of 422 | this software and associated documentation files (the "Software"), to deal in 423 | the Software without restriction, including without limitation the rights to 424 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 425 | of the Software, and to permit persons to whom the Software is furnished to do 426 | so, subject to the following conditions: 427 | 428 | The above copyright notice and this permission notice shall be included in all 429 | copies or substantial portions of the Software. 430 | 431 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 432 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 433 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 434 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 435 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 436 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 437 | SOFTWARE. 438 | 439 | ------------------------------------------------------------------------------ 440 | 441 | ALTERNATIVE B - Public Domain (www.unlicense.org) 442 | 443 | This is free and unencumbered software released into the public domain. 444 | 445 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 446 | software, either in source code form or as a compiled binary, for any purpose, 447 | commercial or non-commercial, and by any means. 448 | 449 | In jurisdictions that recognize copyright laws, the author or authors of this 450 | software dedicate any and all copyright interest in the software to the public 451 | domain. We make this dedication for the benefit of the public at large and to 452 | the detriment of our heirs and successors. We intend this dedication to be an 453 | overt act of relinquishment in perpetuity of all present and future rights to 454 | this software under copyright law. 455 | 456 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 457 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 458 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 459 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 460 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 461 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 462 | 463 | ------------------------------------------------------------------------------ 464 | */ 465 | -------------------------------------------------------------------------------- /source/pixie/pixelfont.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | pixelfont.h - v0.1 - Custom pixel font format builder and renderer. 7 | 8 | Do this: 9 | #define PIXELFONT_IMPLEMENTATION 10 | before you include this file in *one* C/C++ file to create the implementation. 11 | 12 | If you want to generate pixelfonts through the pixelfont_builder api, do this: 13 | #define PIXELFONT_BUILDER_IMPLEMENTATION 14 | before include the file to create the implementation. 15 | 16 | */ 17 | 18 | #ifndef pixelfont_h 19 | #define pixelfont_h 20 | 21 | #ifndef PIXELFONT_I8 22 | #define PIXELFONT_I8 signed char 23 | #endif 24 | 25 | #ifndef PIXELFONT_U8 26 | #define PIXELFONT_U8 unsigned char 27 | #endif 28 | 29 | #ifndef PIXELFONT_U16 30 | #define PIXELFONT_U16 unsigned short 31 | #endif 32 | 33 | #ifndef PIXELFONT_U32 34 | #define PIXELFONT_U32 unsigned int 35 | #endif 36 | 37 | typedef struct pixelfont_t 38 | { 39 | PIXELFONT_U32 size_in_bytes; 40 | PIXELFONT_U8 height; 41 | PIXELFONT_U8 line_spacing; 42 | PIXELFONT_U8 baseline; 43 | PIXELFONT_U16 offsets[ 256 ]; 44 | PIXELFONT_U8 glyphs[ 1 ]; // "open" array - ok to access out of bounds (use size_in_bytes to determine the end) 45 | } pixelfont_t; 46 | 47 | 48 | typedef enum pixelfont_align_t 49 | { 50 | PIXELFONT_ALIGN_LEFT, 51 | PIXELFONT_ALIGN_RIGHT, 52 | PIXELFONT_ALIGN_CENTER, 53 | } pixelfont_align_t; 54 | 55 | 56 | typedef enum pixelfont_bold_t 57 | { 58 | PIXELFONT_BOLD_OFF, 59 | PIXELFONT_BOLD_ON, 60 | } pixelfont_bold_t; 61 | 62 | 63 | typedef enum pixelfont_italic_t 64 | { 65 | PIXELFONT_ITALIC_OFF, 66 | PIXELFONT_ITALIC_ON, 67 | } pixelfont_italic_t; 68 | 69 | 70 | typedef enum pixelfont_underline_t 71 | { 72 | PIXELFONT_UNDERLINE_OFF, 73 | PIXELFONT_UNDERLINE_ON, 74 | } pixelfont_underline_t; 75 | 76 | 77 | typedef struct pixelfont_bounds_t 78 | { 79 | int width; 80 | int height; 81 | } pixelfont_bounds_t; 82 | 83 | 84 | 85 | typedef struct pixelfont_builder_t pixelfont_builder_t; 86 | 87 | pixelfont_builder_t* pixelfont_builder_create( int height, int baseline, int line_spacing, void* memctx ); 88 | void pixelfont_builder_destroy( pixelfont_builder_t* builder ); 89 | 90 | void pixelfont_builder_glyph( pixelfont_builder_t* builder, int glyph, int width, PIXELFONT_U8* pixels, int lead, int trail ); 91 | void pixelfont_builder_kerning( pixelfont_builder_t* builder, int glyph, int follower, int adjust ); 92 | 93 | pixelfont_t* pixelfont_builder_font( pixelfont_builder_t* builder ); 94 | 95 | #endif /* pixelfont_h */ 96 | 97 | 98 | // The following will be defined each time the file is included. By defining PIXELFONT_COLOR and PIXELFONT_FUNC_NAME 99 | // it is possible to create declarations for different pixel formats. By defining the same ones, and the additional 100 | // PIXELFONT_PIXEL_FUNC before including the implementation part, the corresponding definition can be created. 101 | 102 | #ifndef PIXELFONT_COLOR 103 | #define PIXELFONT_COLOR PIXELFONT_U8 104 | #endif 105 | 106 | #ifndef PIXELFONT_FUNC_NAME 107 | #define PIXELFONT_FUNC_NAME pixelfont_blit 108 | #endif 109 | 110 | void PIXELFONT_FUNC_NAME( pixelfont_t const* font, int x, int y, char const* text, PIXELFONT_COLOR color, 111 | PIXELFONT_COLOR* target, int width, int height, pixelfont_align_t align, int wrap_width, int hspacing, 112 | int vspacing, int limit, pixelfont_bold_t bold, pixelfont_italic_t italic, pixelfont_underline_t underline, 113 | pixelfont_bounds_t* bounds ); 114 | 115 | 116 | /* 117 | ---------------------- 118 | IMPLEMENTATION 119 | ---------------------- 120 | */ 121 | 122 | #ifdef PIXELFONT_IMPLEMENTATION 123 | #undef PIXELFONT_IMPLEMENTATION 124 | 125 | #ifndef PIXELFONT_PIXEL_FUNC 126 | #define PIXELFONT_PIXEL_FUNC( dst, src ) *(dst) = (src); 127 | #endif 128 | 129 | void PIXELFONT_FUNC_NAME( pixelfont_t const* font, int x, int y, char const* text, PIXELFONT_COLOR color, 130 | PIXELFONT_COLOR* target, int width, int height, pixelfont_align_t align, int wrap_width, int hspacing, 131 | int vspacing, int limit, pixelfont_bold_t bold, pixelfont_italic_t italic, pixelfont_underline_t underline, 132 | pixelfont_bounds_t* bounds ) 133 | { 134 | int xp = x; 135 | int yp = y; 136 | int max_x = x; 137 | int last_x_on_line = xp; 138 | int count = 0; 139 | char const* str = text; 140 | while( *str ) 141 | { 142 | int line_char_count = 0; 143 | int line_width = 0; 144 | int last_space_char_count = 0; 145 | int last_space_width = 0; 146 | char const* tstr = str; 147 | while( *tstr != '\n' && *tstr != '\0' && ( wrap_width <= 0 || line_width <= wrap_width ) ) 148 | { 149 | if( *tstr <= ' ' ) 150 | { 151 | last_space_char_count = line_char_count; 152 | last_space_width = line_width; 153 | } 154 | PIXELFONT_U8 const* g = font->glyphs + font->offsets[ (int) *tstr ]; 155 | line_width += (PIXELFONT_I8) *g++; 156 | int w = *g++; 157 | g += font->height * w; 158 | line_width += (PIXELFONT_I8) *g++; 159 | line_width += hspacing + ( bold ? 1 : 0 ); 160 | ++tstr; 161 | int kern = *g++; 162 | for( int k = 0; k < kern; ++k ) 163 | if( *g++ == *tstr ) { line_width += (PIXELFONT_I8) *g++; break; } else ++g; 164 | ++line_char_count; 165 | } 166 | 167 | int skip_space = 0; 168 | if( wrap_width > 0 && line_width > wrap_width ) 169 | { 170 | if( last_space_char_count > 0 ) line_char_count = last_space_char_count; 171 | line_width = last_space_width; 172 | skip_space = 1; 173 | } 174 | 175 | if( wrap_width > 0 ) 176 | { 177 | if( align == PIXELFONT_ALIGN_RIGHT ) x += wrap_width - line_width; 178 | if( align == PIXELFONT_ALIGN_CENTER ) x += ( wrap_width - line_width ) / 2; 179 | } 180 | else 181 | { 182 | if( align == PIXELFONT_ALIGN_RIGHT ) x -= line_width; 183 | if( align == PIXELFONT_ALIGN_CENTER ) x -= line_width / 2; 184 | } 185 | 186 | for( int c = 0; c < line_char_count; ++c ) 187 | { 188 | PIXELFONT_U8 const* g = font->glyphs + font->offsets[ (int) *str ]; 189 | x += (PIXELFONT_I8) *g++; 190 | int w = *g++; 191 | int h = font->height; 192 | for( int iy = y; iy < y + h; ++iy ) 193 | { 194 | int xs = x + ( italic ? ( h - ( iy - y ) ) / 2 - 1 : 0 ); 195 | for( int ix = xs; ix < xs + w; ++ix ) 196 | { 197 | int p = *g++; 198 | if( p && target ) 199 | if( limit < 0 || count < limit ) 200 | if( ix >= 0 && iy >= 0 && ix < width && iy < height ) 201 | { 202 | last_x_on_line = ix >= last_x_on_line ? ix + ( bold ? 1 : 0 ) : last_x_on_line; 203 | PIXELFONT_PIXEL_FUNC( ( &target[ ix + iy * width ] ), (PIXELFONT_U8)( color + p - 1 ) ); 204 | if( bold && ix + 1 < width ) 205 | PIXELFONT_PIXEL_FUNC( ( &target[ ix + 1 + iy * width ] ), (PIXELFONT_U8)( color + p - 1 ) ); 206 | } 207 | } 208 | } 209 | 210 | x += (PIXELFONT_I8) *g++; 211 | x += hspacing + ( bold ? 1 : 0 ); 212 | ++str; 213 | ++count; 214 | 215 | int kern = *g++; 216 | for( int k = 0; k < kern; ++k ) 217 | if( *g++ == *str ) { x += (PIXELFONT_I8) *g++; break; } else ++g; 218 | 219 | } 220 | 221 | if( underline && target && y + font->baseline + 1 >= 0 && y + font->baseline + 1 < height && last_x_on_line > xp ) 222 | for( int ix = xp; ix <= last_x_on_line; ++ix ) 223 | if( ix >= 0 && ix < width ) 224 | PIXELFONT_PIXEL_FUNC( ( &target[ ix + ( y + font->baseline + 1 ) * width ] ), color ); 225 | last_x_on_line = xp; 226 | max_x = x > max_x ? x : max_x; 227 | x = xp; 228 | y += font->line_spacing + vspacing; 229 | if( *str == '\n' ) ++str; 230 | if( *str && skip_space && *str <= ' ' ) ++str; 231 | } 232 | 233 | if( bounds ) 234 | { 235 | bounds->width = wrap_width > 0 ? wrap_width : ( max_x - xp ); 236 | bounds->height = y - yp; 237 | } 238 | } 239 | 240 | 241 | #undef PIXELFONT_COLOR 242 | #undef PIXELFONT_FUNC_NAME 243 | 244 | #endif /* PIXELFONT_IMPLEMENTATION */ 245 | 246 | 247 | 248 | #ifdef PIXELFONT_BUILDER_IMPLEMENTATION 249 | #undef PIXELFONT_BUILDER_IMPLEMENTATION 250 | 251 | 252 | #ifndef PIXELFONT_MALLOC 253 | #undef _CRT_NONSTDC_NO_DEPRECATE 254 | #define _CRT_NONSTDC_NO_DEPRECATE 255 | #undef _CRT_SECURE_NO_WARNINGS 256 | #define _CRT_SECURE_NO_WARNINGS 257 | #include 258 | #define PIXELFONT_MALLOC( ctx, size ) ( malloc( size ) ) 259 | #define PIXELFONT_FREE( ctx, ptr ) ( free( ptr ) ) 260 | #endif 261 | 262 | 263 | #ifndef PIXELFONT_MEMCPY 264 | #undef _CRT_NONSTDC_NO_DEPRECATE 265 | #define _CRT_NONSTDC_NO_DEPRECATE 266 | #undef _CRT_SECURE_NO_WARNINGS 267 | #define _CRT_SECURE_NO_WARNINGS 268 | #include 269 | #define PIXELFONT_MEMCPY( dst, src, cnt ) ( memcpy( dst, src, cnt ) ) 270 | #endif 271 | 272 | 273 | typedef struct pixelfont_builder_glyph_t 274 | { 275 | int lead; 276 | int trail; 277 | int width; 278 | PIXELFONT_U8* pixels; 279 | } pixelfont_builder_glyph_t; 280 | 281 | 282 | typedef struct pixelfont_builder_kerning_t 283 | { 284 | int glyph; 285 | int follower; 286 | int adjust; 287 | } pixelfont_builder_kerning_t; 288 | 289 | 290 | struct pixelfont_builder_t 291 | { 292 | void* memctx; 293 | pixelfont_t* font; 294 | int height; 295 | int baseline; 296 | int line_spacing; 297 | 298 | pixelfont_builder_glyph_t glyphs[ 256 ]; 299 | 300 | int kernings_capacity; 301 | int kernings_count; 302 | pixelfont_builder_kerning_t* kernings; 303 | }; 304 | 305 | 306 | pixelfont_builder_t* pixelfont_builder_create( int height, int baseline, int line_spacing, void* memctx ) 307 | { 308 | pixelfont_builder_t* builder = (pixelfont_builder_t*) PIXELFONT_MALLOC( memctx, sizeof( pixelfont_builder_t ) ) ; 309 | memset( builder, 0, sizeof( *builder ) ); 310 | builder->memctx = memctx; 311 | builder->font = 0; 312 | builder->height = height < 0 ? 0 : height > 255 ? 255 : height; 313 | builder->baseline = baseline < 0 ? 0 : baseline > 255 ? 255 : baseline; 314 | builder->line_spacing = line_spacing < 0 ? 0 : line_spacing > 255 ? 255 : line_spacing; 315 | builder->kernings_capacity = 0; 316 | builder->kernings_count = 0; 317 | builder->kernings = 0; 318 | return builder; 319 | } 320 | 321 | 322 | void pixelfont_builder_destroy( pixelfont_builder_t* builder ) 323 | { 324 | for( int i = 0; i < sizeof( builder->glyphs ) / sizeof( *builder->glyphs ); ++i ) 325 | if( builder->glyphs[ i ].pixels ) PIXELFONT_FREE( builder->memctx, builder->glyphs[ i ].pixels ); 326 | 327 | if( builder->kernings ) PIXELFONT_FREE( builder->memctx, builder->kernings ); 328 | if( builder->font ) PIXELFONT_FREE( builder->memctx, builder->font ); 329 | PIXELFONT_FREE( builder->memctx, builder ); 330 | } 331 | 332 | 333 | void pixelfont_builder_glyph( pixelfont_builder_t* builder, int glyph, int width, PIXELFONT_U8* pixels, int lead, int trail ) 334 | { 335 | if( glyph < 0 || glyph > 255 ) return; 336 | 337 | if( builder->glyphs[ glyph ].pixels ) PIXELFONT_FREE( builder->memctx, builder->glyphs[ glyph ].pixels ); 338 | builder->glyphs[ glyph ].pixels = 0; 339 | builder->glyphs[ glyph ].lead = 0; 340 | builder->glyphs[ glyph ].trail = 0; 341 | builder->glyphs[ glyph ].width = 0; 342 | 343 | if( pixels && width > 0 ) 344 | { 345 | builder->glyphs[ glyph ].pixels = (PIXELFONT_U8*) PIXELFONT_MALLOC( builder->memctx, width * builder->height * sizeof( PIXELFONT_U8 ) ); 346 | PIXELFONT_MEMCPY( builder->glyphs[ glyph ].pixels, pixels, width * builder->height * sizeof( PIXELFONT_U8 ) ); 347 | builder->glyphs[ glyph ].lead = lead < -127 ? -127 : lead > 127 ? 127 : lead; 348 | builder->glyphs[ glyph ].trail = trail < -127 ? -127 : trail > 127 ? 127 : trail; 349 | builder->glyphs[ glyph ].width = width < 0 ? 0 : width > 255 ? 255 : width; 350 | } 351 | else if( lead || trail ) 352 | { 353 | builder->glyphs[ glyph ].pixels = (PIXELFONT_U8*) PIXELFONT_MALLOC( builder->memctx, 1 ); 354 | builder->glyphs[ glyph ].lead = lead < -127 ? -127 : lead > 127 ? 127 : lead; 355 | builder->glyphs[ glyph ].trail = trail < -127 ? -127 : trail > 127 ? 127 : trail; 356 | builder->glyphs[ glyph ].width = 0; 357 | } 358 | } 359 | 360 | 361 | void pixelfont_builder_kerning( pixelfont_builder_t* builder, int glyph, int follower, int adjust ) 362 | { 363 | adjust = adjust < -127 ? -127 : adjust > 127 ? 127 : adjust; 364 | 365 | for( int i = 0; i < builder->kernings_count; ++i ) 366 | { 367 | if( builder->kernings[ i ].glyph == glyph && builder->kernings[ i ].follower == follower ) 368 | { 369 | if( adjust ) 370 | builder->kernings[ i ].adjust = adjust; 371 | else 372 | builder->kernings[ i ] = builder->kernings[ --builder->kernings_count ]; 373 | return; 374 | } 375 | } 376 | 377 | if( adjust ) 378 | { 379 | if( !builder->kernings || builder->kernings_count >= builder->kernings_capacity ) 380 | { 381 | builder->kernings_capacity = builder->kernings_capacity ? builder->kernings_capacity * 2 : 256; 382 | pixelfont_builder_kerning_t* kernings = (pixelfont_builder_kerning_t*) PIXELFONT_MALLOC( builder->memctx, 383 | sizeof( pixelfont_builder_kerning_t ) * builder->kernings_capacity ); 384 | if( builder->kernings ) 385 | { 386 | PIXELFONT_MEMCPY( kernings, builder->kernings, sizeof( pixelfont_builder_kerning_t ) * builder->kernings_count ); 387 | PIXELFONT_FREE( builder->memctx, builder->kernings ); 388 | } 389 | builder->kernings = kernings; 390 | } 391 | 392 | builder->kernings[ builder->kernings_count ].glyph = glyph; 393 | builder->kernings[ builder->kernings_count ].follower = follower; 394 | builder->kernings[ builder->kernings_count ].adjust = adjust; 395 | ++builder->kernings_count; 396 | } 397 | } 398 | 399 | 400 | pixelfont_t* pixelfont_builder_font( pixelfont_builder_t* builder ) 401 | { 402 | if( builder->font ) PIXELFONT_FREE( builder->memctx, builder->font ); 403 | 404 | PIXELFONT_U8 kerning_counts[ 256 ]; 405 | memset( kerning_counts, 0, sizeof( kerning_counts ) ); 406 | for( int i = 0; i < builder->kernings_count; ++i ) 407 | ++kerning_counts[ builder->kernings[ i ].glyph ]; 408 | 409 | PIXELFONT_U16 offsets[ 256 ]; 410 | memset( offsets, 0, sizeof( offsets ) ); 411 | int current_offset = 0; 412 | int total_width = 0; 413 | int glyph_count = 0; 414 | for( int i = 0; i < sizeof( builder->glyphs ) / sizeof( *builder->glyphs ); ++i ) 415 | { 416 | if( builder->glyphs[ i ].pixels ) 417 | { 418 | ++glyph_count; 419 | total_width += builder->glyphs[ i ].width; 420 | if( current_offset > 0xffff ) return 0; // font too large for pixelfont format 421 | offsets[ i ] = (PIXELFONT_U16) current_offset; 422 | current_offset += 1 + kerning_counts[ i ] + 3 + builder->glyphs[ i ].width * builder->height; 423 | } 424 | } 425 | 426 | size_t size_in_bytes = sizeof( pixelfont_t ) - sizeof( PIXELFONT_U8 ); // base size excluding final placeholder byte 427 | size_in_bytes += glyph_count + 2 * builder->kernings_count; // kerning pair count for each glyph + all kerning data 428 | size_in_bytes += 3 * glyph_count; // lead, trail and width for each glyph 429 | size_in_bytes += total_width * builder->height; // pixel data for all glyphs 430 | 431 | pixelfont_t* font = (pixelfont_t*) PIXELFONT_MALLOC( builder->memctx, size_in_bytes ); 432 | memset( font, 0, sizeof( *font ) ); 433 | font->size_in_bytes = (PIXELFONT_U32) size_in_bytes; 434 | font->height = (PIXELFONT_U8) builder->height; 435 | font->line_spacing = (PIXELFONT_U8) builder->line_spacing; 436 | font->baseline = (PIXELFONT_U8) builder->baseline; 437 | memcpy( font->offsets, offsets, sizeof( font->offsets ) ); 438 | 439 | for( int i = 0; i < sizeof( builder->glyphs ) / sizeof( *builder->glyphs ); ++i ) 440 | { 441 | if( builder->glyphs[ i ].pixels ) 442 | { 443 | PIXELFONT_U8* src = builder->glyphs[ i ].pixels; 444 | PIXELFONT_U8* out = font->glyphs + offsets[ i ]; 445 | *out++ = (PIXELFONT_U8) builder->glyphs[ i ].lead; 446 | *out++ = (PIXELFONT_U8) builder->glyphs[ i ].width; 447 | for( int y = 0; y < builder->height; ++y ) 448 | for( int x = 0; x < builder->glyphs[ i ].width; ++x ) 449 | *out++ = *src++; 450 | *out++ = (PIXELFONT_U8) builder->glyphs[ i ].trail; 451 | 452 | *out++ = kerning_counts[ i ]; 453 | for( int j = 0; j < builder->kernings_count; ++j ) 454 | { 455 | if( builder->kernings[ j ].glyph == i ) 456 | { 457 | *out++ = (PIXELFONT_U8) builder->kernings[ j ].follower; 458 | *out++ = (PIXELFONT_U8) builder->kernings[ j ].adjust; 459 | } 460 | } 461 | } 462 | } 463 | 464 | builder->font = font; 465 | return builder->font; 466 | } 467 | 468 | 469 | #endif /* PIXELFONT_BUILDER_IMPLEMENTATION */ 470 | 471 | 472 | /* 473 | ------------------------------------------------------------------------------ 474 | 475 | This software is available under 2 licenses - you may choose the one you like. 476 | 477 | ------------------------------------------------------------------------------ 478 | 479 | ALTERNATIVE A - MIT License 480 | 481 | Copyright (c) 2017 Mattias Gustavsson 482 | 483 | Permission is hereby granted, free of charge, to any person obtaining a copy of 484 | this software and associated documentation files (the "Software"), to deal in 485 | the Software without restriction, including without limitation the rights to 486 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 487 | of the Software, and to permit persons to whom the Software is furnished to do 488 | so, subject to the following conditions: 489 | 490 | The above copyright notice and this permission notice shall be included in all 491 | copies or substantial portions of the Software. 492 | 493 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 494 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 495 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 496 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 497 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 498 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 499 | SOFTWARE. 500 | 501 | ------------------------------------------------------------------------------ 502 | 503 | ALTERNATIVE B - Public Domain (www.unlicense.org) 504 | 505 | This is free and unencumbered software released into the public domain. 506 | 507 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 508 | software, either in source code form or as a compiled binary, for any purpose, 509 | commercial or non-commercial, and by any means. 510 | 511 | In jurisdictions that recognize copyright laws, the author or authors of this 512 | software dedicate any and all copyright interest in the software to the public 513 | domain. We make this dedication for the benefit of the public at large and to 514 | the detriment of our heirs and successors. We intend this dedication to be an 515 | overt act of relinquishment in perpetuity of all present and future rights to 516 | this software under copyright law. 517 | 518 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 519 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 520 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 521 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 522 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 523 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 524 | 525 | ------------------------------------------------------------------------------ 526 | */ 527 | -------------------------------------------------------------------------------- /source/pixie/tml.h: -------------------------------------------------------------------------------- 1 | /* TinyMidiLoader - v0.7 - Minimalistic midi parsing library - https://github.com/schellingb/TinySoundFont 2 | no warranty implied; use at your own risk 3 | Do this: 4 | #define TML_IMPLEMENTATION 5 | before you include this file in *one* C or C++ file to create the implementation. 6 | // i.e. it should look like this: 7 | #include ... 8 | #include ... 9 | #define TML_IMPLEMENTATION 10 | #include "tml.h" 11 | 12 | [OPTIONAL] #define TML_NO_STDIO to remove stdio dependency 13 | [OPTIONAL] #define TML_MALLOC, TML_REALLOC, and TML_FREE to avoid stdlib.h 14 | [OPTIONAL] #define TML_MEMCPY to avoid string.h 15 | 16 | LICENSE (ZLIB) 17 | 18 | Copyright (C) 2017, 2018 Bernhard Schelling 19 | 20 | This software is provided 'as-is', without any express or implied 21 | warranty. In no event will the authors be held liable for any damages 22 | arising from the use of this software. 23 | 24 | Permission is granted to anyone to use this software for any purpose, 25 | including commercial applications, and to alter it and redistribute it 26 | freely, subject to the following restrictions: 27 | 28 | 1. The origin of this software must not be misrepresented; you must not 29 | claim that you wrote the original software. If you use this software 30 | in a product, an acknowledgment in the product documentation would be 31 | appreciated but is not required. 32 | 2. Altered source versions must be plainly marked as such, and must not be 33 | misrepresented as being the original software. 34 | 3. This notice may not be removed or altered from any source distribution. 35 | 36 | */ 37 | 38 | #ifndef TML_INCLUDE_TML_INL 39 | #define TML_INCLUDE_TML_INL 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | // Define this if you want the API functions to be static 46 | #ifdef TML_STATIC 47 | #define TMLDEF static 48 | #else 49 | #define TMLDEF extern 50 | #endif 51 | 52 | // Channel message type 53 | enum TMLMessageType 54 | { 55 | TML_NOTE_OFF = 0x80, TML_NOTE_ON = 0x90, TML_KEY_PRESSURE = 0xA0, TML_CONTROL_CHANGE = 0xB0, TML_PROGRAM_CHANGE = 0xC0, TML_CHANNEL_PRESSURE = 0xD0, TML_PITCH_BEND = 0xE0, TML_SET_TEMPO = 0x51 56 | }; 57 | 58 | // Midi controller numbers 59 | enum TMLController 60 | { 61 | TML_BANK_SELECT_MSB, TML_MODULATIONWHEEL_MSB, TML_BREATH_MSB, TML_FOOT_MSB = 4, TML_PORTAMENTO_TIME_MSB, TML_DATA_ENTRY_MSB, TML_VOLUME_MSB, 62 | TML_BALANCE_MSB, TML_PAN_MSB = 10, TML_EXPRESSION_MSB, TML_EFFECTS1_MSB, TML_EFFECTS2_MSB, TML_GPC1_MSB = 16, TML_GPC2_MSB, TML_GPC3_MSB, TML_GPC4_MSB, 63 | TML_BANK_SELECT_LSB = 32, TML_MODULATIONWHEEL_LSB, TML_BREATH_LSB, TML_FOOT_LSB = 36, TML_PORTAMENTO_TIME_LSB, TML_DATA_ENTRY_LSB, TML_VOLUME_LSB, 64 | TML_BALANCE_LSB, TML_PAN_LSB = 42, TML_EXPRESSION_LSB, TML_EFFECTS1_LSB, TML_EFFECTS2_LSB, TML_GPC1_LSB = 48, TML_GPC2_LSB, TML_GPC3_LSB, TML_GPC4_LSB, 65 | TML_SUSTAIN_SWITCH = 64, TML_PORTAMENTO_SWITCH, TML_SOSTENUTO_SWITCH, TML_SOFT_PEDAL_SWITCH, TML_LEGATO_SWITCH, TML_HOLD2_SWITCH, 66 | TML_SOUND_CTRL1, TML_SOUND_CTRL2, TML_SOUND_CTRL3, TML_SOUND_CTRL4, TML_SOUND_CTRL5, TML_SOUND_CTRL6, 67 | TML_SOUND_CTRL7, TML_SOUND_CTRL8, TML_SOUND_CTRL9, TML_SOUND_CTRL10, TML_GPC5, TML_GPC6, TML_GPC7, TML_GPC8, 68 | TML_PORTAMENTO_CTRL, TML_FX_REVERB = 91, TML_FX_TREMOLO, TML_FX_CHORUS, TML_FX_CELESTE_DETUNE, TML_FX_PHASER, 69 | TML_DATA_ENTRY_INCR, TML_DATA_ENTRY_DECR, TML_NRPN_LSB, TML_NRPN_MSB, TML_RPN_LSB, TML_RPN_MSB, 70 | TML_ALL_SOUND_OFF = 120, TML_ALL_CTRL_OFF, TML_LOCAL_CONTROL, TML_ALL_NOTES_OFF, TML_OMNI_OFF, TML_OMNI_ON, TML_POLY_OFF, TML_POLY_ON 71 | }; 72 | 73 | // A single MIDI message linked to the next message in time 74 | typedef struct tml_message 75 | { 76 | // Time of the message in milliseconds 77 | unsigned int time; 78 | 79 | // Type (see TMLMessageType) and channel number 80 | unsigned char type, channel; 81 | 82 | // 2 byte of parameter data based on the type: 83 | // - key, velocity for TML_NOTE_ON and TML_NOTE_OFF messages 84 | // - key, key_pressure for TML_KEY_PRESSURE messages 85 | // - control, control_value for TML_CONTROL_CHANGE messages (see TMLController) 86 | // - program for TML_PROGRAM_CHANGE messages 87 | // - channel_pressure for TML_CHANNEL_PRESSURE messages 88 | // - pitch_bend for TML_PITCH_BEND messages 89 | union 90 | { 91 | struct { union { char key, control, program, channel_pressure; }; union { char velocity, key_pressure, control_value; }; }; 92 | struct { unsigned short pitch_bend; }; 93 | }; 94 | 95 | // The pointer to the next message in time following this event 96 | struct tml_message* next; 97 | } tml_message; 98 | 99 | // The load functions will return a pointer to a struct tml_message. 100 | // Normally the linked list gets traversed by following the next pointers. 101 | // Make sure to keep the pointer to the first message to free the memory. 102 | // On error the tml_load* functions will return NULL most likely due to an 103 | // invalid MIDI stream (or if the file did not exist in tml_load_filename). 104 | 105 | #ifndef TML_NO_STDIO 106 | // Directly load a MIDI file from a .mid file path 107 | TMLDEF tml_message* tml_load_filename(const char* filename); 108 | #endif 109 | 110 | // Load a MIDI file from a block of memory 111 | TMLDEF tml_message* tml_load_memory(const void* buffer, int size); 112 | 113 | // Get infos about this loaded MIDI file, returns the note count 114 | // NULL can be passed for any output value pointer if not needed. 115 | // used_channels: Will be set to how many channels play notes 116 | // (i.e. 1 if channel 15 is used but no other) 117 | // used_programs: Will be set to how many different programs are used 118 | // total_notes: Will be set to the total number of note on messages 119 | // time_first_note: Will be set to the time of the first note on message 120 | // time_length: Will be set to the total time in milliseconds 121 | TMLDEF int tml_get_info(tml_message* first_message, int* used_channels, int* used_programs, int* total_notes, unsigned int* time_first_note, unsigned int* time_length); 122 | 123 | // Read the tempo (microseconds per quarter note) value from a message with the type TML_SET_TEMPO 124 | TMLDEF int tml_get_tempo_value(tml_message* set_tempo_message); 125 | 126 | // Free all the memory of the linked message list (can also call free() manually) 127 | TMLDEF void tml_free(tml_message* f); 128 | 129 | // Stream structure for the generic loading 130 | struct tml_stream 131 | { 132 | // Custom data given to the functions as the first parameter 133 | void* data; 134 | 135 | // Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes) 136 | int (*read)(void* data, void* ptr, unsigned int size); 137 | }; 138 | 139 | // Generic Midi loading method using the stream structure above 140 | TMLDEF tml_message* tml_load(struct tml_stream* stream); 141 | TMLDEF tml_message* tml_load_tsf_stream(struct tsf_stream* stream); 142 | 143 | #ifdef __cplusplus 144 | } 145 | #endif 146 | 147 | // end header 148 | // --------------------------------------------------------------------------------------------------------- 149 | #endif //TML_INCLUDE_TML_INL 150 | 151 | #ifdef TML_IMPLEMENTATION 152 | 153 | #if !defined(TML_MALLOC) || !defined(TML_FREE) || !defined(TML_REALLOC) 154 | # include 155 | # define TML_MALLOC malloc 156 | # define TML_FREE free 157 | # define TML_REALLOC realloc 158 | #endif 159 | 160 | #if !defined(TML_MEMCPY) 161 | # include 162 | # define TML_MEMCPY memcpy 163 | #endif 164 | 165 | #ifndef TML_NO_STDIO 166 | # include 167 | #endif 168 | 169 | #define TML_NULL 0 170 | 171 | ////crash on errors and warnings to find broken midi files while debugging 172 | //#define TML_ERROR(msg) *(int*)0 = 0xbad; 173 | //#define TML_WARN(msg) *(int*)0 = 0xf00d; 174 | 175 | ////print errors and warnings 176 | //#define TML_ERROR(msg) printf("ERROR: %s\n", msg); 177 | //#define TML_WARN(msg) printf("WARNING: %s\n", msg); 178 | 179 | #ifndef TML_ERROR 180 | #define TML_ERROR(msg) 181 | #endif 182 | 183 | #ifndef TML_WARN 184 | #define TML_WARN(msg) 185 | #endif 186 | 187 | #ifdef __cplusplus 188 | extern "C" { 189 | #endif 190 | 191 | #ifndef TML_NO_STDIO 192 | static int tml_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); } 193 | TMLDEF tml_message* tml_load_filename(const char* filename) 194 | { 195 | struct tml_message* res; 196 | struct tml_stream stream = { TML_NULL, (int(*)(void*,void*,unsigned int))&tml_stream_stdio_read }; 197 | #if __STDC_WANT_SECURE_LIB__ 198 | FILE* f = TML_NULL; fopen_s(&f, filename, "rb"); 199 | #else 200 | FILE* f = fopen(filename, "rb"); 201 | #endif 202 | if (!f) { TML_ERROR("File not found"); return 0; } 203 | stream.data = f; 204 | res = tml_load(&stream); 205 | fclose(f); 206 | return res; 207 | } 208 | #endif 209 | 210 | struct tml_stream_memory { const char* buffer; unsigned int total, pos; }; 211 | static int tml_stream_memory_read(struct tml_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TML_MEMCPY(ptr, m->buffer+m->pos, size); m->pos += size; return size; } 212 | TMLDEF struct tml_message* tml_load_memory(const void* buffer, int size) 213 | { 214 | struct tml_stream stream = { TML_NULL, (int(*)(void*,void*,unsigned int))&tml_stream_memory_read }; 215 | struct tml_stream_memory f = { 0, 0, 0 }; 216 | f.buffer = (const char*)buffer; 217 | f.total = size; 218 | stream.data = &f; 219 | return tml_load(&stream); 220 | } 221 | 222 | struct tml_track 223 | { 224 | unsigned int Idx, End, Ticks; 225 | }; 226 | 227 | struct tml_tempomsg 228 | { 229 | unsigned int time; 230 | unsigned char type, Tempo[3]; 231 | tml_message* next; 232 | }; 233 | 234 | struct tml_parser 235 | { 236 | unsigned char *buf, *buf_end; 237 | int last_status, message_array_size, message_count; 238 | }; 239 | 240 | enum TMLSystemType 241 | { 242 | TML_TEXT = 0x01, TML_COPYRIGHT = 0x02, TML_TRACK_NAME = 0x03, TML_INST_NAME = 0x04, TML_LYRIC = 0x05, TML_MARKER = 0x06, TML_CUE_POINT = 0x07, 243 | TML_EOT = 0x2f, TML_SMPTE_OFFSET = 0x54, TML_TIME_SIGNATURE = 0x58, TML_KEY_SIGNATURE = 0x59, TML_SEQUENCER_EVENT = 0x7f, 244 | TML_SYSEX = 0xf0, TML_TIME_CODE = 0xf1, TML_SONG_POSITION = 0xf2, TML_SONG_SELECT = 0xf3, TML_TUNE_REQUEST = 0xf6, TML_EOX = 0xf7, TML_SYNC = 0xf8, 245 | TML_TICK = 0xf9, TML_START = 0xfa, TML_CONTINUE = 0xfb, TML_STOP = 0xfc, TML_ACTIVE_SENSING = 0xfe, TML_SYSTEM_RESET = 0xff 246 | }; 247 | 248 | static int tml_readbyte(struct tml_parser* p) 249 | { 250 | return (p->buf == p->buf_end ? -1 : *(p->buf++)); 251 | } 252 | 253 | static int tml_readvariablelength(struct tml_parser* p) 254 | { 255 | unsigned int res = 0, i = 0; 256 | unsigned char c; 257 | for (; i != 4; i++) 258 | { 259 | if (p->buf == p->buf_end) { TML_WARN("Unexpected end of file"); return -1; } 260 | c = *(p->buf++); 261 | if (c & 0x80) res = ((res | (c & 0x7F)) << 7); 262 | else return (int)(res | c); 263 | } 264 | TML_WARN("Invalid variable length byte count"); return -1; 265 | } 266 | 267 | static int tml_parsemessage(tml_message** f, struct tml_parser* p) 268 | { 269 | int deltatime = tml_readvariablelength(p), status = tml_readbyte(p); 270 | tml_message* evt; 271 | 272 | if (deltatime & 0xFFF00000) deltatime = 0; //throw away delays that are insanely high for malformatted midis 273 | if (status < 0) { TML_WARN("Unexpected end of file"); return -1; } 274 | if ((status & 0x80) == 0) 275 | { 276 | // Invalid, use same status as before 277 | if ((p->last_status & 0x80) == 0) { TML_WARN("Undefined status and invalid running status"); return -1; } 278 | p->buf--; 279 | status = p->last_status; 280 | } 281 | else p->last_status = status; 282 | 283 | if (p->message_array_size == p->message_count) 284 | { 285 | //start allocated memory size of message array at 64, double each time until 8192, then add 1024 entries until done 286 | p->message_array_size += (!p->message_array_size ? 64 : (p->message_array_size > 4096 ? 1024 : p->message_array_size)); 287 | *f = (tml_message*)TML_REALLOC(*f, p->message_array_size * sizeof(tml_message)); 288 | if (!*f) { TML_ERROR("Out of memory"); return -1; } 289 | } 290 | evt = *f + p->message_count; 291 | 292 | //check what message we have 293 | if ((status == TML_SYSEX) || (status == TML_EOX)) //sysex 294 | { 295 | //sysex messages are not handled 296 | p->buf += tml_readvariablelength(p); 297 | if (p->buf > p->buf_end) { TML_WARN("Unexpected end of file"); p->buf = p->buf_end; return -1; } 298 | evt->type = 0; 299 | } 300 | else if (status == 0xFF) //meta events 301 | { 302 | int meta_type = tml_readbyte(p), buflen = tml_readvariablelength(p); 303 | unsigned char* metadata = p->buf; 304 | if (meta_type < 0) { TML_WARN("Unexpected end of file"); return -1; } 305 | if (buflen > 0 && (p->buf += buflen) > p->buf_end) { TML_WARN("Unexpected end of file"); p->buf = p->buf_end; return -1; } 306 | 307 | switch (meta_type) 308 | { 309 | case TML_EOT: 310 | if (buflen != 0) { TML_WARN("Invalid length for EndOfTrack event"); return -1; } 311 | if (!deltatime) return TML_EOT; //no need to store this message 312 | evt->type = TML_EOT; 313 | break; 314 | 315 | case TML_SET_TEMPO: 316 | if (buflen != 3) { TML_WARN("Invalid length for SetTempo meta event"); return -1; } 317 | evt->type = TML_SET_TEMPO; 318 | ((struct tml_tempomsg*)evt)->Tempo[0] = metadata[0]; 319 | ((struct tml_tempomsg*)evt)->Tempo[1] = metadata[1]; 320 | ((struct tml_tempomsg*)evt)->Tempo[2] = metadata[2]; 321 | break; 322 | 323 | default: 324 | evt->type = 0; 325 | } 326 | } 327 | else //channel message 328 | { 329 | int param; 330 | if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; } 331 | evt->key = (param & 0x7f); 332 | evt->channel = (status & 0x0f); 333 | switch (evt->type = (status & 0xf0)) 334 | { 335 | case TML_NOTE_OFF: 336 | case TML_NOTE_ON: 337 | case TML_KEY_PRESSURE: 338 | case TML_CONTROL_CHANGE: 339 | if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; } 340 | evt->velocity = (param & 0x7f); 341 | break; 342 | 343 | case TML_PITCH_BEND: 344 | if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; } 345 | evt->pitch_bend = ((param & 0x7f) << 7) | evt->key; 346 | break; 347 | 348 | case TML_PROGRAM_CHANGE: 349 | case TML_CHANNEL_PRESSURE: 350 | evt->velocity = 0; 351 | break; 352 | 353 | default: //ignore system/manufacture messages 354 | evt->type = 0; 355 | break; 356 | } 357 | } 358 | 359 | if (deltatime || evt->type) 360 | { 361 | evt->time = deltatime; 362 | p->message_count++; 363 | } 364 | return evt->type; 365 | } 366 | 367 | TMLDEF tml_message* tml_load(struct tml_stream* stream) 368 | { 369 | int num_tracks, division, trackbufsize = 0; 370 | unsigned char midi_header[14], *trackbuf = TML_NULL; 371 | struct tml_message* messages = TML_NULL; 372 | struct tml_track *tracks, *t, *tracksEnd; 373 | struct tml_parser p = { TML_NULL, TML_NULL, 0, 0, 0 }; 374 | 375 | // Parse MIDI header 376 | if (stream->read(stream->data, midi_header, 14) != 14) { TML_ERROR("Unexpected end of file"); return messages; } 377 | if (midi_header[0] != 'M' || midi_header[1] != 'T' || midi_header[2] != 'h' || midi_header[3] != 'd' || 378 | midi_header[7] != 6 || midi_header[9] > 2) { TML_ERROR("Doesn't look like a MIDI file: invalid MThd header"); return messages; } 379 | if (midi_header[12] & 0x80) { TML_ERROR("File uses unsupported SMPTE timing"); return messages; } 380 | num_tracks = (int)(midi_header[10] << 8) | midi_header[11]; 381 | division = (int)(midi_header[12] << 8) | midi_header[13]; //division is ticks per beat (quarter-note) 382 | if (num_tracks <= 0 && division <= 0) { TML_ERROR("Doesn't look like a MIDI file: invalid track or division values"); return messages; } 383 | 384 | // Allocate temporary tracks array for parsing 385 | tracks = (struct tml_track*)TML_MALLOC(sizeof(struct tml_track) * num_tracks); 386 | tracksEnd = &tracks[num_tracks]; 387 | for (t = tracks; t != tracksEnd; t++) t->Idx = t->End = t->Ticks = 0; 388 | 389 | // Read all messages for all tracks 390 | for (t = tracks; t != tracksEnd; t++) 391 | { 392 | unsigned char track_header[8]; 393 | int track_length; 394 | if (stream->read(stream->data, track_header, 8) != 8) { TML_WARN("Unexpected end of file"); break; } 395 | if (track_header[0] != 'M' || track_header[1] != 'T' || track_header[2] != 'r' || track_header[3] != 'k') 396 | { TML_WARN("Invalid MTrk header"); break; } 397 | 398 | // Get size of track data and read into buffer (allocate bigger buffer if needed) 399 | track_length = track_header[7] | (track_header[6] << 8) | (track_header[5] << 16) | (track_header[4] << 24); 400 | if (track_length < 0) { TML_WARN("Invalid MTrk header"); break; } 401 | if (trackbufsize < track_length) { TML_FREE(trackbuf); trackbuf = (unsigned char*)TML_MALLOC(trackbufsize = track_length); } 402 | if (stream->read(stream->data, trackbuf, track_length) != track_length) { TML_WARN("Unexpected end of file"); break; } 403 | 404 | t->Idx = p.message_count; 405 | for (p.buf_end = (p.buf = trackbuf) + track_length; p.buf != p.buf_end;) 406 | { 407 | int type = tml_parsemessage(&messages, &p); 408 | if (type == TML_EOT || type < 0) break; //file end or illegal data encountered 409 | } 410 | if (p.buf != p.buf_end) { TML_WARN( "Track length did not match data length"); } 411 | t->End = p.message_count; 412 | } 413 | TML_FREE(trackbuf); 414 | 415 | // Change message time signature from delta ticks to actual msec values and link messages ordered by time 416 | if (p.message_count) 417 | { 418 | tml_message *PrevMessage = TML_NULL, *Msg, *MsgEnd, Swap; 419 | unsigned int ticks = 0, tempo_ticks = 0; //tick counter and value at last tempo change 420 | int step_smallest, msec, tempo_msec = 0; //msec value at last tempo change 421 | double ticks2time = 500000 / (1000.0 * division); //milliseconds per tick 422 | 423 | // Loop through all messages over all tracks ordered by time 424 | for (step_smallest = 0; step_smallest != 0x7fffffff; ticks += step_smallest) 425 | { 426 | step_smallest = 0x7fffffff; 427 | msec = tempo_msec + (int)((ticks - tempo_ticks) * ticks2time); 428 | for (t = tracks; t != tracksEnd; t++) 429 | { 430 | if (t->Idx == t->End) continue; 431 | for (Msg = &messages[t->Idx], MsgEnd = &messages[t->End]; Msg != MsgEnd && t->Ticks + Msg->time == ticks; Msg++, t->Idx++) 432 | { 433 | t->Ticks += Msg->time; 434 | if (Msg->type == TML_SET_TEMPO) 435 | { 436 | unsigned char* Tempo = ((struct tml_tempomsg*)Msg)->Tempo; 437 | ticks2time = ((Tempo[0]<<16)|(Tempo[1]<<8)|Tempo[2])/(1000.0 * division); 438 | tempo_msec = msec; 439 | tempo_ticks = ticks; 440 | } 441 | if (Msg->type) 442 | { 443 | Msg->time = msec; 444 | if (PrevMessage) { PrevMessage->next = Msg; PrevMessage = Msg; } 445 | else { Swap = *Msg; *Msg = *messages; *messages = Swap; PrevMessage = messages; } 446 | } 447 | } 448 | if (Msg != MsgEnd && t->Ticks + Msg->time > ticks) 449 | { 450 | int step = (int)(t->Ticks + Msg->time - ticks); 451 | if (step < step_smallest) step_smallest = step; 452 | } 453 | } 454 | } 455 | if (PrevMessage) PrevMessage->next = TML_NULL; 456 | else p.message_count = 0; 457 | } 458 | TML_FREE(tracks); 459 | 460 | if (p.message_count == 0) 461 | { 462 | TML_FREE(messages); 463 | messages = TML_NULL; 464 | } 465 | 466 | return messages; 467 | } 468 | 469 | TMLDEF tml_message* tml_load_tsf_stream(struct tsf_stream* stream) 470 | { 471 | return tml_load((struct tml_stream*)stream); 472 | } 473 | 474 | TMLDEF int tml_get_info(tml_message* Msg, int* out_used_channels, int* out_used_programs, int* out_total_notes, unsigned int* out_time_first_note, unsigned int* out_time_length) 475 | { 476 | int used_programs = 0, used_channels = 0, total_notes = 0; 477 | unsigned int time_first_note = 0xffffffff, time_length = 0; 478 | unsigned char channels[16] = { 0 }, programs[128] = { 0 }; 479 | for (;Msg; Msg = Msg->next) 480 | { 481 | time_length = Msg->time; 482 | if (Msg->type == TML_PROGRAM_CHANGE && !programs[(int)Msg->program]) { programs[(int)Msg->program] = 1; used_programs++; } 483 | if (Msg->type != TML_NOTE_ON) continue; 484 | if (time_first_note == 0xffffffff) time_first_note = time_length; 485 | if (!channels[Msg->channel]) { channels[Msg->channel] = 1; used_channels++; } 486 | total_notes++; 487 | } 488 | if (time_first_note == 0xffffffff) time_first_note = 0; 489 | if (out_used_channels ) *out_used_channels = used_channels; 490 | if (out_used_programs ) *out_used_programs = used_programs; 491 | if (out_total_notes ) *out_total_notes = total_notes; 492 | if (out_time_first_note) *out_time_first_note = time_first_note; 493 | if (out_time_length ) *out_time_length = time_length; 494 | return total_notes; 495 | } 496 | 497 | TMLDEF int tml_get_tempo_value(tml_message* msg) 498 | { 499 | unsigned char* Tempo; 500 | if (!msg || msg->type != TML_SET_TEMPO) return 0; 501 | Tempo = ((struct tml_tempomsg*)msg)->Tempo; 502 | return ((Tempo[0]<<16)|(Tempo[1]<<8)|Tempo[2]); 503 | } 504 | 505 | TMLDEF void tml_free(tml_message* f) 506 | { 507 | TML_FREE(f); 508 | } 509 | 510 | #ifdef __cplusplus 511 | } 512 | #endif 513 | 514 | #endif //TML_IMPLEMENTATION 515 | -------------------------------------------------------------------------------- /source/stranded.c: -------------------------------------------------------------------------------- 1 | #include "pixie.h" 2 | #include "dialog.h" 3 | #include "dialog_playback.h" 4 | 5 | #ifndef PIXIE_NO_BUILD 6 | void* adjust_sprite( const char* filenames[], int count, int* out_size ); 7 | void* build_dialog( char const* filenames[], int count, int* out_size ); 8 | #endif /* PIXIE_NO_BUILD */ 9 | 10 | #define ASSET_DIALOG( id, filename ) id, 11 | 12 | 13 | ASSETS_BEGIN( "stranded.dat" ) 14 | ASSET_BINARY( SOUNDFONT, "stranded/AweROMGM.sf2" ) 15 | ASSET_SONG( SONG, "stranded/stranded.mid" ) 16 | ASSET_FONT( FONT, "stranded/Volter__28Goldfish_29.ttf" ) 17 | ASSET_PALETTE( PALETTE, "stranded/palette.png" ) 18 | ASSET_SPRITE( BACKGROUND, "stranded/background.png" ) 19 | ASSET_SPRITE( TREELINE, "stranded/sprites/treeline.png" ) 20 | ASSET_SPRITE( FACE, "stranded/sprites/face.png" ) 21 | ASSET_SPRITE( LOGO, "stranded/logo.png" ) 22 | ASSET_DIALOG( DIALOG, "stranded/dialog.dlg" ) 23 | ASSET_SPRITE( BOAT, "stranded/sprites/boat.png" ) 24 | ASSET_SPRITE( CHAIR, "stranded/sprites/chair.png" ) 25 | ASSET_SPRITE( GUY_CHAIR, "stranded/sprites/guychair.png" ) 26 | ASSET_SPRITE( GUY_IDLE, "stranded/sprites/guy_idle.png" ) 27 | ASSET_SPRITE( GUY_JUMP, "stranded/sprites/guy_jump_*.png" ) 28 | ASSET_SPRITE( GUY_LEFT, "stranded/sprites/guy_walk_left_*.png" ) 29 | ASSET_SPRITE( GUY_RIGHT, "stranded/sprites/guy_walk_right_*.png" ) 30 | ASSET_SPRITE( HUT1, "stranded/sprites/hut1.png" ) 31 | ASSET_SPRITE( HUT2, "stranded/sprites/hut2.png" ) 32 | ASSET_SPRITE( HUT_BOOK, "stranded/sprites/hut_book.png" ) 33 | ASSET_SPRITE( HUT_BOOK_NOBULB, "stranded/sprites/hut_book_nobulb.png" ) 34 | ASSET_SPRITE( HUT_TV, "stranded/sprites/hut_tv.png" ) 35 | ASSET_SPRITE( HUT_TV_NODISH, "stranded/sprites/hut_tv_nodish.png" ) 36 | ASSET_SPRITE( PALMTREE, "stranded/sprites/palmtree.png" ) 37 | ASSET_SPRITE( PENGUIN, "stranded/sprites/penguin.png" ) 38 | ASSET_SPRITE( PENGUIN_BATTERY, "stranded/sprites/penguin_battery.png" ) 39 | ASSET_SPRITE( PENGUIN_WALK, "stranded/sprites/penguin_walk_*.png" ) 40 | ASSET_SPRITE( RADIO_BATTERY, "stranded/sprites/radio_battery.png" ) 41 | ASSET_SPRITE( RADIO_BATTERY_DISH, "stranded/sprites/radio_battery_dish.png" ) 42 | ASSET_SPRITE( RADIO_BULB, "stranded/sprites/radio_bulb.png" ) 43 | ASSET_SPRITE( RADIO_BULB_BATTERY, "stranded/sprites/radio_bulb_battery.png" ) 44 | ASSET_SPRITE( RADIO_BULB_DISH, "stranded/sprites/radio_bulb_dish.png" ) 45 | ASSET_SPRITE( RADIO_DISH, "stranded/sprites/radio_dish.png" ) 46 | ASSET_SPRITE( RADIO_FULL, "stranded/sprites/radio_full.png" ) 47 | ASSET_SPRITE( RADIO_NONE, "stranded/sprites/radio_none.png" ) 48 | ASSET_SPRITE( RADIO_SPR, "stranded/sprites/radio_spr.png" ) 49 | ASSET_SPRITE( TREE2, "stranded/sprites/tree2.png" ) 50 | ASSETS_END() 51 | 52 | 53 | 54 | int is_near( int spr, int xpos ) { 55 | return ( xpos > sprite_x( spr ) - 25 ) && ( xpos < sprite_x( spr ) + 25 ); 56 | } 57 | 58 | 59 | void set_interact( int spr, char const* str, int interact, int offset ) { 60 | label_text( interact, str ); 61 | sprite_pos( interact, sprite_x( spr ) + 160 + offset, sprite_y( interact ) ); 62 | } 63 | 64 | // Color indexes of the loaded palette 65 | int color_green = 17; 66 | int color_yellow = 30; 67 | int color_purple = 51; 68 | int color_brown = 35; 69 | 70 | void setup_speech( int speech, char const* character ) 71 | { 72 | struct { 73 | char const* name; 74 | int x; 75 | int y; 76 | int color; 77 | int wrap; 78 | } speech_settings[] = { 79 | { "player_sitting", 50, 85, color_green, 220 }, 80 | { "player", 20, 60, color_green, 220 }, 81 | { "player_hut", 100, 60, color_green, 220 }, 82 | { "player_radio", 50, 20, color_green, 220 }, 83 | { "tv_guy", 20, 20, color_yellow, 220 }, 84 | { "book_guy", 20, 20, color_purple, 220 }, 85 | { "penguin", 185, 75, color_brown, 120 }, 86 | }; 87 | 88 | for( int i = 0; i < ARRAY_COUNT( speech_settings ) ; ++i ) { 89 | if( compare( str( speech_settings[ i ].name ), str( character ) ) == 0 ) { 90 | sprite_pos( speech, speech_settings[ i ].x, speech_settings[ i ].y ); 91 | label_color( speech, speech_settings[ i ].color ); 92 | label_wrap( speech, speech_settings[ i ].wrap ); 93 | return; 94 | } 95 | } 96 | } 97 | 98 | 99 | void title_screen( void ) { 100 | int spr_index = 1; 101 | sprite( spr_index++, -1050, 0, BACKGROUND ); 102 | sprite( spr_index++, -1050, 0, TREELINE ); 103 | 104 | int face = sprite( spr_index++, 0, 200, FACE ) ; 105 | int logo = sprite( spr_index++, 0, -200, LOGO ) ; 106 | sprite_move_y( face, move_delay( 6 ), move_ease_out_elastic( 120, 50 ), move_end() ); 107 | sprite_move_y( logo, move_delay( 30 ), move_ease_out_bounce( 60, 0 ), move_end() ); 108 | 109 | int credits = label( spr_index++, -160, 185, "Code, art, music and design by Mattias Gustavsson", 2, FONT ); 110 | label_align( credits, TEXT_ALIGN_CENTER ); 111 | label_outline( credits, 0 ); 112 | label_shadow( credits, 0 ); 113 | sprite_move_x( credits, move_delay( 30 ), move_ease_out_elastic( 60, 160 ), move_end() ); 114 | 115 | LOOP { 116 | wait_vbl(); 117 | if( key_was_pressed( KEY_ESCAPE ) ) end( 0 ); // exit with no error 118 | if( key_was_pressed( KEY_RETURN ) && key_is_down( KEY_MENU ) ) fullscreen() ? fullscreen_off() : fullscreen_on(); 119 | if( key_was_pressed( KEY_TAB ) ) crt_mode() ? crt_mode_off() : crt_mode_on(); 120 | if( key_was_pressed( KEY_SPACE ) ) { 121 | sprites_off(); 122 | return; 123 | } 124 | } 125 | } 126 | 127 | 128 | DECLARE_ARRAY( objects, int, 32 ); 129 | 130 | 131 | int pixmain( int argc, char** argv ) { 132 | (void) argc, (void) argv; 133 | #ifndef PIXIE_NO_BUILD 134 | register_asset_type( "DIALOG", build_dialog ); 135 | register_asset_type( "SPRITE", adjust_sprite ); 136 | #endif 137 | if( load_assets() != 0 ) return 1; 138 | 139 | // title( "Stranded" ); 140 | // screen_size( 320, 200 ); 141 | // border_size( 32, 44 ); 142 | fullscreen_on(); 143 | crt_mode_on(); 144 | // mouse_hide(); 145 | 146 | load_palette( PALETTE ); 147 | set_soundfont( SOUNDFONT ); 148 | play_song( SONG ); 149 | 150 | title_screen(); 151 | 152 | objects_array objects = { 0 }; 153 | 154 | int spr_index = 1; 155 | 156 | int background = sprite( spr_index++, -65, 0, BACKGROUND ); 157 | int treeline = sprite( spr_index++, -65, 0, TREELINE ); 158 | int radio = sprite( spr_index++, 240, 0, RADIO_SPR ); objects_add( &objects, radio ); 159 | int hut1 = sprite( spr_index++, 540, 0, HUT1 ); objects_add( &objects, hut1 ); 160 | int hut2 = sprite( spr_index++, 850, 20, HUT2 ); objects_add( &objects, hut2 ); 161 | objects_add( &objects, sprite( spr_index++, 180, -30, TREE2 ) ); 162 | objects_add( &objects, sprite( spr_index++, 120, -10, PALMTREE ) ); 163 | int chair = sprite( spr_index++, 65, 25, CHAIR ); objects_add( &objects, chair ); 164 | int penguin_battery = sprite( spr_index++, 970 + 62, -10, PENGUIN_BATTERY ); objects_add( &objects, penguin_battery ); 165 | int penguin = sprite( spr_index++, 970, -10, PENGUIN ); objects_add( &objects, penguin ); 166 | 167 | int xpos = 65; 168 | int guy = sprite( spr_index++, xpos, 25, GUY_CHAIR ); 169 | 170 | int interact = label( spr_index++, 160, 70, "", 2 /* brightest_color() */, FONT ); 171 | label_align( interact, TEXT_ALIGN_CENTER ); 172 | label_outline( interact, 0 ); 173 | label_shadow( interact, 0 ); 174 | 175 | int radio_back = sprite( spr_index++, -250, 0, BACKGROUND ); 176 | sprite_hide( radio_back ); 177 | 178 | int radio_pic = sprite( spr_index++, 0, 0, RADIO_FULL ); 179 | sprite_hide( radio_pic ); 180 | 181 | int hut_tv_pic = sprite( spr_index++, 0, 0, HUT_TV ); 182 | sprite_hide( hut_tv_pic ); 183 | 184 | int hut_bulb = sprite( spr_index++, 0, 0, HUT_BOOK ); 185 | sprite_hide( hut_bulb ); 186 | 187 | int guy_inside = sprite( spr_index++, 0, 0, GUY_IDLE ); 188 | sprite_hide( guy_inside ); 189 | 190 | int boat = sprite( spr_index++, -320, 30, BOAT ); 191 | 192 | int speech = label( spr_index++, 50, 70, "", 18 /* green */, FONT ); 193 | label_align( speech, TEXT_ALIGN_CENTER ); 194 | label_outline( speech, 0 ); 195 | label_shadow( speech, 0 ); 196 | label_wrap( speech, 220 ); 197 | 198 | dialog_playback_t dlg; 199 | dialog_playback_init( &dlg ); 200 | dialog_playback_add_dialog( &dlg, (dialog_t*) asset_data( DIALOG ) ); 201 | 202 | int fadeout = 0; 203 | int fadeout_delay = 120; 204 | int speech_visible = 1; 205 | int rescued = 0; 206 | int may_stand = 0; 207 | int looked_at_radio = 0; 208 | int sitting = 1; 209 | int penguin_gone = 0; 210 | int at_radio = 0; 211 | int at_hut_tv = 0; 212 | int at_hut_bulb = 0; 213 | int has_dish = 0; 214 | int has_bulb = 0; 215 | int has_battery = 0; 216 | int shown_intro = 0; 217 | 218 | float anim = 0.0f; 219 | 220 | LOOP { 221 | for( int i = 0; i < objects_count( &objects ); ++i ) 222 | sprite_origin( objects_get( &objects, i ), xpos, 0 ); 223 | 224 | sprite_origin( interact, xpos, 0 ); 225 | 226 | sprite_origin( guy, xpos, 0 ); 227 | sprite_pos( guy, xpos, sprite_y( guy ) ); 228 | sprite_pos( background, -xpos, sprite_y( background ) ); 229 | sprite_pos( treeline, -xpos, sprite_y( treeline ) ); 230 | 231 | if( !shown_intro ) { 232 | wait( 60 ); 233 | dialog_playback_start_conversation( &dlg, "lets_go_home" ); 234 | shown_intro = 1; 235 | } 236 | 237 | anim += 25.0f / 60.0f; 238 | sprite_cel( guy, (int) anim ); 239 | 240 | wait_vbl(); 241 | 242 | 243 | if( fadeout ) { 244 | if( --fadeout_delay <= 0 ) { 245 | int all_black = 1; 246 | for( int i = 0; i < 64; ++i ) { 247 | rgb_t c = getcol( i ); 248 | if( c.r > 0 || c.g > 0 || c.b > 0 ) all_black = 0; 249 | if( c.r > 0 ) c.r = (u8)( c.r - min( c.r, 3 ) ); 250 | if( c.r < 80 && c.g > 0 ) c.g = (u8)( c.g - min( c.g, 3 ) ); 251 | if( c.g < 80 && c.b > 0 ) c.b = (u8)( c.b - min( c.b, 3 ) ); 252 | setcol( i, c ); 253 | } 254 | if( all_black ) end( 0 ); 255 | } 256 | } 257 | 258 | if( key_was_pressed( KEY_ESCAPE ) ) end( 0 ); // exit with no error 259 | if( key_was_pressed( KEY_RETURN ) && key_is_down( KEY_MENU ) ) fullscreen() ? fullscreen_off() : fullscreen_on(); 260 | if( key_was_pressed( KEY_TAB ) ) crt_mode() ? crt_mode_off() : crt_mode_on(); 261 | if( dialog_playback_state( &dlg ) != DIALOG_PLAYBACK_STATE_READY ) { 262 | if( dialog_playback_state( &dlg ) == DIALOG_PLAYBACK_STATE_SENDING_EVENT ) { 263 | string event = str( dialog_playback_current_conversation_event( &dlg ) ); 264 | if( starts_with( event, str( "SetFlag:" ) ) ) { 265 | string flag = trim( mid( event, 8, -1 ) ); 266 | if( compare( flag, str( "may_stand" ) ) == 0 ) { 267 | may_stand = 1; 268 | } 269 | if( compare( flag, str( "looked_at_radio" ) ) == 0 ) { 270 | looked_at_radio = 1; 271 | } 272 | if( compare( flag, str( "penguin_running" ) ) == 0 ) { 273 | sprite_pos( penguin, 970 + 62, sprite_y( penguin ) ); 274 | sprite_bitmap( penguin, PENGUIN_WALK ); 275 | sprite_move_x( penguin, move_delay( 30 ), move_linear( 60, 970 + 62 + 160 ), move_end() ); 276 | wait( 30 ); 277 | float penganim = 0.0f; 278 | for( int i = 0; i < 90; ++i ) { 279 | wait_vbl(); 280 | penganim += 25.0f / 60.0f; 281 | sprite_cel( penguin, (int) penganim ); 282 | } 283 | sprite_hide( penguin ); 284 | } 285 | if( compare( flag, str( "penguin_gone" ) ) == 0 ) { 286 | penguin_gone = 1; 287 | dialog_playback_execute( &dlg ); 288 | } 289 | if( compare( flag, str( "rescued" ) ) == 0 ) { 290 | rescued = 1; 291 | } 292 | } 293 | if( starts_with( event, str( "ClearFlag:" ) ) ) { 294 | string flag = trim( mid( event, 10, -1 ) ); 295 | if( compare( flag, str( "at_hut_tv" ) ) == 0 ) { 296 | sprite_hide( hut_tv_pic ); 297 | sprite_hide( guy_inside ); 298 | sprite_bitmap( hut_tv_pic, HUT_TV_NODISH ); 299 | has_dish = 1; 300 | at_hut_tv = 0; 301 | } 302 | if( compare( flag, str( "at_hut_bulb" ) ) == 0 ) { 303 | sprite_hide( hut_bulb ); 304 | sprite_hide( guy_inside ); 305 | sprite_bitmap( hut_bulb, HUT_BOOK_NOBULB ); 306 | has_bulb = 1; 307 | at_hut_bulb = 0; 308 | } 309 | } 310 | dialog_playback_execute( &dlg ); 311 | } else if( dialog_playback_state( &dlg ) == DIALOG_PLAYBACK_STATE_DISPLAYING_SPEECH ) { 312 | setup_speech( speech, dialog_playback_current_conversation_line_actor( &dlg ) ); 313 | label_text( speech, dialog_playback_current_conversation_line_text( &dlg ) ); 314 | if( speech_visible && key_was_pressed( KEY_SPACE ) ) { 315 | speech_visible = 0; 316 | label_text( speech, "" ); 317 | wait( 30 ); 318 | dialog_playback_execute( &dlg ); 319 | speech_visible = 1; 320 | } 321 | } else { 322 | dialog_playback_execute( &dlg ); 323 | } 324 | continue; 325 | } else { 326 | label_text( speech, "" ); 327 | } 328 | 329 | label_text( interact, "" ); 330 | if( !sitting && !at_radio && !at_hut_tv && !at_hut_bulb && !rescued ) { 331 | if( is_near( radio, xpos ) ) set_interact( radio, "Radio", interact, 0 ); 332 | if( is_near( hut1, xpos ) ) set_interact( hut1, "Hut", interact, 0 ); 333 | if( is_near( hut2, xpos ) ) set_interact( hut2, "Hut", interact, 0 ); 334 | if( !penguin_gone && is_near( penguin, xpos ) ) set_interact( penguin, "Penguin", interact, 60 ); 335 | if( !has_battery && penguin_gone && is_near( penguin_battery, xpos ) ) set_interact( penguin_battery, "Battery", interact, 0 ); 336 | 337 | if( key_is_down( KEY_LEFT ) ) { 338 | xpos -= 2; 339 | if( xpos < 10 ) xpos = 10; 340 | sprite( guy, sprite_x( guy ), sprite_y( guy ), GUY_LEFT ); 341 | } else if( key_is_down( KEY_RIGHT ) ) { 342 | xpos += 2; 343 | if( xpos > 1120 ) xpos = 1120; 344 | sprite( guy, sprite_x( guy ), sprite_y( guy ), GUY_RIGHT ); 345 | } else { 346 | sprite( guy, sprite_x( guy ), sprite_y( guy ), GUY_IDLE ); 347 | } 348 | 349 | if( key_was_pressed( KEY_SPACE ) ) { 350 | if( is_near( radio, xpos ) ) { 351 | sprite_show( radio_pic ); 352 | if( has_dish && has_bulb && has_battery ) { 353 | sprite_bitmap( radio_pic, RADIO_FULL ); 354 | dialog_playback_start_conversation( &dlg, "radio_all" ); 355 | } else if( has_dish && has_bulb ) { 356 | sprite_bitmap( radio_pic, RADIO_BULB_DISH ); 357 | dialog_playback_start_conversation( &dlg, "radio_bulb_dish" ); 358 | } else if( has_battery && has_bulb ) { 359 | sprite_bitmap( radio_pic, RADIO_BULB_BATTERY ); 360 | dialog_playback_start_conversation( &dlg, "radio_bulb_battery" ); 361 | } else if( has_dish && has_battery ) { 362 | sprite_bitmap( radio_pic, RADIO_BATTERY_DISH ); 363 | dialog_playback_start_conversation( &dlg, "radio_battery_dish" ); 364 | } else if( has_dish ) { 365 | sprite_bitmap( radio_pic, RADIO_DISH ); 366 | dialog_playback_start_conversation( &dlg, "radio_dish" ); 367 | } else if( has_battery ) { 368 | sprite_bitmap( radio_pic, RADIO_BATTERY ); 369 | dialog_playback_start_conversation( &dlg, "radio_battery" ); 370 | } else if( has_bulb ) { 371 | sprite_bitmap( radio_pic, RADIO_BULB ); 372 | dialog_playback_start_conversation( &dlg, "radio_bulb" ); 373 | } else { 374 | sprite_bitmap( radio_pic, RADIO_NONE ); 375 | if( !looked_at_radio ) 376 | dialog_playback_start_conversation( &dlg, "radio_first" ); 377 | else 378 | dialog_playback_start_conversation( &dlg, "radio_none" ); 379 | } 380 | 381 | sprite_show( radio_back ); 382 | at_radio = 1; 383 | } 384 | 385 | if( is_near( hut1, xpos ) ) { 386 | sprite_show( hut_tv_pic ); 387 | sprite_show( guy_inside ); 388 | sprite_pos( guy_inside, 100, 40 ); 389 | if( !has_dish ) 390 | dialog_playback_start_conversation( &dlg, "hut_tv" ); 391 | else 392 | dialog_playback_start_conversation( &dlg, "hut_tv_sleep" ); 393 | at_hut_tv = 1; 394 | } 395 | 396 | if( is_near( hut2, xpos ) ) { 397 | sprite_show( hut_bulb ); 398 | sprite_show( guy_inside ); 399 | sprite_pos( guy_inside, 100, 40 ); 400 | if( !has_bulb ) 401 | dialog_playback_start_conversation( &dlg, "hut_book" ); 402 | else 403 | dialog_playback_start_conversation( &dlg, "hut_book_sleep" ); 404 | at_hut_bulb = 1; 405 | } 406 | 407 | if( !penguin_gone && is_near( penguin, xpos ) ) { 408 | sprite_bitmap( penguin, PENGUIN ); 409 | dialog_playback_start_conversation( &dlg, "penguin" ); 410 | label_text( interact, "" ); 411 | sprite_bitmap( guy, GUY_IDLE ); 412 | } 413 | 414 | if( !has_battery && penguin_gone && is_near( penguin_battery, xpos ) ) { 415 | label_text( interact, "" ); 416 | sprite_hide( penguin ); 417 | dialog_playback_start_conversation( &dlg, "grab_battery" ); 418 | sprite_hide( penguin_battery ); 419 | has_battery = 1; 420 | } 421 | 422 | if( is_near( chair, xpos ) ) { 423 | sitting = 1; 424 | xpos = 65; 425 | sprite_bitmap( guy, GUY_CHAIR ); 426 | sprite_pos( guy, 0, 25 ); 427 | } 428 | } 429 | 430 | } else if( sitting && may_stand ) { 431 | if( key_was_pressed( KEY_SPACE ) ) { 432 | sitting = 0; 433 | sprite( guy, 0, 30, GUY_IDLE ); 434 | } 435 | } else if( at_radio ) { 436 | if( key_was_pressed( KEY_SPACE ) ) { 437 | sprite_hide( radio_pic ); 438 | sprite_hide( radio_back ); 439 | at_radio = 0; 440 | 441 | if( rescued ) { 442 | sprite_bitmap( guy, GUY_JUMP ); 443 | xpos = 20; 444 | sprite_move_x( boat, move_delay( 90 ), move_ease_out_quad( 120, -190 ), move_end() ); 445 | fadeout = 1; 446 | } 447 | } 448 | } else if( at_hut_tv ) { 449 | if( key_was_pressed( KEY_SPACE ) ) { 450 | sprite_hide( hut_tv_pic ); 451 | sprite_hide( guy_inside ); 452 | sprite_bitmap( hut_tv_pic, HUT_TV_NODISH ); 453 | has_dish = 1; 454 | at_hut_tv = 0; 455 | } 456 | } else if( at_hut_bulb ) { 457 | if( key_was_pressed( KEY_SPACE ) ) { 458 | sprite_hide( hut_bulb ); 459 | sprite_hide( guy_inside ); 460 | sprite_bitmap( hut_bulb, HUT_BOOK_NOBULB ); 461 | has_bulb = 1; 462 | at_hut_bulb = 0; 463 | } 464 | } 465 | 466 | dialog_playback_execute( &dlg ); 467 | } 468 | } 469 | 470 | 471 | #define DIALOG_IMPLEMENTATION 472 | #include "dialog.h" 473 | 474 | #define DIALOG_PLAYBACK_IMPLEMENTATION 475 | #include "dialog_playback.h" 476 | 477 | #define PIXIE_IMPLEMENTATION 478 | #include "pixie.h" 479 | 480 | 481 | #ifndef PIXIE_NO_BUILD 482 | 483 | #define _CRT_NONSTDC_NO_DEPRECATE 484 | #define _CRT_SECURE_NO_WARNINGS 485 | #include 486 | #include 487 | #include "img.h" 488 | #include "pixie/stb_image.h" 489 | #include "pixie/stb_image_write.h" 490 | 491 | void* build_dialog( char const* filenames[], int count, int* out_size ) { 492 | if( count != 1 ) return 0; 493 | 494 | int in_size = 0; 495 | char* in_data = load_text_file( filenames[ 0 ], &in_size ); 496 | if( !in_data ) return 0; 497 | 498 | dialog_t* dialog = dialog_create( in_data, in_size ); 499 | free_text_file( in_data ); 500 | if( !dialog ) return 0; 501 | int size = dialog_save( dialog, 0, 0 ); 502 | void* out_data = malloc( (size_t) size ); 503 | *out_size = dialog_save( dialog, out_data, size ); 504 | dialog_destroy( dialog ); 505 | return out_data; 506 | } 507 | 508 | 509 | void process_image( IMG_U32* abgr, int w, int h ) { 510 | img_t img = img_from_abgr32( abgr, w, h ); 511 | img_adjust_brightness( &img, 0.065f ); 512 | img_sharpen( &img, 0.5f, 1.0f ); 513 | img_adjust_contrast( &img, 1.15f ); 514 | img_adjust_saturation( &img, 0.05f ); 515 | img_to_argb32( &img, abgr ); 516 | img_free( &img ); 517 | } 518 | 519 | 520 | void* adjust_sprite( const char* filenames[], int count, int* out_size ) { 521 | char** tempnames = (char**) malloc( sizeof( *tempnames ) * count ); 522 | for( int i = 0; i < count; ++i ) { 523 | int w, h, c; 524 | stbi_uc* img = stbi_load( filenames[ i ], &w, &h, &c, 4 ); 525 | if( !img ) return NULL; 526 | if( strstr( filenames[ i ], "/sprites/" ) != 0 ) { 527 | process_image( (IMG_U32*) img, w, h ); 528 | } 529 | char tempname[ 64 ]; 530 | sprintf( tempname, "temp_%d.tga", i ); 531 | tempnames[ i ] = strdup( tempname ); 532 | stbi_write_tga_with_rle = 0; 533 | stbi_write_tga( tempname, w, h, 4, img ); 534 | stbi_image_free( img ); 535 | } 536 | void* data = build_sprite( (char const**) tempnames, count, out_size ); 537 | for( int i = 0; i < count; ++i ) { 538 | free( tempnames[ i ] ); 539 | } 540 | free( tempnames ); 541 | return data; 542 | } 543 | 544 | #pragma warning( push ) 545 | #pragma warning( disable: 4365 ) 546 | #define STB_IMAGE_WRITE_IMPLEMENTATION 547 | #include "pixie/stb_image_write.h" 548 | #undef STB_IMAGE_WRITE_IMPLEMENTATION 549 | #pragma warning( pop ) 550 | 551 | #define IMG_IMPLEMENTATION 552 | #include "img.h" 553 | 554 | #endif /* PIXIE_NO_BUILD */ 555 | 556 | --------------------------------------------------------------------------------