diff --git a/.gitignore b/.gitignore index 857f5f79..037349ec 100644 --- a/.gitignore +++ b/.gitignore @@ -20,19 +20,34 @@ Win32_Debug/ Win32_Release/ x64_Debug/ x64_Release/ +build/ *.pyc *.exe +*.dll +*.htm +*.so +*.dylib +*.a +upload.sh +docs/ +lib/ +web/ docs/screens/ samples/assets/mslug/ samples/assets/sunny_land/ samples/c/*.dll samples/csharp/*.dll +samples/SDL2 +samples/wasm +src/sdl +src/libpng/ # deploy packages *.zip *.gz *.bz2 +.luajit-src/ # Visual Studio specific Debug/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..3fe4cd01 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,213 @@ +# Tilengine changelog + +## [2.8.4] - 12/5/2020 +- Deprecates old TLN_SetLayer that required passing a NULL tileset, and replaces with new TLN_SetLayerTilemap that just requires the tilemap +- TLN_SetLoadPath() not needed anymore: chain loading of resources being loaded from subdirectories (.tsx -> .tmx -> png) +- TLN_DrawFrame(): frame parameter can be always 0, in that case it auto-increments internally. Application doesn't need to keep track of frames just to make the animation engine work. +- Updates many samples to use current features, making them easier to follow + +## [2.8.3] - 11/5/2020 +- Changes tileset animation method: discards internal bitmap copy and backup of pixel data, and replaces by an intermediate LUT of indexes +- Removes TLN_CopyTile(), not needed anymore +- Tilemaps created procedurally are visible by default + +## [2.8.2] - 9/5/2020 +- Fixes bug where tileset animations weren't correctly played on layer index other than 0 +- Enhanced TMX compatibility: + - handles "visible" property on layers and objects + - correctly loads "Rectangle" objects in addition to "Tile" ones + - TLN_ObjectInfo returns standard TMX properties: "name", "type" (integer) and "visible" + +## [2.8.0] - 3/5/2020 +- Revamped animation engine. Slotted animations used only by palette (color cycle) +- Tileset sequences: a frame and the target tile can be the same +- Enhanced .tmx loader: more layers and shared tileset (.tsx) on several layers +- Updated ResourcePacker loader to collision-free hashes (get packer get at https://megamarc.itch.io/resourcepacker) +- Forest sample adds optional parameter to specify resource pack file and encryption password +- Window: fullscreen windows are desktop borderless instead of exclusive + +## [2.7.0] - 26/4/2020 +- Adds sprite linked list order: TLN_SetFirstSprite() and TLN_SetNextSprite() +- Adds sprite masking: TLN_EnableSpriteMasking() and TLN_SetSpritesMaskRegion() +- Removes parameter "count" in TLN_CreateSpriteSequence(), now gets detected from sprite names +- CRT effect: when disabled always uses nearest-neighbor interpolation +- Fixes compiler-induced optimization bugs in built-in window +- Samples: removes usage of old sqx files for sprite animation and replaces with TLN_CreateSpriteAnimation() (shooter, supermarioclone, forest) +- Fixes broken loading of txt-based spritesheets in 2.6.0 + +## [2.6.0] - 22/4/2020 +- Supports .json array for spritesheet loader, compatible with Aseprite, Texturepacker, Piskel... +- Supports loading of 24/32 bpp pixel data on the fly. Note that bitmaps must have less than 255 unique colors + +## [2.5.0] - 15/4/2020 +- Adds support for Tiled editor image-based tilesets and objet layers +- ObjectList: Removes old TLN_AddSpriteToList(), TLN_GetObjectsInReigion() and TLN_Object struct +- ObjectList: Renames old TLN_AddObjectToList() to TLN_AddTileObjectToList() +- ObjectList: Adds TLN_GetListNumObjects(), TLN_GetListObject() and TLN_ObjectInfo struct +- Tileset: adds new function TLN_CreateImageTileset() and TLN_TileImage struct +- Fixes TLN_GetLayerTile() crashing when passing negative x +- Adds TLN_ERR_REF_LIST error value to signal bad TLN_ObjectList reference +- Updates sample "Forest" to showcase new image-based tileset and object list features + +## [2.4.2] - 22/12/2019 +- Fixes broken crt effect in x64, due to mismatched bool for uint8_t causing misalignment of indexed data. Updates windows binaries + +## [2.4.1] - 19/11/2019 +- Uses explicit casts on all functions returning generic void*, allows for more strict C++ compile-time checks but it's still full pure C compliant +- Sibling objects are destroyed by explicit TLN_DestroyXXX() destructor instead of improperly calling generic DeleteBaseObject() on siblings, causing memory leaks. +- Other fixes related to issue #53 (proper context destroy, xml parser fix...) +- TLN_SetLayerBitmap() checks for validity of palette before setting permission. + +## [2.4.0] - 9/5/2019 +- Adds object lists and layer objects for decorations: TLN_ObjectList and TLN_SetLayerObjects() +- Adds layer priority: TLN_SetLayerPriority() +- Adds layer parent for synchronized scroll in several layers: TLN_SetLayerParent() +- Adds creation of sequential name-based sprite animations: TLN_CreateSpriteSequence() +- Adds new example for illustrating the new features: "Forest" +- Removes unused assets +- Fixes some compilation warnings + +## [2.3.0] - ]19/3/2019 +- Supports resource packager with AES-128 encryption +- Adds TLN_OpenResourcePack() and TLN_CloseResourcePack() to bind a resource pack created with the `respack` tool: https://megamarc.itch.io/resourcepacker + +## [2.2.1] - 17/3/2019 +- Windowing: adds TLN_GetWindowWidth() and TLN_GetWindowHeight() to get actual size of created window, to properly scaling mouse coordinates when using SDL_callback. +- Sprite: adds TLN_GetSpriteState() function and TLN_SpriteState structure to query detailed runtime info about a given sprite (position, actual size, etc.) + +## [2.2.0 MPL] - 18/2/2019 +- Changes license to MPL 2.0. Allows linking as static library, compatible with licensing requirements of console devkits + +## [2.1.2] 2/2/2019 +- Window: expands unique inputs up to 32 per player +- Window: added two new standard inputs: INPUT_QUIT for closing the window, and INPUT_CRT for toggling the CRT effect. They're assigned to Escape and Backspace by default, but can be remapped with TLN_DefineInputKey() +- Library: prints version, CPU word size and build date to the standard output on TLN_Init() + +## [2.1.0] - 16/8/2018 +- Adds logging support to standard output with TLN_SetLogLevel() and TLN_LogLevel enum type. Defaults to none +- Internal changes to enable the logging feature. Object creation and type checking now concentrates much of the internal error handling +- Loaders destroy the used SimpleXmlParser instance, there was a memory leak +- Test.c now test for various runtime errors +- fixes segment violation in various linux builds + +## [2.0.0 LGPL] - 4/7/2018 +- Releases full source code under LGPL 2.1 license +- Adds context mechanism with TLN_Engine and associated companion functions to work with multiple instances of the engine using a global context. Backwards compatible with previous version +- Functionally equivalent to older 1.21 MIT-licensed version + +## [1.21.1] - 19/5/2018 +- Window title and CRT effect state is retained between fullscreen/windowed mode toggles. +- TLN_LoadBitmap() loader only loads 8 bpp png images and discards other formats with a "TLN_ERR_WRONG_FORMAT" error instead of crashing. +- TLN_LoadTileset() loader discards non-multiple of 8 pixel tiles with a "TLN_ERR_WRONG_SIZE" error. +- String texts are copied with bounded strncpy() instead of strcpy() to avoid possible buffer overflows. + +## [1.21.0] - 18/5/2018 +- Adds function TLN_SetSDLCallback() to register a user-provided function that gets called every time an event is generated in the underlying SDL2 layer. This function receives a pointer to a SDL_Event union. You must include SDL_event.h from your SDL2 sdk. This callback is very useful to bypass the built-in input handling in Tilengine, reacting to custom keys, gamepad, mouse, etc + +## [1.20] - 15/5/2018 +- Adds new background type: full bitmap layers. Enable with TLN_SetLayerBitmap() +- Provides binaries for Windows x86 and x64, other builds to come +- Updates Python and C# bindings + +## [1.19] - 3/12/2017 +- Spriteset: adds TLN_SetSpritesetData() to modify spriteset attributes and pixels at runtime. +- Window: adds CWF_NEAREST window flag to the TLN_WindowFlags enumeration for window creation. This flag upscales window with nearest-neighbor instead of default bilinear filter. +- Engine: adds TLN_SetFrameCallback() to get a user-provided function called when a frame is about to be drawn. Especially useful for multi-threaded window + +## [1.18] - 23/11/2017 +- Vastly improved user input: 6 action buttons, start button, up to four simultaneous players. Key bindings and joystick buttons can be redefined. Introduces TLN_EnableInput(), TLN_AssignInputJoystick(), TLN_DefineInputKey(), TLN_DefineInputButton() and new input names +- Sequences can be queried for info about name and number of frames. Introduces TLN_GetSequenceInfo() and associated TLN_SequenceInfo structure + +## [1.17] - 9/11/2017 +- Tilesets edited in Tiled editor support a new property: "priority". Setting it to "true", sets all occurrences of the tile in a tilemap with the priority flag, so they are rendered in front of regular sprites +- TLN_CreateTileset() functions takes an array of the new TLN_TileAttributes structure instead of the old array of integers that just accepted the tile type + +## [1.16] - 5/10/2017 +- Fixed TLN_LoadTilemap() when loading other layer than the first one in +a tmx with many layers +- Fixed TLN_GetSpriteInfo() that returned wrong sprite dimensions +- Sprite flag FLAG_PRIORITY implemented, so now objects can be placed in +four different "priority spaces + +## [1.14] - 2/6/2017 7:28:23 +- Loads Tileset from Tilemap and attaches a reference +- Loads background color attribute from Tilemap +- Loads SequencePack from Tileset with pattern animations and attaches a +reference +- Loads the type of tile in Tileset, via custom attribute: "type" +integer in range 0-255 +- New struture SequenceFrame to build Sequence objects at runtime, with +delay per-frame +- Deleted old TLN_Cycle object, unified its functionality to Sequence +object +- Extended TLN_TileInfo struct with new members to tell the type of a +tile and if a given cell is empty +- Added TLN_SetLayerMap() function to setup a layer only with a Tilemap +reference, it extracts and loads the attached Tileset automatically +- Added TLN_SetBGColorFromTilemap() function to set the background color +from a given Tilemap +- Added new functions to retrieve the new associations between objects +(Tileset -> Tilemap -> SequencePack), the dimensions of a layer in +pixels, the number of sequences in a SequencePack, and to query the +contained sequences by an index + +## [1.13] - 6/5/2017 +- New user defined pixel mapping transform for layers: +TLN_SetLayerPixelMapping() +- Fixed "shooter" example that crashed when boss was going to appear in +64 bit builds + +## [1.12] - 26/4/2017 +- Improved overall performance, especially with big tilesets, solid +areas and blending +- Reworked blending: deprecated "factor" parameter, added custom +blending function callback BLEND_CUSTOM and TLN_SetCustomBlendFunction +- Added mosaic effect for layers: TLN_SetLayerMosaic() +- Added CRT post-processing effect: TLN_EnableCRTEffect() +- Added palette manipulation functions: TLN_AddPaletteColor(), +TLN_SubPaletteColor, TLN_ModPaletteColor() +- The "Benchmark" sample now uses a real tsx/tmx pair instead of just +random noise + +## [1.11] - 13/4/2017 +- Implemented background layer clipping to a given rectangle: +TLN_SetLayerClip() and TLN_DisableLayerClip() + +## [1.10] - 28/12/2016 +- New TLN_GetErrorString(): returns string description about an error +- New TLN_SetLoadPath(): Graphic assets can now be located in their own +directory +- New TLN_SetTilesetAnimation(): implemented tileset animation in the +animation engine +- New modulation blending mode: BLEND_MUL +- TLN_TileInfo struct has new member ::color that returns color index at +the given location, useful for implementing pixel accurate collision +detection between sprite and tilemap +- Improved spriteset loader: can now read CSV files compatible with +online Leshy Spriteset Tool: +https://www.leshylabs.com/blog/posts/2013-12-03-Leshy_SpriteSheet_Tool.html +- Improved tilemap loader: can now accept NULL as layer name to read +the first layer in a multilayer tmx file +- Added SuperMarioClone sample that shows many of the new features + +## [1.7] - 15/2/2016 +- New active rendering without callbacks. See TLN_BeginFrame, +TLN_DrawNextScanline, TLN_BeginWindowFrame, TLN_EndWindowFrame + +## [1.6.5] - 29/1/2016 +- Fixed samples source code to match 1.6.5 API version +- New function TLN_Delay () to pause execution +- Affine transformed layers can now be combined with blending modes +- Adds native x64 Windows build + +## [1.6] - 13/1/2016 +- Per-pixel sprite collision detection +- Added two new functions: TLN_EnableSpriteCollision() and +TLN_GetSpriteCollision() to check collisions at pixel level + +## [1.5] - 9/1/2016 +- Runtime error information +- Adds TLN_GetLastError() and TLN_Error enumeration to check error causes at runtime + +## [1.4] - 6/9/2015 +- First public release diff --git a/build b/build new file mode 100755 index 00000000..61e9b846 --- /dev/null +++ b/build @@ -0,0 +1,5 @@ +#!/bin/bash +cd src +make +cd .. +sudo ./install diff --git a/docs/06_layers.md b/docs/06_layers.md deleted file mode 100644 index 0657f431..00000000 --- a/docs/06_layers.md +++ /dev/null @@ -1,251 +0,0 @@ -# Background layers - -[TOC] - -Background layers are one of the two main graphics entities in tilengine, the other are sprites. Background layers are a two dimensional arrangement of tiles, called tilemap, where each tile is a small square bitmap extracted from a bigger palette of tiles, called tileset, plus some flags that modify its appearance. Background layers can have transparent areas, where the underlying layer(s) or background color is seen. - -Layers are referenced by an index, starting at 0 for the topmost, increasing up to number of layers minus 1 for the bottom-most. - -## Basic setup -In order to get displayed, a layer needs to have attached three items: a tilemap, a tileset and a palette. Usually a tilemap has an internal reference to its associated tileset, and a tileset has a palette embedded, we only have to explicitly set the tilemap and the other items are loaded automatically. This is accomplished with the \ref TLN_SetLayer function. - -We have to load the tilemap first with \ref TLN_LoadTilemap (read more about [tilemaps](11_tilemaps.md). Then we call \ref TLN_SetLayer to attach it to the layer, passing the index layer, an optional tileset, and the tilemap to attach: -```c -TLN_Tilemap tilemap = TLN_LoadTilemap ("ruff_n_tumble.tmx"); -TLN_SetupLayer (0, NULL, tilemap); -``` -Layer with tilemap loaded: - -![Layer with tilemap loaded](img/layer_base.png) - -We can just pass a NULL to use the internal tileset referenced by the tilemap. But we can explicitly load and attach a tileset too with \ref TLN_LoadTileset : -```c -TLN_Tilemap tilemap = TLN_LoadTilemap ("ruff_n_tumble.tmx"); -TLN_Tileset tileset = TLN_LoadTileset ("ruff_n_tumble.tsx"); -TLN_SetupLayer (0, tileset, tilemap); -``` - -## Scrolling -Scrolling is the common term for moving the display area inside a bigger map. By default, when a layer is setup for the first time, its located at position 0,0 inside the tilemap. But it can be relocated with \ref TLN_SetLayerPosition . For example, to locate the layer 0 at position 120,16 (120 pixels to the right, 16 pixels down): -```c -TLN_SetLayerPosition (0,120,16); -``` -Layer moved to 120,16: - -![Layer moved to 120,16](img/layer_scroll.png) - -### Smooth scroll -Smooth scrolling consists in moving continuously a few pixels each time. This sample scrolls layer 0 to the right: -```c -int x = 0; -while (TLN_ProcessWindow()) -{ - TLN_SetLayerPosition (0, x,16); - TLN_DrawFrame (); - x += 1; -} -``` -![Smooth scroll](img/scroll.gif) - -### Parallax scroll -Parallax scrolling consists in scrolling at least two or more layers, where the background layer represents objects that are far away and move slowly, whereas the foreground layer has the objects that are nearer to to player and move faster. -```c -int x = 0; -while (TLN_ProcessWindow()) -{ - TLN_SetLayerPosition (0, x,0); /* move foreground layer two pixels per frame */ - TLN_SetLayerPosition (1, x/2,0); /* move background layer one pixel per frame */ - TLN_DrawFrame (); - x += 2; -} -``` - -## Setting the palette -By default, any layer uses the palette that came with the attached tileset, but it can be changed and use any other palette with \ref TLN_SetLayerPalette. We can explicitly load a palette with \ref TLN_LoadPalette : -```c -TLN_Palette palette = TLN_LoadPalette ("palette.act"); -TLN_SetLayerPalette (0, palette); -``` -Alternative palette: - -![Alternative palette](img/layer_palette.png) - -## Blending -Blending is supported in layers, with different modes and effects. To get extended information about the modes and their effects, please refer to [Chapter 09 - Blending](09_blending.md). - -To enable blending, call \ref TLN_SetLayerBlendMode passing the layer index and the blending mode. For example, to set 50%/50% blending in layer 0: -```c -TLN_SetLayerBlendMode (0, BLEND_MIX, 0); -``` -The last parameter, *factor*, is kept for compatibility but isn't used. - -To disable blending, call the same function with blending mode set to BLEND_NONE : -```c -TLN_SetLayerBlendMode (0, BLEND_NONE, 0); -``` - -## Clipping -Each layer can be assigned a clipping rectangle: a region that delimits where drawing occurs inside the window, leaving outside pixels untouched as if the layer were disabled. By default the clipping rectangle is disabled and the layer covers the entire window. - -To enable the clipping rectangle, call \ref TLN_SetLayerClip passing the index of the layer, and four numbers telling the x,y of the top-left corner, and the x,y of the bottom-right corner. For example, to set clipping in layer 0 from 32,20 to 360,240: -```c -TLN_SetLayerClip (0, 32,20, 360,240); -``` -Clipping rectangle 32,20 - 360,240: - -![Clipping rectangle](img/layer_clip.png) - -To disable the clipping rectangle, call \ref TLN_DisableLayerClip passing the layer index to disable: -```c -TLN_DisableLayerClip (0); -``` - -## Column offset -This feature allows displacing each column of tiles in screen space by a given amount of pixels. This can be used to fake vertical parallaxing, or to do moderate tilting and deformation of terrain. - -To setup the effect, it needs an array of integers as large as the number of columns that fit in one screen plus 2. For example, if you've setup a 400x240 framebuffer and are using 8x8 tiles, the number of positions is 400/8 + 2 = 52. Then call \ref TLN_SetLayerColumnOffset passing the layer index and a pointer to the array of integers: -```c -const int hres = 400; -const int tile = 8; -const int size = hres/tile + 2; -int offsets[size] = {0}; -/* ... */ -TLN_SetLayerColumnOffset (0, offsets); -``` -Now the layer 0 column offset is linked to the *offsets* array. Setting any value(s) inside the array and drawing a frame has immediate visible effects, there's no need to call the function each time. For example, to create a slightly sloped terrain: -```c -int c; -for (c=0; c index = y * width + x - -Then we set `dx` and `dy` with the coordinates of the desired source pixel: -```c -int x = 320; /* pixel we want to remap */ -int y = 240; -int index = y * width + x; -/* ... */ -pixel_map[index].dx = 200; -pixel_map[index].dy = 100; -``` - -## Disabling transformations -To disable any of the three previous transformation modes and return the layer to standard mode, call the \ref TLN_ResetLayerMode passing the layer index: -```c -TLN_ResetLayerMode (0); -``` - -## Mosaic effect -The mosaic effect pixelates the layer, making some pixels bigger and skipping others so the relative image size keeps constant. It's similar to the mosaic effect in SNES, but more flexible. Different horizontal and vertical pixel values are possible -not just square pixels-, and any size can be set, not just powers of 2. To enable the effect, call \ref TLN_SetLayerMosaic passing the layer index, the horizontal pixel size, and the vertical pixel size. For example to set mosaic on layer 0 with 8 pixel horizontal factor and 6 pixel vertical factor: -```c -TLN_SetLayerMosaic (0, 8,6); -``` -Mosaic effect with 8 horizontal and 6 vertical pixel size factor: - -![Affine transform](img/layer_mosaic.png) - -To disable the mosaic effect, just call \ref TLN_DisableLayerMosaic passing the layer index: -```c -TLN_DisableLayerMosaic (0); -``` - -## Getting layer data -Sometimes it's useful to get info about the layer: width and height in pixels -which depends on its tileset and tilemap, its palette, and detailed data about a specific tile: -* Use \ref TLN_GetLayerWidth and \ref TLN_GetLayerHeight to get size in pixels -* Use \ref TLN_GetLayerPalette to get the active palette - -A basic feature in any game with backgrounds and sprites is the interaction with the background. Tilengine provides the \ref TLN_TileInfo structure and the function TLN_GetLayerTile to query a tile in map coordinates. \ref TLN_TileInfo returns the row and col of the tile inside of the tilemap, the pixel offset inside the tile (distance from the top-left corner of the tile to the requested pixel), the tile index, its type (type of terrain, etc) and color index. Let's say we want to know about tile located at map coordinates 1800,300: -```c -TLN_TileInfo tile_info; /* declare struct to hold data */ -TLN_GetLayerTile (0, 1800,300, &tile_info); /* get layer 0 tile at 1800,300 */ -``` - -## Disabling -To disable a layer so it is not rendered, just call \ref TLN_DisableLayer passing the layer index: -```c -TLN_DisableLayer (0); -``` - -## Summary -This is a quick reference of related functions in this chapter: - -|Function | Quick description -|--------------------------------|------------------------------------- -|\ref TLN_SetLayer |Configures a tiled background layer -|\ref TLN_SetLayerBitmap |Configures a full-bitmap background layer -|\ref TLN_SetLayerPalette |Sets the color palette to the layer -|\ref TLN_SetLayerPosition |Moves the viewport inside the layer -|\ref TLN_SetLayerScaling |Enables layer scaling -|\ref TLN_SetLayerTransform |Sets affine transform matrix to enable rotating and scaling -|\ref TLN_SetLayerPixelMapping |Sets the table for pixel mapping render mode -|\ref TLN_ResetLayerMode |Disables scaling or affine transform for the layer -|\ref TLN_SetLayerBlendMode |Sets the blending mode (transparency effect) -|\ref TLN_SetLayerColumnOffset |Enables column offset mode for this layer -|\ref TLN_SetLayerClip |Enables clipping rectangle -|\ref TLN_DisableLayerClip |Disables clipping rectangle -|\ref TLN_SetLayerMosaic |Enables mosaic effect (pixelation) -|\ref TLN_DisableLayerMosaic |Disables mosaic effect -|\ref TLN_DisableLayer |Disables the specified layer so it is not drawn -|\ref TLN_GetLayerPalette |Returns the current palette of a layer -|\ref TLN_GetLayerTile |Gets info about the tile located in tilemap space -|\ref TLN_GetLayerWidth |Returns the layer width in pixels -|\ref TLN_GetLayerHeight |Returns the layer height in pixels \ No newline at end of file diff --git a/docs/07_sprites.md b/docs/07_sprites.md deleted file mode 100644 index a20d48b5..00000000 --- a/docs/07_sprites.md +++ /dev/null @@ -1,112 +0,0 @@ -# Sprites - -[TOC] - -A sprite is any moving object in a videogame that is not a background: a character, enemy, bullet,player, ball, special effect... - -## Basic setup -A sprite needs at least a [spriteset](12_spritesets.md), and optionally a set of [flags](\ref TLN_TileFlags) and a [palette](\ref TLN_Palette). Usually you want to pack together all the animation frames of a single character inside the same spriteset, so to change the displayed graphic it's only needed to change the image index. - -We have to load the spriteset first with \ref TLN_LoadSpriteset (read more about [spritesets](12_spritesets.md)). Then we call \ref TLN_SetSpriteSet to attach it to the sprite, passing the sprite index and the reference to the spriteset: -```c -TLN_Spriteset spriteset = TLN_LoadSpriteset ("FireLeo"); -TLN_SetSpriteSet (0, spriteset); -``` -Now the sprite is displayed at position 0,0 by default (top-left corner) with the first graphic inside the spriteset. To change the graphic show, call \ref TLN_SetSpritePicture passing the sprite index and the graphic index inside the spriteset, starting with 0. For example to set sprite 0 with the 4th graphic inside the spriteset: -```c -TLN_SetSpritePicture (0, 3); -``` -**NOTE**: each graphic inside a spriteset can have a unique name, and there is a convenient way for retrieving the graphic index based on its name: \ref TLN_FindSpritesetSprite - -## Moving around -To move the sprite to a different location, call the \ref TLN_SetSpritePosition, passing the sprite index, and the x,y coordinates. These values are absolute screen coordinates. For example to move sprite 3 to 160,120: -```c -TLN_SetSpritePosition (3, 160,120); -``` -## Setting attributes -There are some special modifiers that control sprite flipping and priority. Sprite flipping allows you to draw a sprite upside down and/or horizontally mirrored. For example in a platformer game you just need to draw sprites facing to the right, when you want to draw them facing left, just set the horizontal flipping flag. *Priority* determines the final composition (which elements are drawn in front of others). To set attributes, call \ref TLN_SetSpriteFlags passing the sprite index and a combination of \ref TLN_TileFlags. For example to draw sprite 0 upside down: -```c -TLN_SetSpriteFlags (0, FLAG_FLIPY) -``` -Flipping modes: a) 0, b) FLAG_FLIPX, c) FLAG_FLIPY, d) FLAG_FLIPX + FLAG_FLIPY: - -![Flipping modes](img/sprite_flags.png) - -## Setting the palette -By default, a sprite is assigned the associated palette of its spriteset, but this can be changed calling \ref TLN_SetSpritePalette passing the sprite index and a \ref TLN_Palette reference: -```c -TLN_Palette palette = TLN_LoadPalette ("palette.act") -TLN_SetSpritePalette (0, palette); -``` - -## Blending -Blending is supported in sprites, with different modes and effects. To get extended information about the modes and their effects, please refer to [Chapter 09 - Blending](09_blending.md). - -To enable blending, call \ref TLN_SetSpriteBlendMode passing the sprite index and the blending mode. For example, to set 50%/50% blending in sprite 0: -```c -TLN_SetSpriteBlendMode (0, BLEND_MIX, 0); -``` -The last parameter, *factor*, is kept for compatibility but isn't used. - -To disable blending, call the same function with blending mode set to BLEND_NONE : -```c -TLN_SetSpriteBlendMode (0, BLEND_NONE, 0); -``` - -## Scaling -Sprites can be drawn upscaled or downscaled with an arbitrary factor. To enable scaling, call \ref TLN_SetSpriteScaling passing the sprite index and two floating point values with the horizontal and vertical factor, respectively. Values greater than 1.0 upscale, and smaller than 1.0 downscale. For example to set an horizontal downscaling of 0.5 and vertical upscaling of 1.5 for layer 0: -```c -TLN_SetSpriteScaling (0, 0.5f, 1.5f); -``` -Different sprite scaling examples: - -![Different sprite scaling examples](img/sprite_scaling.png) - -To disable scaling, call \ref TLN_ResetSpriteScaling passing the sprite index. For example to disable scaling in sprite 0: -```c -TLN_ResetSpriteScaling (0); -``` - -## Collision detection -A basic action on any game is checking if two given sprites collide. For example, if our hero is hit by any enemy bullet. A quick way to determine a collision is to check if their bounding boxes overlap (a *bounding box* is the rectangular area that fully encloses a sprite). This methos is fast and easy to implement, but sometimes the bounding boxes of two sprites can overlap, but in regions where there aren't solid pixels, just transparent ones. In this case, you see that the bullet isn't going to hit your hero, but it gets actually hit without touching it. A common solution is to use bounding boxes that are *smaller* than the sprite, but this can have the opposite effect: missing collisions that actually happen. - -To solve this, tilengine implements pixel-based collision detection. With this feature enabled, you know that a sprite gets involved in a collision only if there are actual non-transparent pixels in both sprites overlapping. But this methos also has a limitation: it can tell you that a given sprite is per-pixel colliding with another sprite, but you don't know with *which* sprite. - -The final solution consists in combining both methods as they compliment each other: first determine coarse collision with bounding boxes, and then check per-pixel collision detection in those sprites. - -Per-pixel collision detection requires more CPU cycles that regular sprites, so it's an optional feature that is disabled by default. You can enable it for each sprite calling \ref TLN_EnableSpriteCollision passing the sprite index and a boolean value with *true* to enable or *false* to disable the feature. For example, to enable collision detection for sprite 0 and disable it for sprite 3: -```c -TLN_EnableSpriteCollision (0, true); -TLN_EnableSpriteCollision (3, false); -``` -To query the collision status for a given sprite, call \ref TLN_GetSpriteCollision passing the sprite index to check. It returns a boolean value indicating the collision status: -```c -bool collision = TLN_GetSpriteCollision (0); -``` - -## Disabling -To disable a sprite so it is not rendered, just call \ref TLN_DisableSprite passing the sprite index: -```c -TLN_DisableSprite (0); -``` - -## Summary -This is a quick reference of related functions in this chapter: - -|Function | Quick description -|--------------------------------|------------------------------------- -|\ref TLN_ConfigSprite |Configures a sprite, setting spriteset and flags at once -|\ref TLN_SetSpriteSet |Assigns the spriteset and its palette to a given sprite -|\ref TLN_SetSpriteFlags |Sets flags for a given sprite -|\ref TLN_SetSpritePosition |Sets the sprite position inside the viewport -|\ref TLN_SetSpritePicture |Sets the actual graphic to the sprite -|\ref TLN_SetSpritePalette |Assigns a palette to a sprite -|\ref TLN_SetSpriteBlendMode |Sets the blending mode (transparency effect) -|\ref TLN_SetSpriteScaling |Sets the scaling factor of the sprite -|\ref TLN_ResetSpriteScaling |Disables scaling for a given sprite -|\ref TLN_GetSpritePicture |Returns the index of the assigned picture from the spriteset -|\ref TLN_GetAvailableSprite |Returns the first available (unused) sprite -|\ref TLN_EnableSpriteCollision |Enable sprite collision checking at pixel level -|\ref TLN_GetSpriteCollision |Gets the collision status of a given sprite -|\ref TLN_DisableSprite |Disables the sprite so it is not drawn -|\ref TLN_GetSpritePalette |Returns the current palette of a sprite \ No newline at end of file diff --git a/docs/12_spritesets.md b/docs/12_spritesets.md deleted file mode 100644 index f7158fdc..00000000 --- a/docs/12_spritesets.md +++ /dev/null @@ -1,14 +0,0 @@ -# Spritesets - -[TOC] - -## Load from file - -## Create at runtime - -## Getting sprite info - -## Delete - -## Summary -This is a quick reference of related functions in this chapter: \ No newline at end of file diff --git a/docs/17_reference.md b/docs/17_reference.md deleted file mode 100644 index eab833f4..00000000 --- a/docs/17_reference.md +++ /dev/null @@ -1,37 +0,0 @@ -# API reference overview - -[TOC] - -Tilengine uses eight types of elements, arranged in two groups: -* **Static items** are reserved at startup and cannot be modified at runtime. These elements are layers (the backgrounds), sprites(the objects) and the animations. They are referenced by an index, starting at 0 for the first element up to num_elements - 1. They represent the "hardware" capabilities of Tilengine. Each one of these elements can be enabled or disabled as they are needed, but cannot be created or destroyed. -* **Dynamic assets** can be freely loaded, cloned and deleted at runtime. They are referenced by a pointer, and represent the "assets" that are assigned to the static elements - -## Setup & run -This section shows the basic functions to initialise, manage and query Tilengine. Read at least the functions \ref TLN_Init, \ref TLN_SetRenderTarget and \ref TLN_UpdateFrame - -## Windowing & user input -Tilengine is designed to act as a back-end renderer for other gaming environments, but it has a built-in windowing system for easy setup and testing without having to rely on user provided components. Read at lest the functions \ref TLN_CreateWindow, \ref TLN_ProcessWindow, \ref TLN_DrawFrame and \ref TLN_GetInput (for user input) - -## Background layers -Layers represent the scrolling planes (or backgrounds) composed of square tiles. A layer must be assigned a tileset, a tilemap and optionally a palette. Read at least the functions \ref TLN_SetLayer and \ref TLN_SetLayerPosition - -## Sprites -Sprites represent the movable objects that are not part of the backgrounds and where the gameplay occurs. A sprite must be assigned a spriteset and optionally a palette. Read at least the functions \ref TLN_ConfigSprite, \ref TLN_SetSpritePicture and \ref TLN_SetSpritePosition. - -## Animations -Animations represent the built-in sequencers that play sequences. An animation must be assigned a sequence and one of the following: a layer (for tileset animation), a sprite (for sprite animation) or a palette (for color animation). Read at least the functions \ref TLN_SetSpriteAnimation, \ref TLN_SetTilemapAnimation and \ref TLN_SetPaletteAnimation. - -## Tilesets -Tilesets are the building blocks of the backgrounds, the individual tiles themselves packed together. They are loaded at runtime from Tiled *tsx* files with the \ref TLN_LoadTileset function. - -## Tilemaps -Tilemaps are a gird layout of tiles (from a tileset) that form a background. They are loaded at runtime from Tiled *tmx* files with the \ref TLN_LoadTilemap function. - -## Spritesets -Spritesets are collections of related individual images for sprites grouped together inside a big picture, for example all the animation frames of the main character. They are loaded at runtime from *png/txt* pairs with the \ref TLN_LoadSpriteset function. - -## Palettes -Palettes are the color tables used by the graphics in layers and sprites. They can be loaded at runtime from standard adobe *.act* files with the function \ref TLN_LoadPalette, retrieved from tilesets with \ref TLN_GetTilesetPalette or from spritesets with \ref TLN_GetSpritesetPalette. - -## Sequences -A sequence is a serie of frames played in succession at a given speed by an animation. They can be loaded at runtime from *.sqx* files (xml) with the \ref TLN_LoadSequencePack function. \ No newline at end of file diff --git a/docs/Doxyfile b/docs/Doxyfile index 21ae3dd2..dca3a890 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,7 +1,7 @@ -# Doxyfile 1.8.16 +# Doxyfile 1.14.0 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. +# Doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. @@ -12,6 +12,16 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use Doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use Doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -38,13 +48,13 @@ PROJECT_NAME = Tilengine # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = 2.15.4 # Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a +# for a project that appears at the top of each page and should give viewers a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = Tilengine- The 2D retro graphics engine with raster effects +PROJECT_BRIEF = "Tilengine - the 2D retro graphics engine with raster effects" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -53,24 +63,42 @@ PROJECT_BRIEF = Tilengine- The 2D retro graphics engine with raster eff PROJECT_LOGO = logo.png +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If +# entered, it will be relative to the location where Doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding Doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise cause +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. @@ -79,36 +107,28 @@ CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this +# documentation generated by Doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -129,13 +149,13 @@ REPEAT_BRIEF = YES ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief +# Doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. @@ -143,7 +163,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -153,11 +173,11 @@ FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to +# If left blank the directory from which Doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. +# will be relative from the directory where Doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = @@ -171,41 +191,42 @@ STRIP_FROM_PATH = STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't +# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but +# less readable) file names. This can be useful if your file system doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the +# first line (until the first dot, question mark or exclamation mark) of a +# Javadoc-style comment as the brief description. If set to NO, the Javadoc- +# style will behave just like regular Qt-style comments (thus requiring an +# explicit @brief command for a brief description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO -# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be -# interpreted by doxygen. +# interpreted by Doxygen. # The default value is: NO. JAVADOC_BANNER = NO -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first +# line (until the first dot, question mark or exclamation mark) of a Qt-style +# comment as the brief description. If set to NO, the Qt-style will behave just +# like regular Qt-style comments (thus requiring an explicit \brief command for +# a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this @@ -217,13 +238,21 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and Doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# Doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as Doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. @@ -240,16 +269,16 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = "thread_safety=@par Thread safety\n" \ "pointer_lifetime=@par Pointer lifetime\n" \ @@ -262,12 +291,6 @@ ALIASES = "thread_safety=@par Thread safety\n" \ "macos=__macOS:__" \ "linux=__Linux:__" -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -308,27 +331,30 @@ OPTIMIZE_OUTPUT_SLICE = NO # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is -# Fortran), use: inc=Fortran f=C. +# default for Fortran type files). For instance to make Doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by Doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# The output of markdown processing is further processed by Doxygen, so you can +# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. @@ -338,25 +364,45 @@ MARKDOWN_SUPPORT = YES # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. +# Minimum value: 0, maximum value: 99, default value: 6. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 3 -# When enabled doxygen tries to link words that correspond to documented +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled Doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. +# globally by setting AUTOLINK_SUPPORT to NO. Words listed in the +# AUTOLINK_IGNORE_WORDS tag are excluded from automatic linking. # The default value is: YES. AUTOLINK_SUPPORT = YES +# This tag specifies a list of words that, when matching the start of a word in +# the documentation, will suppress auto links generation, if it is enabled via +# AUTOLINK_SUPPORT. This list does not affect links explicitly created using \# +# or the \link or commands. +# This tag requires that the tag AUTOLINK_SUPPORT is set to YES. + +AUTOLINK_IGNORE_WORDS = + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and +# tag to YES in order to let Doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO @@ -368,16 +414,16 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. +# Doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. @@ -386,7 +432,7 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first +# tag is set to YES then Doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. @@ -444,21 +490,42 @@ TYPEDEF_HIDES_STRUCT = YES # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The +# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# Doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest +# symbols. At the end of a run Doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use +# during processing. When set to 0 Doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -517,7 +584,14 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. @@ -525,22 +599,31 @@ EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = YES -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = YES -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# If the HIDE_UNDOC_NAMESPACES tag is set to YES, Doxygen will hide all +# undocumented namespaces that are normally visible in the namespace hierarchy. +# If set to NO, these namespaces will be included in the various overviews. This +# option has no effect if EXTRACT_ALL is enabled. +# The default value is: YES. + +HIDE_UNDOC_NAMESPACES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -554,30 +637,44 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) ands Mac users are advised to set this option to NO. -# The default value is: system dependent. +# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and macOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = NO -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -590,7 +687,7 @@ SHOW_INCLUDE_FILES = NO SHOW_GROUPED_MEMB_INC = NO -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. @@ -602,14 +699,14 @@ FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = NO -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. @@ -617,7 +714,7 @@ SORT_MEMBER_DOCS = NO SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. @@ -629,7 +726,7 @@ SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. @@ -646,11 +743,11 @@ SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. @@ -720,24 +817,25 @@ SHOW_FILES = YES SHOW_NAMESPACES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from +# Doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file +# by Doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated +# by Doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can +# that represents Doxygen's defaults, run Doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# Note that if you run Doxygen from a directory containing a file called +# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = @@ -752,19 +850,35 @@ LAYOUT_FILE = CITE_BIB_FILES = +# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH +# environment variable) so that external tools such as latex and gs can be +# found. +# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the +# path already specified by the PATH variable, and are added in the order +# specified. +# Note: This option is particularly useful for macOS version 14 (Sonoma) and +# higher, when running Doxygen from Doxywizard, because in this case any user- +# defined changes to the PATH are ignored. A typical example on macOS is to set +# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin +# together with the standard path, the full search path used by doxygen when +# launching external tools will then become +# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +EXTERNAL_TOOL_PATH = + #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the +# standard output by Doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -772,49 +886,97 @@ QUIET = NO WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete +# function parameter documentation. If set to NO, Doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, Doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = NO -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about +# undocumented enumeration values. If set to NO, Doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If WARN_LAYOUT_FILE option is set to YES, Doxygen will warn about issues found +# while parsing the user defined layout file, such as missing or wrong elements. +# See also LAYOUT_FILE for details. If set to NO, problems with the layout file +# will be suppressed. +# The default value is: YES. + +WARN_LAYOUT_FILE = YES + +# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the Doxygen process Doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. # The default value is: NO. WARN_AS_ERROR = NO -# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# The WARN_FORMAT tag determines the format of the warning messages that Doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of Doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -829,31 +991,45 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = ../include/Tilengine.h \ - ../src \ - . + . # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses. The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. +# read by Doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. # -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be +# provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.md @@ -868,12 +1044,10 @@ RECURSIVE = NO # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # -# Note that relative paths are relative to the directory from which doxygen is +# Note that relative paths are relative to the directory from which Doxygen is # run. EXCLUDE = ..\simplexml.c \ - ..\strdup.c \ - ..\GaussianBlur.c \ ..\aes.c \ ..\Base64.c \ ..\Test.c @@ -898,14 +1072,14 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = TLNAPI \ engine \ - WndParams + WndParams \ + TLN_SetLayerParent \ + TLN_DisableLayerParent \ + TLN_DisableAnimation # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -933,7 +1107,7 @@ EXAMPLE_RECURSIVE = NO IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should +# The INPUT_FILTER tag can be used to specify a program that Doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # @@ -948,9 +1122,14 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # +# Note that Doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. +# properly processed by Doxygen. INPUT_FILTER = @@ -963,7 +1142,7 @@ INPUT_FILTER = # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. +# properly processed by Doxygen. FILTER_PATTERNS = @@ -985,9 +1164,27 @@ FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. +# and want to reuse the introduction page also for the Doxygen output. + +USE_MDFILE_AS_MAINPAGE = introduction.md + +# If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- +# directories of the project's root, is used as the documentation for that sub- +# directory, except when the README.md starts with a \dir, \page or \mainpage +# command. If set to NO, the README.md file needs to start with an explicit \dir +# command in order to be used as directory documentation. +# The default value is: YES. + +IMPLICIT_DIR_DOCS = YES + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. -USE_MDFILE_AS_MAINPAGE = 01_introduction.md +FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1003,12 +1200,13 @@ USE_MDFILE_AS_MAINPAGE = 01_introduction.md SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. +# multi-line macros, enums or list initialized variables directly into the +# documentation. # The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. @@ -1046,7 +1244,7 @@ REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# point to the HTML generated by the htags(1) tool instead of Doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. @@ -1060,14 +1258,14 @@ SOURCE_TOOLTIPS = YES # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # -# The result: instead of the source browser generated by doxygen, the links to +# The result: instead of the source browser generated by Doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. @@ -1075,31 +1273,42 @@ USE_HTAGS = NO VERBATIM_HEADERS = NO -# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. -# Note: The availability of this option depends on whether or not doxygen was +# If the CLANG_ASSISTED_PARSING tag is set to YES then Doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which Doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not Doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then Doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories +# the include paths will already be set by Doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the -# path to the compilation database (see: -# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files -# were built. This is equivalent to specifying the "-p" option to a clang tool, -# such as clang-check. These options will then be passed to the parser. -# Note: The availability of this option depends on whether or not doxygen was +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not Doxygen was # generated with the -Duse_libclang=ON option for CMake. CLANG_DATABASE_PATH = @@ -1115,17 +1324,11 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = TLN_ @@ -1134,7 +1337,7 @@ IGNORE_PREFIX = TLN_ # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1155,40 +1358,40 @@ HTML_OUTPUT = html HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a +# each generated HTML page. If the tag is left blank Doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. +# that Doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally +# for information on how to generate the default header that Doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description +# default header when upgrading to a newer version of Doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = header.html # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard +# generated HTML page. If the tag is left blank Doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. +# that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. +# the HTML output. If left blank Doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. +# sheet that Doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. @@ -1198,13 +1401,18 @@ HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. +# created by Doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = "customdoxygen.css" @@ -1219,9 +1427,22 @@ HTML_EXTRA_STYLESHEET = "customdoxygen.css" HTML_EXTRA_FILES = +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1231,7 +1452,7 @@ HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1249,20 +1470,11 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will +# are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, +# page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1277,6 +1489,33 @@ HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to @@ -1292,10 +1531,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, Doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1312,6 +1552,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1334,14 +1581,18 @@ DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline (the HTML help workshop was already many +# years in maintenance mode). You can download the HTML help workshop from the +# web archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for @@ -1361,14 +1612,14 @@ CHM_FILE = Tilengine.chm # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. +# Doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = "C:\Program Files (x86)\HTML Help Workshop\hhc.exe" # The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1395,6 +1646,16 @@ BINARY_TOC = NO TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help @@ -1413,7 +1674,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1421,8 +1683,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1430,16 +1692,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1451,9 +1713,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1496,18 +1758,39 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by Doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has more details information than the tab index, you +# could consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO +# When GENERATE_TREEVIEW is set to YES, the PAGE_OUTLINE_PANEL option determines +# if an additional navigation panel is shown at the right hand side of the +# screen, displaying an outline of the contents of the main page, similar to +# e.g. https://developer.android.com/reference If GENERATE_TREEVIEW is set to +# NO, this option has no effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +PAGE_OUTLINE_PANEL = YES + +# When GENERATE_TREEVIEW is set to YES, the FULL_SIDEBAR option determines if +# the side bar is limited to only the treeview area (value NO) or if it should +# extend to the full height of the window (value YES). Setting this to YES gives +# a layout similar to e.g. https://docs.readthedocs.io with more room for +# contents, but less room for the project logo, title, and description. If +# GENERATE_TREEVIEW is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. +# Doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. @@ -1516,6 +1799,12 @@ GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 +# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified +# enumeration values besides the enumeration mnemonics. +# The default value is: NO. + +SHOW_ENUM_VALUES = NO + # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. @@ -1523,35 +1812,48 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 300 -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML +# Doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. -FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1561,11 +1863,29 @@ FORMULA_TRANSPARENT = YES USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1578,33 +1898,40 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and +# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for +# the HTML output. The underlying search engine uses JavaScript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then +# For large projects the JavaScript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically @@ -1621,9 +1948,9 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH -# setting. When disabled, doxygen will generate a PHP script for searching and +# setting. When disabled, Doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing # and searching needs to be provided by external tools. See the section # "External Indexing and Searching" for details. @@ -1632,7 +1959,7 @@ SEARCHENGINE = YES SERVER_BASED_SEARCH = NO -# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# When EXTERNAL_SEARCH tag is enabled Doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain the @@ -1640,7 +1967,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1653,8 +1981,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1675,7 +2004,7 @@ SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through Doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of @@ -1689,7 +2018,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, Doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -1734,7 +2063,7 @@ MAKEINDEX_CMD_NAME = makeindex LATEX_MAKEINDEX_CMD = makeindex -# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# If the COMPACT_LATEX tag is set to YES, Doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1763,36 +2092,38 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank Doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that Doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of Doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank Doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that Doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created -# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# by Doxygen. Using this option one can overrule certain style aspects. Doxygen # will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the @@ -1818,56 +2149,46 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, Doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error. +# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch +# mode nothing is printed on the terminal, errors are scrolled as if is +# hit at every error; missing files that TeX tries to input or request from +# keyboard input (\read on a not open input stream) cause the job to abort, +# NON_STOP In nonstop mode the diagnostic message will appear on the terminal, +# but there is no possibility of user interaction just like in batch mode, +# SCROLL In scroll mode, TeX will stop only for missing files to input or if +# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at +# each error, asking for user intervention. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BATCHMODE = NO -# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# If the LATEX_HIDE_INDICES tag is set to YES then Doxygen will not include the # index chapters (such as File Index, Compound Index, etc.) in the output. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. -# The default value is: plain. +# The default value is: plainnat. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the @@ -1880,7 +2201,7 @@ LATEX_EMOJI_DIRECTORY = # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, Doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1895,7 +2216,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, Doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1915,38 +2236,36 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's +# Load stylesheet definitions from file. Syntax is similar to Doxygen's # configuration file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the -# default style sheet that doxygen normally uses. +# default style sheet that Doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's configuration file. A template extensions file can be +# similar to Doxygen's configuration file. A template extensions file can be # generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. +# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the RTF_OUTPUT output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_SOURCE_CODE = NO +RTF_EXTRA_FILES = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, Doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -1977,7 +2296,7 @@ MAN_EXTENSION = .3 MAN_SUBDIR = -# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, then it # will generate one additional man file for each entity documented in the real # man page(s). These additional files only source the real man page, but without # them the man command would be unable to find the correct page. @@ -1990,7 +2309,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, Doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -2004,7 +2323,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, Doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -2013,7 +2332,7 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES -# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include # namespace members in file scope as well, matching the HTML output. # The default value is: NO. # This tag requires that the tag GENERATE_XML is set to YES. @@ -2024,7 +2343,7 @@ XML_NS_MEMB_FILE_SCOPE = NO # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, Doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -2038,32 +2357,49 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# If the GENERATE_AUTOGEN_DEF tag is set to YES, Doxygen will generate an +# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures # the structure of the code including all documentation. Note that this feature # is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + +# If the GENERATE_SQLITE3 tag is set to YES Doxygen will generate a Sqlite3 +# database with symbols found by Doxygen stored in tables. +# The default value is: NO. + +GENERATE_SQLITE3 = NO + +# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be +# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put +# in front of it. +# The default directory is: sqlite3. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_OUTPUT = sqlite3 + +# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db +# database file will be recreated with each Doxygen run. If set to NO, Doxygen +# will warn if a database file is already found and not modify it. +# The default value is: YES. +# This tag requires that the tag GENERATE_SQLITE3 is set to YES. + +SQLITE3_RECREATE_DB = YES + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, Doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -2071,7 +2407,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, Doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -2101,13 +2437,13 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, Doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# If the MACRO_EXPANSION tag is set to YES, Doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. @@ -2133,7 +2469,8 @@ SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the -# preprocessor. +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = @@ -2165,7 +2502,7 @@ PREDEFINED = TLNAPI EXPAND_AS_DEFINED = -# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# If the SKIP_FUNCTION_MACROS tag is set to YES then Doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have # an all uppercase name, and do not end with a semicolon. Such function macros # are typically used for boiler-plate code, and will confuse the parser if not @@ -2189,26 +2526,26 @@ SKIP_FUNCTION_MACROS = YES # section "Linking to external documentation" for more information about the use # of tag files. # Note: Each tag file must have a unique name (where the name does NOT include -# the path). If a tag file is not located in the directory in which doxygen is +# the path). If a tag file is not located in the directory in which Doxygen is # run, you must also specify the path to the tagfile here. TAGFILES = -# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# When a file name is specified after GENERATE_TAGFILE, Doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. +# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces +# will be listed in the class and namespace index. If set to NO, only the +# inherited external classes will be listed. # The default value is: NO. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be +# in the topic index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. @@ -2222,42 +2559,26 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to diagram generator tools #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. HIDE_UNDOC_RELATIONS = YES -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# If you set the HAVE_DOT tag to YES then Doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO # The default value is: NO. HAVE_DOT = NO -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed -# to run in parallel. When set to 0 doxygen will base this on the number of +# The DOT_NUM_THREADS specifies the number of dot invocations Doxygen is allowed +# to run in parallel. When set to 0 Doxygen will base this on the number of # processors available in the system. You can set it explicitly to a value # larger than 0 to get control over the balance between CPU load and processing # speed. @@ -2266,55 +2587,83 @@ HAVE_DOT = NO DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. +# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of +# subgraphs. When you want a differently looking font in the dot files that +# Doxygen generates you can specify fontname, fontcolor and fontsize attributes. +# For details please see Node, +# Edge and Graph Attributes specification You need to make sure dot is able +# to find the font, which can be done by putting it in a standard location or by +# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. Default graphviz fontsize is 14. +# The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTNAME = Helvetica +DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. +# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can +# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. Complete documentation about +# arrows shapes. +# The default value is: labelfontname=Helvetica,labelfontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTSIZE = 10 +DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. +# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes +# around nodes set 'shape=plain' or 'shape=plaintext' Shapes specification +# The default value is: shape=box,height=0.2,width=0.4. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" + +# You can set the path where dot can find font specified with fontname in +# DOT_COMMON_ATTR and others dot attributes. # This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will +# generate a graph for each documented class showing the direct and indirect +# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and +# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case +# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the +# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used. +# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance +# relations will be shown as texts / links. Explicit enabling an inheritance +# graph or choosing a different representation for an inheritance graph of a +# specific class, can be accomplished by means of the command \inheritancegraph. +# Disabling an inheritance graph can be accomplished by means of the command +# \hideinheritancegraph. +# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES -# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a # graph for each documented class showing the direct and indirect implementation # dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. +# class with other documented classes. Explicit enabling a collaboration graph, +# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the +# command \collaborationgraph. Disabling a collaboration graph can be +# accomplished by means of the command \hidecollaborationgraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES -# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. +# If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for +# groups, showing the direct groups dependencies. Explicit enabling a group +# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means +# of the command \groupgraph. Disabling a directory graph can be accomplished by +# means of the command \hidegroupgraph. See also the chapter Grouping in the +# manual. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2331,10 +2680,41 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the UML_LOOK tag is enabled, field labels are shown along the edge between +# two class nodes. If there are many fields and many nodes the graph may become +# too cluttered. The UML_MAX_EDGE_LABELS threshold limits the number of items to +# make the size more manageable. Set this to 0 for no limit. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag UML_LOOK is set to YES. + +UML_MAX_EDGE_LABELS = 10 + +# If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, Doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will be wrapped across multiple lines. Some heuristics are +# applied to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2344,24 +2724,29 @@ UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to -# YES then doxygen will generate a graph for each documented file showing the +# YES then Doxygen will generate a graph for each documented file showing the # direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO, +# can be accomplished by means of the command \includegraph. Disabling an +# include graph can be accomplished by means of the command \hideincludegraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDE_GRAPH = YES # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are -# set to YES then doxygen will generate a graph for each documented file showing +# set to YES then Doxygen will generate a graph for each documented file showing # the direct and indirect include dependencies of the file with other documented -# files. +# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set +# to NO, can be accomplished by means of the command \includedbygraph. Disabling +# an included by graph can be accomplished by means of the command +# \hideincludedbygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. INCLUDED_BY_GRAPH = YES -# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# If the CALL_GRAPH tag is set to YES then Doxygen will generate a call # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. @@ -2373,7 +2758,7 @@ INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO -# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller # dependency graph for every global function or class method. # # Note that enabling this option will significantly increase the time of a run. @@ -2385,44 +2770,59 @@ CALL_GRAPH = NO CALLER_GRAPH = NO -# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical # hierarchy of all classes instead of a textual one. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = YES -# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the # dependencies a directory has on other directories in a graphical way. The # dependency relations are determined by the #include relations between the -# files in the directories. +# files in the directories. Explicit enabling a directory graph, when +# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command +# \directorygraph. Disabling a directory graph can be accomplished by means of +# the command \hidedirectorygraph. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). -# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order -# to make the SVG files visible in IE 9+ (other browsers do not have this -# requirement). +# https://www.graphviz.org/)). +# +# Note the formats svg:cairo and svg:cairo:cairo cannot be used in combination +# with INTERACTIVE_SVG (the INTERACTIVE_SVG will be set to NO). # Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, -# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and -# png:gdiplus:gdiplus. +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus, +# png:gdiplus:gdiplus, svg:cairo, svg:cairo:cairo, svg:svg, svg:svg:core, +# gif:cairo, gif:cairo:gd, gif:cairo:gdiplus, gif:gdiplus, gif:gdiplus:gdiplus, +# gif:gd, gif:gd:gd, jpg:cairo, jpg:cairo:gd, jpg:cairo:gdiplus, jpg:gd, +# jpg:gd:gd, jpg:gdiplus and jpg:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. DOT_IMAGE_FORMAT = png -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. +# If DOT_IMAGE_FORMAT is set to svg or svg:svg or svg:svg:core, then this option +# can be set to YES to enable generation of interactive SVG images that allow +# zooming and panning. # # Note that this requires a modern browser other than Internet Explorer. Tested # and working are Firefox, Chrome, Safari, and Opera. -# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make -# the SVG files visible. Older versions of IE do not have SVG support. +# +# Note This option will be automatically disabled when DOT_IMAGE_FORMAT is set +# to svg:cairo or svg:cairo:cairo. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2441,11 +2841,12 @@ DOT_PATH = DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). +# You can include diagrams made with dia in Doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. -MSCFILE_DIRS = +DIA_PATH = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile @@ -2453,28 +2854,34 @@ MSCFILE_DIRS = DIAFILE_DIRS = -# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = -# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a -# configuration file for plantuml. +# When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for PlantUML. PLANTUML_CFG_FILE = -# When using plantuml, the specified paths are searched for files specified by -# the !include statement in a plantuml block. +# When using PlantUML, the specified paths are searched for files specified by +# the !include statement in a PlantUML block. PLANTUML_INCLUDE_PATH = +# The PLANTUMLFILE_DIRS tag can be used to specify one or more directories that +# contain PlantUml files that are included in the documentation (see the +# \plantumlfile command). + +PLANTUMLFILE_DIRS = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes -# larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct +# larger than this value, Doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that if the number of direct # children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that # the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. @@ -2495,18 +2902,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support @@ -2516,17 +2911,37 @@ DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO -# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES + +# You can define message sequence charts within Doxygen comments using the \msc +# command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will +# use a built-in version of mscgen tool to produce the charts. Alternatively, +# the MSCGEN_TOOL tag can also specify the name an external tool. For instance, +# specifying prog as the value, Doxygen will call the tool as prog -T +# -o . The external tool should support +# output file formats "png", "eps", "svg", and "ismap". + +MSCGEN_TOOL = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = diff --git a/docs/DoxygenLayout.xml b/docs/DoxygenLayout.xml index 109f19ef..ac118cab 100644 --- a/docs/DoxygenLayout.xml +++ b/docs/DoxygenLayout.xml @@ -3,8 +3,11 @@ - + + + + diff --git a/docs/08_animations.md b/docs/animations.md similarity index 58% rename from docs/08_animations.md rename to docs/animations.md index 083a1dd2..f579751f 100644 --- a/docs/08_animations.md +++ b/docs/animations.md @@ -2,9 +2,9 @@ [TOC] -## Tileset animation +## Overview -## Sprite animation +Tilengine has a built-in sequencing and animation facility, that can be used to create animations in background layers, sprites, and color effects ## Color cycle (palette animation) @@ -18,11 +18,7 @@ This is a quick reference of related functions in this chapter: |Function | Quick description |------------------------------------|------------------------------------- -|\ref TLN_SetTilesetAnimation | Starts a tileset animation -|\ref TLN_SetSpriteAnimation | Starts a sprite animation |\ref TLN_SetPaletteAnimation | Starts a palette animation (color cycle) |\ref TLN_SetPaletteAnimationSource | Sets the source palette of a color cycle animation -|\ref TLN_GetAnimationState | Checks the state of the specified animation -|\ref TLN_SetAnimationDelay | Sets the playback speed of a given animation |\ref TLN_GetAvailableAnimation | Finds an available (unused) animation -|\ref TLN_DisableAnimation | Disables the animation +|\ref TLN_DisablePaletteAnimation | Disables animation of palette (color cycle) diff --git a/docs/15_bitmaps.md b/docs/bitmaps.md similarity index 100% rename from docs/15_bitmaps.md rename to docs/bitmaps.md diff --git a/docs/09_blending.md b/docs/blending.md similarity index 100% rename from docs/09_blending.md rename to docs/blending.md diff --git a/docs/customdoxygen.css b/docs/customdoxygen.css index 445b3417..fa11e73b 100644 --- a/docs/customdoxygen.css +++ b/docs/customdoxygen.css @@ -5,32 +5,38 @@ .sm-dox a:hover, .sm-dox a.highlighted, .sm-dox ul a:hover { - background: none; - text-shadow: none; + background: none; + text-shadow: none; } + .sm-dox a span.sub-arrow { - border-color: #f2f2f2 transparent transparent transparent; + border-color: #f2f2f2 transparent transparent transparent; } + .sm-dox a span.sub-arrow:active, .sm-dox a span.sub-arrow:focus, .sm-dox a span.sub-arrow:hover, .sm-dox a:hover span.sub-arrow { - border-color: #000fb3 transparent transparent transparent; + border-color: #000fb3 transparent transparent transparent; } + .sm-dox ul a span.sub-arrow:active, .sm-dox ul a span.sub-arrow:focus, .sm-dox ul a span.sub-arrow:hover, .sm-dox ul a:hover span.sub-arrow { - border-color: transparent transparent transparent #000fb3; + border-color: transparent transparent transparent #000fb3; } + .sm-dox ul a:hover { - background: #666666; - text-shadow: none; + background: #666666; + text-shadow: none; } + .sm-dox ul.sm-nowrap a { - color: #4d4d4d; - text-shadow: none; + color: #4d4d4d; + text-shadow: none; } + #main-nav, #main-menu, #main-menu a, @@ -55,8 +61,9 @@ span.lineno a:hover, .todo code, .test code, .doxtable code { - background: none; + background: none; } + #titlearea, .footer, .contents, @@ -66,8 +73,9 @@ table.doxtable td, table.doxtable th, hr, .memSeparator { - border: none; + border: none; } + #main-menu a, #main-menu a:visited, #main-menu a:hover, @@ -75,12 +83,14 @@ hr, .reflist dt a.el, .levels span, .directory .levels span { - text-shadow: none; + text-shadow: none; } + .memdoc, dl.reflist dd { - box-shadow: none; + box-shadow: none; } + div.headertitle, .note code, .pre code, @@ -93,13 +103,15 @@ div.headertitle, .todo code, .test code, table.doxtable code { - padding: 0; + padding: 0; } + #nav-path, .directory .levels, span.lineno { - display: none; + display: none; } + html, #titlearea, .footer, @@ -111,11 +123,13 @@ tr.even, .memItemLeft, .memItemRight, code { - background: #f2f2f2; + background: #f2f2f2; } + body { - color: #4d4d4d; + color: #4d4d4d; } + h1, h2, h2.groupheader, @@ -126,178 +140,212 @@ h5, h6, strong, em { - color: #1a1a1a; - border-bottom: none; + color: #1a1a1a; + border-bottom: none; } + h1 { - padding-top: 0.5em; - font-size: 180%; + padding-top: 0.5em; + font-size: 180%; } + h2 { - padding-top: 0.5em; - margin-bottom: 0; - font-size: 140%; + padding-top: 0.5em; + margin-bottom: 0; + font-size: 140%; } + h3 { - padding-top: 0.5em; - margin-bottom: 0; - font-size: 110%; + padding-top: 0.5em; + margin-bottom: 0; + font-size: 110%; } + .tlnheader { - font-size: 16px; - height: 64px; - max-width: 920px; - min-width: 800px; - padding: 0 32px; - margin: 0 auto; -} + font-size: 16px; + height: 64px; + max-width: 920px; + min-width: 800px; + padding: 0 32px; + margin: 0 auto; +} + #tlnhome { - line-height: 64px; - padding-right: 48px; - color: #666666; - font-size: 2.5em; + line-height: 64px; + padding-right: 48px; + color: #666666; + font-size: 2.5em; } + .tlnnavbar { - list-style-type: none; - margin: 0 auto; - float: left; + list-style-type: none; + margin: 0 auto; + float: left; } + #tlnhome, .tlnnavbar li { - float: left; + float: left; } + .tlnnavbar a, .tlnnavbar a:visited { - line-height: 64px; - margin-left: 2em; - display: block; - color: #666666; + line-height: 64px; + margin-left: 2em; + display: block; + color: #666666; } + #tlnhome, .tlnnavbar a, .tlnnavbar a:visited { - transition: .35s ease; + transition: .35s ease; } + #titlearea, .footer { - color: #666666; + color: #666666; } + address.footer { - text-align: center; - padding: 2em; - margin-top: 3em; + text-align: center; + padding: 2em; + margin-top: 3em; } + #top { - background: #666666; + background: #666666; } + #main-nav { - max-width: 960px; - min-width: 800px; - margin: 0 auto; - font-size: 13px; + max-width: 960px; + min-width: 800px; + margin: 0 auto; + font-size: 13px; } + #main-menu { - max-width: 920px; - min-width: 800px; - margin: 0 auto; - font-size: 13px; + max-width: 920px; + min-width: 800px; + margin: 0 auto; + font-size: 13px; } + .memtitle { - display: none; + display: none; } + .memproto, .memname { - font-weight: bold; - text-shadow: none; + font-weight: bold; + text-shadow: none; } + #main-menu { - height: 36px; - display: block; - position: relative; + height: 36px; + display: block; + position: relative; } + #main-menu a, #main-menu a:visited, #main-menu a:hover, #main-menu li { - color: #f2f2f2; + color: #f2f2f2; } + #main-menu li ul.sm-nowrap li a { - color: #4d4d4d; + color: #4d4d4d; } + #main-menu li ul.sm-nowrap li a:hover { - color: #000fb3; + color: #000fb3; } + .contents { - min-height: 590px; + min-height: 590px; } + div.contents, div.header { - max-width: 920px; - margin: 0 auto; - padding: 0 32px; - background: #ffffff none; + max-width: 920px; + margin: 0 auto; + padding: 0 32px; + background: #ffffff none; } + table.doxtable th, dl.reflist dt { - background: linear-gradient(to bottom, #1300e5 0%, #000fb3 100%); - box-shadow: inset 0 0 32px #000fb3; - text-shadow: 0 -1px 1px #000866; - text-align: left; - color: #ffffff; + background: linear-gradient(to bottom, #1300e5 0%, #000fb3 100%); + box-shadow: inset 0 0 32px #000fb3; + text-shadow: 0 -1px 1px #000866; + text-align: left; + color: #ffffff; } + dl.reflist dt a.el { - color: #000fb3; - padding: .2em; - border-radius: 4px; - background-color: #808aff; + color: #000fb3; + padding: .2em; + border-radius: 4px; + background-color: #808aff; } + div.toc { - float: none; - width: auto; + float: none; + width: auto; } + div.toc h3 { - font-size: 1.17em; + font-size: 1.17em; } + div.toc ul { - padding-left: 1.5em; + padding-left: 1.5em; } + div.toc li { - font-size: 1em; - padding-left: 0; - list-style-type: disc; + font-size: 1em; + padding-left: 0; + list-style-type: disc; } + div.toc, .memproto, div.qindex, div.ah { - background: linear-gradient(to bottom, #f2f2f2 0%, #e6e6e6 100%); - box-shadow: inset 0 0 32px #e6e6e6; - text-shadow: 0 1px 1px #ffffff; - color: #1a1a1a; - border: 2px solid #e6e6e6; - border-radius: 4px; -} + background: linear-gradient(to bottom, #f2f2f2 0%, #e6e6e6 100%); + box-shadow: inset 0 0 32px #e6e6e6; + text-shadow: 0 1px 1px #ffffff; + color: #1a1a1a; + border: 2px solid #e6e6e6; + border-radius: 4px; +} + .paramname { - color: #000433; + color: #000433; } + dl.reflist dt { - border: 2px solid #000fb3; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom: none; + border: 2px solid #000fb3; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: none; } + dl.reflist dd { - border: 2px solid #000fb3; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; - border-top: none; + border: 2px solid #000fb3; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-top: none; } + table.doxtable { - border-collapse: inherit; - border-spacing: 0; - border: 2px solid #000fb3; - border-radius: 4px; + border-collapse: inherit; + border-spacing: 0; + border: 2px solid #000fb3; + border-radius: 4px; } + a, a:hover, a:visited, @@ -308,50 +356,57 @@ a.el:visited, #tlnhome:hover, #main-menu a:hover, span.lineno a:hover { - color: #000fb3; - text-decoration: none; + color: #000fb3; + text-decoration: none; } + div.directory { - border-collapse: inherit; - border-spacing: 0; - border: 2px solid #000fb3; - border-radius: 4px; + border-collapse: inherit; + border-spacing: 0; + border: 2px solid #000fb3; + border-radius: 4px; } + hr, .memSeparator { - height: 2px; - background: linear-gradient(to right, #f2f2f2 0%, #d9d9d9 50%, #f2f2f2 100%); + height: 2px; + background: linear-gradient(to right, #f2f2f2 0%, #d9d9d9 50%, #f2f2f2 100%); } + dl.note, dl.pre, dl.post, dl.invariant { - background: linear-gradient(to bottom, #ddfad1 0%, #cbf7ba 100%); - box-shadow: inset 0 0 32px #baf5a3; - color: #1e5309; - border: 2px solid #afe599; + background: linear-gradient(to bottom, #ddfad1 0%, #cbf7ba 100%); + box-shadow: inset 0 0 32px #baf5a3; + color: #1e5309; + border: 2px solid #afe599; } + dl.warning, dl.attention { - background: linear-gradient(to bottom, #fae8d1 0%, #f7ddba 100%); - box-shadow: inset 0 0 32px #f5d1a3; - color: #533309; - border: 2px solid #e5c499; + background: linear-gradient(to bottom, #fae8d1 0%, #f7ddba 100%); + box-shadow: inset 0 0 32px #f5d1a3; + color: #533309; + border: 2px solid #e5c499; } + dl.deprecated, dl.bug { - background: linear-gradient(to bottom, #fad1e3 0%, #f7bad6 100%); - box-shadow: inset 0 0 32px #f5a3c8; - color: #53092a; - border: 2px solid #e599bb; + background: linear-gradient(to bottom, #fad1e3 0%, #f7bad6 100%); + box-shadow: inset 0 0 32px #f5a3c8; + color: #53092a; + border: 2px solid #e599bb; } + dl.todo, dl.test { - background: linear-gradient(to bottom, #d1ecfa 0%, #bae3f7 100%); - box-shadow: inset 0 0 32px #a3daf5; - color: #093a53; - border: 2px solid #99cce5; + background: linear-gradient(to bottom, #d1ecfa 0%, #bae3f7 100%); + box-shadow: inset 0 0 32px #a3daf5; + color: #093a53; + border: 2px solid #99cce5; } + dl.note, dl.pre, dl.post, @@ -362,11 +417,12 @@ dl.deprecated, dl.bug, dl.todo, dl.test { - border-radius: 4px; - padding: 1em; - text-shadow: 0 1px 1px #ffffff; - margin: 1em 0; + border-radius: 4px; + padding: 1em; + text-shadow: 0 1px 1px #ffffff; + margin: 1em 0; } + .note a, .pre a, .post a, @@ -387,45 +443,53 @@ dl.test { .bug a:visited, .todo a:visited, .test a:visited { - color: inherit; + color: inherit; } + div.line { - line-height: inherit; + line-height: inherit; } + div.fragment, pre.fragment { - background: #f2f2f2; - border-radius: 4px; - border: none; - padding: 1em; - overflow: auto; - border-left: 4px solid #cccccc; - margin: 1em 0; -} + background: #f2f2f2; + border-radius: 4px; + border: none; + padding: 1em; + overflow: auto; + border-left: 4px solid #cccccc; + margin: 1em 0; +} + .lineno a, .lineno a:visited, .line, pre.fragment { - color: #4d4d4d; + color: #4d4d4d; } + span.preprocessor, span.comment { - color: #007899; + color: #007899; } + a.code, a.code:visited { - color: #0039e6; + color: #0039e6; } + span.keyword, span.keywordtype, span.keywordflow { - color: #404040; - font-weight: bold; + color: #404040; + font-weight: bold; } + span.stringliteral { - color: #360099; + color: #360099; } + code { - padding: .1em; - border-radius: 4px; -} + padding: .1em; + border-radius: 4px; +} \ No newline at end of file diff --git a/docs/diagrams.odg b/docs/diagrams.odg new file mode 100644 index 00000000..70756577 Binary files /dev/null and b/docs/diagrams.odg differ diff --git a/docs/guides.md b/docs/guides.md new file mode 100644 index 00000000..2834d8bf --- /dev/null +++ b/docs/guides.md @@ -0,0 +1,38 @@ +# Guides + +\attention +Documentation guides are in an unfinished, work-in-progress state. Some sections are complete, others outdated, and others missing. Documentation is being actively written and should be finished soon. + +There are specific guides for each feature + +[Installing](install.md) shows how to install the development files + +[Getting started](quick_guide.md) is a guide for new users of Tilengine. It takes you through the most basic features. + +**Engine setup** + +* [Initialization](starting.md) - basic engine setup and error handling +* [Using the window](windowing.md) - using the built-in window +* [External rendering](rendering.md) - integrating inside a host environment + +**Rendering features** + +* [Background layers](layers.md) - using background layers +* [Sprites](sprites.md) - working with moving objects +* [Animations](animations.md) - animating layers, sprites and colors +* [Blending](blending.md) - compositing transparency, light and shadow effects +* [Raster effects](raster_effects.md) - create advanced FX +* [World](world.md) - assisted parallax scroll and object position in world space + +**Working with assets** + +* [Tilemaps](tilemaps.md) - managing background layout +* [Tilesets](tilesets.md) - managing background graphic pieces +* [Spritesets](spritesets.md) - managing sprite graphics +* [Palettes](palettes.md) - managing color +* [Sequences](sequences.md) - managing sequences for animation engine +* [Bitmaps](bitmaps.md) - using bitmaps + +The [performance tips](performance.md) gives some tips for maximizing performance + +The [reference documentation](modules.html) provides more detailed information about specific functions. diff --git a/docs/header.html b/docs/header.html index 0bcb519b..d56d0d99 100644 --- a/docs/header.html +++ b/docs/header.html @@ -23,6 +23,7 @@
    +  $projectnumber
diff --git a/docs/img/graph_bitmap_layer.png b/docs/img/graph_bitmap_layer.png new file mode 100644 index 00000000..bd63088c Binary files /dev/null and b/docs/img/graph_bitmap_layer.png differ diff --git a/docs/img/graph_object_layer.png b/docs/img/graph_object_layer.png new file mode 100644 index 00000000..9413e323 Binary files /dev/null and b/docs/img/graph_object_layer.png differ diff --git a/docs/img/graph_tiled_layer.png b/docs/img/graph_tiled_layer.png new file mode 100644 index 00000000..ba1401c5 Binary files /dev/null and b/docs/img/graph_tiled_layer.png differ diff --git a/docs/img/quick.png b/docs/img/quick.png new file mode 100644 index 00000000..219f403c Binary files /dev/null and b/docs/img/quick.png differ diff --git a/docs/img/sprite_pivot.png b/docs/img/sprite_pivot.png new file mode 100644 index 00000000..3aa30e0c Binary files /dev/null and b/docs/img/sprite_pivot.png differ diff --git a/docs/img/tilemap.png b/docs/img/tilemap.png new file mode 100644 index 00000000..5a01bbe3 Binary files /dev/null and b/docs/img/tilemap.png differ diff --git a/docs/img/tileset.png b/docs/img/tileset.png new file mode 100644 index 00000000..1a3c65b1 Binary files /dev/null and b/docs/img/tileset.png differ diff --git a/docs/img/world_layers.png b/docs/img/world_layers.png new file mode 100644 index 00000000..f3436cb8 Binary files /dev/null and b/docs/img/world_layers.png differ diff --git a/docs/img/world_scroll.gif b/docs/img/world_scroll.gif new file mode 100644 index 00000000..08e20b1d Binary files /dev/null and b/docs/img/world_scroll.gif differ diff --git a/docs/img/world_tiled.png b/docs/img/world_tiled.png new file mode 100644 index 00000000..27bfdd4e Binary files /dev/null and b/docs/img/world_tiled.png differ diff --git a/docs/02_install.md b/docs/install.md similarity index 65% rename from docs/02_install.md rename to docs/install.md index 8b149bdc..57c8c8f6 100644 --- a/docs/02_install.md +++ b/docs/install.md @@ -14,16 +14,16 @@ install ## Debian-based linux and OSX ``` -./sudo install +sudo ./install ``` -**Note for OS X users**: Tilengine depends on having SDL2 in Framework format installed on your system, that by default is not installed. Please download it here: https://www.libsdl.org/release/SDL2-2.0.5.dmg. Homebrew port won't work, use the official DMG file instead. +**Note for MacOS users**: Tilengine depends on having SDL2 in Framework format installed on your system. Homebrew port won't work. Download the latest dmg here: https://github.com/libsdl-org/SDL/releases. ## Running the samples C samples are located in `Tilengine/samples` folder. To build them you need the gcc compiler suite, and/or Visual C++ in windows. * **Linux**: the GCC compiler suite is already installed by default * **Windows**: you must install [MinGW](http://www.mingw.org/) or [Visual Studio Community](https://www.visualstudio.com/vs/community/) -* **Apple OS X**: You must install [Command-line tools for Xcode](https://developer.apple.com/xcode/). An Apple ID account is required. +* **Apple MacOS**: You must install [Command-line tools for Xcode](https://developer.apple.com/xcode/). An Apple ID account is required. Once installed, open a console window in the C samples folder and type the suitable command depending on your platform: @@ -31,7 +31,7 @@ Once installed, open a console window in the C samples folder and type the suita ``` mingw32-make ``` -### Unix-like (Linux, Raspberry Pi, Apple OS X) +### Unix-like (Linux, Raspberry Pi, MacOS) ``` make ``` diff --git a/docs/01_introduction.md b/docs/introduction.md similarity index 54% rename from docs/01_introduction.md rename to docs/introduction.md index 021e261e..3893277d 100644 --- a/docs/01_introduction.md +++ b/docs/introduction.md @@ -1,19 +1,16 @@ # Introduction Tilengine is a free, cross-platform 2D graphics engine for creating classic/retro games with tile maps, sprites and palettes. Its unique scanline-based rendering algorithm makes raster effects a core feature, a technique used by many games running on real 2D graphics chips. This engine brings back the 16-bit era in its full glory and pushes it even further! -## Features +## Main features * Written in portable C (C99) * MPL 2.0 Licensed: free for any project, including commercial ones -* Cross platform: available builds for Windows (32/64), Linux PC(32/64), Mac OS X and Raspberry Pi -* High performance: all samples run at 60 fps with CRT emulation enabled on a Raspberry Pi 3 -* Streamlined, easy to learn API that requires very little lines of code +* Cross platform: available builds for Windows (32/64), Linux PC(32/64), MacOS and Raspberry Pi * Built-in SDL-based windowing for quick tests * Integrate inside any existing framework as a slave renderer -* Loads assets from open standard standard file formats -* Create or modify graphic assets procedurally at run time * True raster effects: modify render parameters between scanlines -* Background layer scaling and rotation -* Sprite scaling -* Several blending modes for layers and sprites -* Pixel accurate sprite vs sprite and sprite vs layer collision detection -* Special effects: per-column offset, mosaic, per-pixel displacement, CRT emulation... + +Read [Getting started](quick_guide.md) if you're new to Tilengine to get an overview. + +The [Guides](guides.md) contains organized guides for every engine feature. + +The [API reference](modules.html) contains in-depth information about every function in the API. \ No newline at end of file diff --git a/docs/layers.md b/docs/layers.md new file mode 100644 index 00000000..4f086fa2 --- /dev/null +++ b/docs/layers.md @@ -0,0 +1,374 @@ +# Background layers + +[TOC] + +## Introduction + +Background layers are used to implement the levels where gameplay takes place. They can be moved around and always fill the screen. They can be just decorations, or contain actual level structure where sprites can interact and collide (platforms, walls, environmental hazards... ) + +Background layers can have transparent, cut-through areas where the underlying layer(s) or background color is seen. + +Layers are referenced by an index, starting at 0 for the topmost, increasing up to number of layers minus 1 for the bottom-most. The total number of layers is pre-allocated when the engine is initialized with \ref TLN_Init and cannot be changed later at runtime. + +## Types of layers + +Tilengine supports three types of layers: + +* **Tiled layer**: uses a tilemap and a tile-based tileset +* **Object list**: uses an object list and a bitmap-based tileset +* **Single bitmap**: uses a whole bitmap + +### Tiled layers + +Tiled layers are composed of **tilemaps**, a rectangular, grid-like arrangement of square pieces called **tiles**. These tiles are located inside a **tileset**, a collection of related square pieces that are assembled to form a level. + +![Tiled layer graph](img/graph_tiled_layer.png)
+*Block diagram of a tiled layer* + +Tiled layers are loaded from `.tmx` files with the \ref TLN_LoadTilemap function, that gets a filename and an optional layer name, and returns a \ref TLN_Tilemap handler. + +```C +TLN_Tilemap tilemap = TLN_LoadTilemap("level.tmx", "foreground"); +``` + +If layer name is set to `NULL`, it loads the first layer it encounters: + +```C +TLN_Tilemap tilemap = TLN_LoadTilemap("level.tmx", NULL); +``` + +Once a tilemap is loaded, it must be assigned to a layer with \ref TLN_SetLayerTilemap function. It takes the layer index and the tilemap handler: + +```C +TLN_SetLayerTilemap(0, tilemap); +``` + +![A tilemap](img/tilemap.png)
+*A tilemap in Tiled editor* + +### Bitmap layers + +Bitmap layers use a single, big bitmap image that can be loaded with \ref TLN_LoadBitmap function that just takes a filename (`.bmp` and `.png` files supported): + +```C +TLN_Bitmap bitmap = TLN_LoadBitmap("background.png) +``` + +Once a bitmap is loaded, it must be assigned to a layer with \ref TLN_SetLayerBitmap function. It takes the layer index and the bitmap: + +```C +TLN_SetLayerBitmap(0, bitmap); +``` + +![Bitmap layer graph](img/graph_bitmap_layer.png)
+*Block diagram of a bitmap layer* + +### Object layers + +Object layers have a list of different items freely scattered across the playfield. Each item is a bitmap inside a bitmap-based tileset. + +![Object layer graph](img/graph_object_layer.png)
+*Block diagram of an object layer* + +Object list are loaded from `.tmx` files too, with the \ref TLN_LoadObjectList function that takes a filename and an optional layer name, and returns a \ref TLN_ObjectList handle. Just like tiled layers, if no layer name is specified, it loads the first object layer inside the `.tmx` file: + +```C +TLN_ObjectList list = TLN_LoadObjectList("level`.tmx`", NULL); +``` + +To enable a object list layer, it must be set with the \ref TLN_SetLayerObjects, that takes the layer index, the object list, and an optional tileset. If no tileset is specified, it uses the default tileset of the object list: + +```C +TLN_SetLayerObject(0, list, NULL); +``` + +However an explicit bitmap-based tileset can be loaded and assigned: + +```C +TLN_Tileset tileset = TLN_LoadTileset("objects.tsx") +TLN_SetLayerObject(0, list, objects); +``` + +## Common operations + +Layers have a common set of operations that are available to all three types of layers + +### Movement & scroll + +Layers can be much bigger than the display area. To set the what zone is being displayed, use \ref TLN_SetLayerPosition passing the layer index, and the horizontal (x) and vertical (y) displacement from the origin located in the top-left corner. + +Sets layer 0 starting at x=320 and y=160: +```C +TLN_SetLayerPosition(0, 320,160); +``` + +![Layer moved to 120,16](img/layer_scroll.png) + +### Setting the palette + +When a layer is configured, it automatically gets the palette of the attached asset -tileset or bitmap-. However is possible to change the palette to another one with \ref TLN_SetLayerPalette, passing the layer index and a handle to a \ref TLN_Palette object. + +```C +TLN_Palette palette = TLN_LoadPalette("palette.act"); +TLN_SetLayerPalette(0, palette); +``` + +### Blending + +Blending allows to combine the color of a layer with the underlying color already present. There are several predefined blending modes, read chapter. + +To enable blending on a layer use \ref TLN_SetLayerBlendMode passing the layer index and one of available \ref TLN_Blend modes: + +```C +TLN_SetLayerBlendMode(0, BLEND_MIX50); +``` + +To disable blending, use \ref BLEND_NONE mode: + +```C +TLN_SetLayerBlendMode(0, BLEND_NONE); +``` + +### Priority + +By default, background layers are drawn behind the sprites -that's why they're background layers-. However a given layer can also be drawn in front of sprites, giving it *priority* over sprites. Use \ref TLN_SetLayerPriority passing the layer index, and a boolean with `true`to enable or `false` to or disable priority: + +```C +/* enable priority on layer 0 */ +TLN_SetLayerPriority(0, true); + +/* disable priority on layer 0 */ +TLN_SetLayerPriority(0, false); +``` + +### Clipping rectangle + +Each layer can be assigned a clipping rectangle: a region that delimits where drawing occurs inside the window, leaving outside pixels unaffected. By default the clipping rectangle is disabled and the layer covers the entire window. + +To enable the clipping rectangle, call \ref TLN_SetLayerClip passing the index of the layer, and four numbers telling the x,y of the top-left corner, and the x,y of the bottom-right corner. For example, to set clipping in layer 0 from 32,20 to 360,240: +```c +TLN_SetLayerClip (0, 32,20, 360,240); +``` +Clipping rectangle 32,20 - 360,240: + +![Clipping rectangle](img/layer_clip.png) + +To disable the clipping rectangle, call \ref TLN_DisableLayerClip passing the layer index to disable: +```c +TLN_DisableLayerClip (0); +``` + +### Disabling + +Layers can be disabled when they're not needed anymore with \ref TLN_DisableLayer, passing the layer index. + +```C +TLN_DisableLayer(0); +``` + +### Enabling + +A disabled layer with \ref TLN_DisableLayer can be re-enabled again, as long as it was previously configured and contains valid data. To enable a layer use \ref TLN_EnableLayer passing the index to the layer to enable: + +```C +TLN_EnableLayer(0); +``` + +## Special effects + +### Column offset + +This feature allows displacing vertically each column of tiles in screen space by a given amount of pixels. This can be used to fake vertical parallaxing, or to do moderate tilting and deformation of terrain. + +To setup the effect, it needs an array of integers as large as the number of columns that fit in one screen plus 2. For example, with a 400x240 framebuffer and are 16x16 tiles, the number of positions is 400/16 + 2 = 27. Call \ref TLN_SetLayerColumnOffset passing the layer index and a pointer to the array of integers: +```c +const int hres = 400; +const int tile_width = 16; +const int size = hres/tile_width + 2; +int offsets[size] = { 0 }; +/* ... */ +TLN_SetLayerColumnOffset(0, offsets); +``` +Now the layer 0 column offset is linked to the *offsets* array. Setting any value(s) inside the array and drawing a frame has immediate visible effects, there's no need to call the function each time. For example, to create a slightly sloped terrain: +```c +int c; +for (c = 0; c < size; c += 1) + offsets[c] = c; +TLN_DrawFrame(0); +``` +Column offset: each column is displaced 1 pixel incrementally: + +![Column offset](img/layer_column.png) + +To disable the effect, call the function with a `NULL` pointer instead of a valid array: +```c +TLN_SetLayerColumnOffset (0, NULL); +``` + +This effect is only available on tiled layers. + +### Scaling + +Layers can be drawn upscaled or downscaled with an arbitrary factor. The scaling starts in screen space at the top-left corner, so the scrolling position isn't affected by scaling. To enable scaling, call \ref TLN_SetLayerScaling passing the layer index and two floating point values with the horizontal and vertical factor, respectively. Values greater than 1.0 upscale, and smaller than 1.0 downscale. For example to set an horizontal downscaling of 0.5 and vertical upscaling of 1.5 for layer 0: +```c +TLN_SetLayerScaling (0, 0.5f, 1.5f); +``` + +Layer scaling x0.5 horizontal, x1.5 vertical: + +![Column offset](img/layer_scaling.png) + +To disable scaling, call \ref TLN_ResetLayerMode passing the layer index: +```c +TLN_ResetLayerMode (0); +``` + +This effect is available for tiled and bitmap layers. + +### Affine transform + +Affine transform allows to rotate, translate and scale any layer (much like SNES Mode 7). To enable this transformation, call \ref TLN_SetLayerTransform passing the layer index, rotation angle in degrees, two floating point values with the center of rotation in screen space, and two floating point values with horizontal and vertical scaling. For example, to enable affine transform on layer 0, 30 degrees rotation around the center of the screen, and 1.5 upscaling in both axis: +```c +TLN_SetLayerTransform (0, 30.0f, 240.0f,160.0f, 1.5f,1.5f); +``` + +30ยบ degree rotation around 240,160 (screen center), x1.5 upscaling: + +![Affine transform](img/layer_affine.png) + +**TIP**: affine transform is an intensive operation. If you just want to implement scaling but not rotation, use \ref TLN_SetLayerScaling instead because it's much more lightweight. + +To disable affine transform, call \ref TLN_ResetLayerMode passing the layer index: +```c +TLN_ResetLayerMode (0); +``` + +This effect is available for tiled and bitmap layers. + +### Per-pixel mapping + +Per-pixel mapping is a similar operation to *column offset*, but applied to every screen pixel instead of just every column. + +To setup the effect, it needs an array of \ref TLN_PixelMap items with as many positions as pixels in the framebuffer. For example, for a 480x320 setup, it needs to have 480x320 = 153,600 items. Each item contains a pair of integer values with the coordinates of that pixel relative to the to left corner of the screen at 0,0. Call \ref TLN_SetLayerPixelMapping passing the layer index and a pointer to the array of \ref TLN_PixelMap items: + +```c +const int hres = 480; +const int vres = 320; +const int num_pixels = hres * vres; +TLN_PixelMap pixel_map[num_pixels]; +/* ... */ +TLN_SetLayerPixelMapping (0, pixel_map); +``` + +Pixel mapping applying some trigonometric displacements to the \ref TLN_PixelMap array: + +![Affine transform](img/layer_mapping.png) + +Let's we want that pixel at 320,240 takes its value from pixel located at 200,100. First we have to calculate which pixel to modify inside the \ref TLN_PixelMap array. The formula is: +> index = y * width + x + +Then we set `dx` and `dy` with the coordinates of the desired source pixel: +```c +int x = 320; /* pixel we want to remap */ +int y = 240; +int index = y * width + x; +/* ... */ +pixel_map[index].dx = 200; +pixel_map[index].dy = 100; +``` + +This effect is available for tiled and bitmap layers. + +### Mosaic + +The mosaic effect pixelates the layer, making some pixels bigger and skipping others so the relative image size keeps constant. It's similar to the mosaic effect in SNES, but more flexible. Different horizontal and vertical pixel values are possible -not just square pixels-, and any size can be set, not just powers of 2. To enable the effect, call \ref TLN_SetLayerMosaic passing the layer index, the horizontal pixel size, and the vertical pixel size. For example to set mosaic on layer 0 with 8 pixel horizontal factor and 6 pixel vertical factor: +```c +TLN_SetLayerMosaic (0, 8,6); +``` +Mosaic effect with 8 horizontal and 6 vertical pixel size factor: + +![Affine transform](img/layer_mosaic.png) + +To disable the mosaic effect, just call \ref TLN_DisableLayerMosaic passing the layer index: +```c +TLN_DisableLayerMosaic (0); +``` + +This effect is available for tiled and bitmap layers. + +### Special effects chart + +Not all special effects are available for any layer. This chart shows which effects are available on which layers: + +Effect | Tiled | Bitmap | Object +-------------|-------|--------|-------------- +Column offset| yes | - | - +Scaling | yes | yes | - +Affine | yes | yes | - +Per-pixel map| yes | yes | - +Mosaic | yes | yes | - + +## Gameplay support + +### Retrieving active assets + +Sometimes there's needed to get what assets are used by a given layer, especially when the layers have been automatically populated with \ref TLN_LoadWorld + +Query | Returns +-------------------------|------------------------------- +\ref TLN_GetLayerType | Returns layer type, \ref TLN_LayerType enumeration +\ref TLN_GetLayerTilemap | Returns active tilemap on tiled layers +\ref TLN_GetLayerTileset | Returns active tileset on tiled an object layers +\ref TLN_GetLayerBitmap | Returns active bitmap on bitmapped layers +\ref TLN_GetLayerPalette | Returns active palette if the layer uses one +\ref TLN_GetLayerObjects | Returns active object list on object layers + +### Getting Tile data + +Gameplay using layers for character interaction will require gathering information about it: + +* Size in pixels +* Details about any tile + +Use \ref TLN_GetLayerWidth and \ref TLN_GetLayerHeight to get size in pixels: + +```C +int width = TLN_GetLayerWidth(0); +int height = TLN_GetLayerHeight(0); +``` + +To get details about a specific tile, call \ref TLN_GetLayerTile passing the layer index, the x,y pixel in layer space, and a pointer to a \ref TLN_TileInfo struct that will hold the returned data. + +```c +TLN_TileInfo tile_info; /* declare struct to hold returned data */ +TLN_GetLayerTile (0, 1800,300, &tile_info); /* get layer 0 tile at 1800,300 */ +``` + +**NOTE**: This function is only available for tiled layers + +## Summary + +This is a quick reference of related functions in this chapter: + +|Function | Quick description +|--------------------------------|------------------------------------- +|\ref TLN_SetLayerTilemap |Configures a tiled background layer +|\ref TLN_SetLayerBitmap |Configures a full-bitmap background layer +|\ref TLN_SetLayerObjects |Configures an object list background layer +|\ref TLN_SetLayerPalette |Sets the color palette to the layer +|\ref TLN_SetLayerPosition |Moves the viewport inside the layer +|\ref TLN_SetLayerClip |Enables clipping rectangle +|\ref TLN_DisableLayerClip |Disables clipping rectangle +|\ref TLN_SetLayerBlendMode |Sets the blending mode (transparency effect) +|\ref TLN_SetLayerPriority |Sets layer to be drawn on top of sprites +|\ref TLN_SetLayerScaling |Enables layer scaling +|\ref TLN_SetLayerTransform |Sets affine transform matrix to enable rotating and scaling +|\ref TLN_SetLayerPixelMapping |Sets the table for pixel mapping render mode +|\ref TLN_ResetLayerMode |Disables scaling or affine transform for the layer +|\ref TLN_SetLayerColumnOffset |Enables column offset mode for this layer +|\ref TLN_SetLayerMosaic |Enables mosaic effect +|\ref TLN_DisableLayerMosaic |Disables mosaic effect +|\ref TLN_DisableLayer |Disables the specified layer so it is not drawn +|\ref TLN_GetLayerWidth |Returns the layer width in pixels +|\ref TLN_GetLayerHeight |Returns the layer height in pixels +|\ref TLN_GetLayerTile |Gets info about the tile located in tilemap space diff --git a/docs/13_palettes.md b/docs/palettes.md similarity index 100% rename from docs/13_palettes.md rename to docs/palettes.md diff --git a/docs/performance.md b/docs/performance.md new file mode 100644 index 00000000..17bd91bf --- /dev/null +++ b/docs/performance.md @@ -0,0 +1,3 @@ +# Performance tips + +[TOC] diff --git a/docs/quick_guide.md b/docs/quick_guide.md new file mode 100644 index 00000000..390b2c88 --- /dev/null +++ b/docs/quick_guide.md @@ -0,0 +1,176 @@ +# Getting started + +[TOC] + +This guide takes you through writing a simple application using Tilengine. The application will initialize the engine, create a window, load a background layer and a sprite, and exit when the user closes the window. This guide will introduce a few of the most commonly used functions, but there are many more. + +## Step by step + +### Including the Tilengine header + +In the source files of your application where you use Tilengine, you need to include the header file: + +```C +#include "Tilengine.h" +``` + +### Initializing the engine + +First step is initialize the engine with \ref TLN_Init. This takes the following parameters, that will remain constant and cannot be changed once the engine is initialized: + +* Width and height of the framebuffer (virtual screen) +* Maximum number of [background layers](layers.md) +* Maximum number of [sprites](sprites.md) +* Maximum number of color-cycle [palette animations](animations.md) + +```C +TLN_Init(480, 272, 4, 64, 0); +``` + +This creates a framebuffer of 480x272 pixels, 4 background layers, 64 sprites and no palette animations. + +### Creating a window + +By default Tilengine doesn't create a window when it's initialized, because it is designed to act as a slave renderer for any framework or environment. However it also provides its own window environment. + +To create a window, call \ref TLN_CreateWindow **after** having called \ref TLN_Init: + +```C +TLN_CreateWindow(NULL, 0); +``` + +This creates a default window. There are some key bindings: + +* Press Esc to close the window +* Press Alt + Enter to toggle full-screen/windowed +* Press Backspace to toggle on/off built-in CRT effect (default on) + +### Loading a tilemap + +[Tilemaps](tilemaps.md) with the layout of backgrounds are stored inside `.tmx` files. Each `.tmx` file can contain many tilemap and object layers. To load one, call \ref TLN_LoadTilemap with the filename and the name of the layer to load, and returns a \ref TLN_Tilemap handler: + +```C +TLN_Tilemap background = TLN_LoadTilemap ("ruff_n_tumble.tmx", NULL); +``` + +### Setting the layer + +Once the tilemap is loaded, it must be assigned to one of the available background layers with \ref TLN_SetLayerTilemap, passing the layer index and the loaded tilemap: + +```C +TLN_SetLayerTilemap(0, background); +``` + +### Scrolling the background layer + +By default the tilemap is aligned to the top-left corner of the screen (position 0,0). To move it to another position, use \ref TLN_SetLayerPosition passing the layer index and the [x, y] position inside the tilemap: + +```C +TLN_SetLayerPosition(0, 32, 0); +``` + +This advances the viewport of layer 0 32 pixels to the right. + +### Loading a spriteset + +[Spritesets](spritesets.md) containing the animation frames for a sprite are contained inside `.png` files with layout information in associated `.txt`, `.csv`, or `.json` text files. They're loaded with \ref TLN_LoadSpriteset passing the .png file, and return a \ref TLN_Spriteset handler: + +```C +TLN_Spriteset character = TLN_LoadSpriteset("ruff1.png"); +``` + +### Setting the sprite + +Once the spriteset is loaded, it must be assigned to one of the available spritest with \ref TLN_SetSpriteSet, passing the sprite index and the loaded spriteset: + +```C +TLN_SetSpriteSet(0, character); +``` + +### Moving the sprite around + +By default, the sprite is initialized at position 0,0, (top left corner). To move it to another location, use \ref TLN_SetSpritePosition passing the sprite index, and the, x,y screen coordinates: + +```C +TLN_SetSpritePosition(0, 160, 192); +``` + +### Setting the sprite image + +A spriteset contains many frames of animation. By default a sprite is assigned the first image of the spriteset. Images in a spriteset are referenced with their index, starting from 0. Indexes can be obtained from sprite name with \ref TLN_FindSpritesetSprite and assigned with \ref TLN_SetSpritePicture passing sprite index and frame index: + +```C +int frame = TLN_FindSpritesetSprite(spriteset, "standing1"); +TLN_SetSpritePicture(0, index); +``` + +A good practice is to cache the indexes of required frames so they don't need to be searched every time. + +### Running the window loop + +On each frame the window must be updated with \ref TLN_ProcessWindow, which returns `true` until the user requests closing the window, and the frame must be rendered with \ref TLN_DrawFrame. Parameter "frame" is deprecated and can be left as 0: + +```C +while (TLN_ProcessWindow()) { + TLN_DrawFrame (0); +} +``` + +The window will continue active until closed or Esc key is pressed. + +## Putting it together + +Now it's possible to create a simple program that initializes the engine, creates a window, and sets up a background layer and a sprite loaded from asset files: + +### Source code + +```C +#include "Tilengine.h" + +void main(void) { + TLN_Tilemap background; + TLN_Spriteset character; + + /* init engine and window */ + TLN_Init(480, 272, 4, 64, 0); + TLN_CreateWindow (NULL, 0); + + /* setup background layer */ + background = TLN_LoadTilemap ("ruff_n_tumble.tmx", NULL); + TLN_SetLayerTilemap (0, background); + TLN_SetLayerPosition(0, 32, 0); + + /* setup sprite */ + character = TLN_LoadSpriteset("ruff1.png"); + TLN_SetSpriteSet(0, character); + TLN_SetSpritePosition(0, 160, 192); + + /* loop until close */ + while (TLN_ProcessWindow()) + TLN_DrawFrame(0); + + /* release resources */ + TLN_DeleteSpriteset(character); + TLN_DeleteTilemap(background); + TLN_DeleteWindow(); + TLN_Deinit(); +} +``` + +### Building + +To build for Linux, just link with `libTilengine.so` library: + +``` +gcc test.c -lTilengine -o test +``` + +Building for Windows requires linking to `Tilengine.lib`: + +``` +gcc test.c Tilengine.lib -o test.exe +``` + +### Final result + +![](img/quick.png) diff --git a/docs/16_raster_effects.md b/docs/raster_effects.md similarity index 100% rename from docs/16_raster_effects.md rename to docs/raster_effects.md diff --git a/docs/05_rendering.md b/docs/rendering.md similarity index 61% rename from docs/05_rendering.md rename to docs/rendering.md index 3f315dd7..b295dbf4 100644 --- a/docs/05_rendering.md +++ b/docs/rendering.md @@ -2,8 +2,11 @@ [TOC] +## Introduction +Tilengine can be set up to render to any user-provided 32-bit RGBA surface, without creating its own window. This allow to integrate tilengine inside any existing framework as a back-end renderer + ## Setting the target surface -The framebuffer can be any 32 bpp RGBA bitmap, with the dimensions specified during initialization with \ref TLN_Init where tilengine performs the rendering. This surface is user-allocated and can be of any origin: a locked OpenGL texture, an SDL video surface, a Java canvas... whatever you choose. The \ref TLN_SetRenderTarget function takes two parameters: a pointer to the start of pixel data in the surface, and the number of bytes per scanline, the so-called *pitch*. This value is usually the horizontal resolution multipliead by four (the number of bytes required by each pixel) rounded to the next multiple of four. The following example allocates an in-memory framebuffer and sets it: +The framebuffer can be any 32 bpp RGBA bitmap, with the dimensions specified during initialization with \ref TLN_Init where tilengine performs the rendering. This surface is user-allocated and can be of any origin: a locked OpenGL texture, an SDL video surface, a Java canvas... whatever. The \ref TLN_SetRenderTarget function takes two parameters: a pointer to the start of pixel data in the surface, and the number of bytes per scanline, the so-called *pitch*. This value is usually the horizontal resolution multiplied by four (the number of bytes required by each pixel) rounded to the next multiple of four. The following example allocates an in-memory framebuffer and sets it: ```c const int hres = 400; const int vres = 240; @@ -13,11 +16,11 @@ TLN_SetRenderTarget (framebuffer, pitch); ``` ## Drawing frames -Once the framebuffer is set, to update the frame use the \ref TLN_UpdateFrame function. It takes an optional time parameter that's used by animation engine to keep track of frames. For now you can set it to 0 or as an increasing counter: +Once the framebuffer is set, to update the frame use the \ref TLN_UpdateFrame function. It takes an optional frame counter parameter that's used by animation engine to keep track of frames. ```c TLN_UpdateFrame (0); ``` -Now the previously created `framebuffer`surface holds the rendered frame. +Now the previously created `framebuffer` surface holds the rendered frame. ## Basic example This example creates a 400x240 framebuffer in memory, initializes the engine, does the main loop and exits: @@ -26,12 +29,10 @@ This example creates a 400x240 framebuffer in memory, initializes the engine, do #include #include "Tilengine.h" -int main (int argc, char* argv[]) -{ +int main (int argc, char* argv[]) { const int hres = 400; const int vres = 240; const int pitch = hres * sizeof(uint32_t); - int frame = 0; void* framebuffer; /* init and set framebuffer */ @@ -40,12 +41,10 @@ int main (int argc, char* argv[]) TLN_SetRenderTarget (framebuffer, pitch); /* main loop */ - while (1) - { - /* your game stuff goes here */ + while (1) { + /* game logic stuff goes here */ - TLN_UpdateFrame (frame); - frame += 1; + TLN_UpdateFrame (0); } /* deallocate */ diff --git a/docs/14_sequences.md b/docs/sequences.md similarity index 100% rename from docs/14_sequences.md rename to docs/sequences.md diff --git a/docs/sprites.md b/docs/sprites.md new file mode 100644 index 00000000..fade181c --- /dev/null +++ b/docs/sprites.md @@ -0,0 +1,234 @@ +# Sprites + +[TOC] + +## Introduction + +Sprites are any moving object in a videogame that are not a background: a character, enemy, bullet, player, ball, special effect... They can be animated and placed freely anywhere. + +Sprites are referenced by an index, starting at 0 up to number of sprites minus 1. The total number of sprites is pre-allocated when the engine is initialized with \ref TLN_Init and cannot be changed later at runtime. + +## Basic setup +A sprite needs at least a [spriteset](spritesets.md), a collection of related images packed together inside a single bitmap. It's common to pack together all the animation frames of a single character inside the same spriteset, so to change the displayed graphic it's only needed to change the image index. + +Spritesets are loaded with \ref TLN_LoadSpriteset (read more about [spritesets](spritesets.md)). Once loaded, call \ref TLN_SetSpriteSet to attach it to the sprite, passing the sprite index and the reference to the spriteset: + +```c +TLN_Spriteset spriteset = TLN_LoadSpriteset ("FireLeo.png"); +TLN_SetSpriteSet (0, spriteset); +``` + +Now the sprite is displayed at position 0,0 by default (top-left corner) with the first graphic inside the spriteset. Each graphic inside a spriteset has a unique index, starting from 0, and an unique name. To change the graphic show, call \ref TLN_SetSpritePicture passing the sprite index and the graphic index inside the spriteset, starting with 0. For example to set sprite 0 with the 4th graphic inside the spriteset: + +```c +TLN_SetSpritePicture (0, 3); +``` + +Sometimes it's convenient to use the graphic name instead of the index, because the latter can be difficult to know. To get the index of a given name, call \ref TLN_FindSpritesetSprite. This will return the graphic index (staring from 0), or -1 if not found: + +```C +int index = TLN_FindSpritesetSprite(spriteset, "ship4"); +if (index != -1) + TLN_SetSpritePicture(0, index); +``` + +This searches de index of graphic with name "ship4" inside spriteset, and if found, assigns it to sprite 0. + +## Pivot position + +By default the pivot position of the sprite is located at its top-left corner. This pivot point is the "anchor" that determines the exact location when positioning, and the scaling origin. Depending on gameplay it's more natural to have the pivot on another position. For example in games with pseudo-3D graphics, like 3/4 perspective or 3D roads, it's more natural to have the pivot at the bottom, at the "floor" level. + +This can be accomplished with the \ref TLN_SetSpritePivot function, that takes normalized coordinates in the range 0.0 (top/left) and 1.0 (bottom/right). + +For example to set sprite 3 pivot to 50% horizontal and full bottom: + +```C +TLN_SetSpritePivot (3, 0.5f, 1.0f); +``` + +![Sprite pivot](img/sprite_pivot.png) + +* left image: pivot by default at 0,0 (top left) +* right image: pivot at 0.5, 1.0 + +## Moving around +To move the sprite to a different location, call the \ref TLN_SetSpritePosition, passing the sprite index, and the x,y coordinates. These values are absolute screen coordinates. For example to move sprite 3 to 160,120: +```c +TLN_SetSpritePosition (3, 160,120); +``` +## Special attributes +There are some special modifiers that control sprite flipping, priority and masking. Sprite flipping allows to draw a sprite upside down and/or horizontally mirrored. For example a platformer game just needs to have sprites drawn facing to the right, when character need to walk to the left, just set the horizontal flipping flag. + +**Priority** will draw the sprite in front of priority layers, instead of behind them. To set attributes, call \ref TLN_EnableSpriteFlag passing the sprite index and a combination of \ref TLN_TileFlags. For example to draw sprite 0 upside down: + +```c +TLN_EnableSpriteFlag (0, FLAG_FLIPY); +``` +Flipping modes: a) 0, b) FLAG_FLIPX, c) FLAG_FLIPY, d) FLAG_FLIPX + FLAG_FLIPY: + +![Flipping modes](img/sprite_flags.png) + +**Masking** will mark the sprite as affected by mask region if flagged with \ref FLAG_MASKED. Read "sprite masking" section below to know more about masking. + +## Setting the palette +By default, a sprite is assigned the associated palette of its spriteset, but this can be changed calling \ref TLN_SetSpritePalette passing the sprite index and a \ref TLN_Palette reference: + +```c +TLN_Palette palette = TLN_LoadPalette ("palette.act") +TLN_SetSpritePalette (0, palette); +``` + +## Blending +Blending is supported in sprites, with different modes and effects. To get extended information about the modes and their effects, please refer to [Blending section](blending.md). + +To enable blending, call \ref TLN_SetSpriteBlendMode passing the sprite index and the blending mode. For example, to set 50%/50% blending in sprite 0: +```c +TLN_SetSpriteBlendMode (0, BLEND_MIX, 0); +``` +The last parameter, *factor*, is kept for compatibility but isn't used. + +To disable blending, call the same function with blending mode set to BLEND_NONE : +```c +TLN_SetSpriteBlendMode (0, BLEND_NONE, 0); +``` + +## Scaling +Sprites can be drawn upscaled or downscaled with an arbitrary factor. To enable scaling, call \ref TLN_SetSpriteScaling passing the sprite index and two floating point values with the horizontal and vertical factor, respectively. Values greater than 1.0 upscale, and smaller than 1.0 downscale. For example to set an horizontal downscaling of 0.5 and vertical upscaling of 1.5 for layer 0: +```c +TLN_SetSpriteScaling (0, 0.5f, 1.5f); +``` +Different sprite scaling examples: + +![Different sprite scaling examples](img/sprite_scaling.png) + +To disable scaling, call \ref TLN_ResetSpriteScaling passing the sprite index. For example to disable scaling in sprite 0: +```c +TLN_ResetSpriteScaling (0); +``` + +## Collision detection +A basic action on any game is checking if two given sprites collide. For example, if our hero is hit by any enemy bullet. A quick way to determine a collision is to check if their bounding boxes overlap (a *bounding box* is the rectangular area that fully encloses a sprite). This method is fast and easy to implement, but sometimes the bounding boxes of two sprites can overlap, but in regions where there aren't solid pixels, just transparent ones. In this case, you see that the bullet isn't going to hit your hero, but it gets actually hit without touching it. A common solution is to use bounding boxes that are *smaller* than the sprite, but this can have the opposite effect: missing collisions that actually happen. + +To solve this, Tilengine implements pixel-based collision detection. With this feature enabled, you know that a sprite gets involved in a collision only if there are actual non-transparent pixels in both sprites overlapping. But this method also has a limitation: it can tell you that a given sprite is per-pixel colliding with another sprite, but you don't know with *which* sprite. + +The final solution consists in combining both methods as they compliment each other: first determine coarse collision with bounding boxes, and then check per-pixel collision detection in those sprites. + +Per-pixel collision detection requires more CPU cycles that regular sprites, so it's an optional feature that is disabled by default. You can enable it for each sprite calling \ref TLN_EnableSpriteCollision passing the sprite index and a boolean value with *true* to enable or *false* to disable the feature. For example, to enable collision detection for sprite 0 and disable it for sprite 3: +```c +TLN_EnableSpriteCollision (0, true); +TLN_EnableSpriteCollision (3, false); +``` +To query the collision status for a given sprite, call \ref TLN_GetSpriteCollision passing the sprite index to check. It returns a boolean value indicating the collision status: +```c +bool collision = TLN_GetSpriteCollision (0); +``` + +## Sprite drawing order + +By default, each sprite activated is added to the end of a list of sprites that are drawn from first to last, following [painter's algorithm](https://en.wikipedia.org/wiki/Painter%27s_algorithm). That means dat sprites added later will overlap the ones added first. For example if sprites 0, 1, 2, 3 are added in sequence: + +``` +0 -> 1 -> 2 -> 3 +``` + +Sprite 0 will be drawn first, sprite 3 will be drawn last, overlapping the others. + +This order can be changed with \ref TLN_SetFirstSprite and \ref TLN_SetNextSprite functions. + +To set the first sprite in the list, call \ref TLN_SetFirstSprite passing the index of first sprite. In the above example, to set 2 at the beginning: + +```C +TLN_SetFirstSprite(2); +``` + +The list becomes: + +```C +2 -> 0 -> 1 -> 3 +``` + +To change any other sprite, call \ref TLN_SetNextSprite, passing the current sprite, and which one goes next. Taking the previous list, to move sprite 0 from its position to be drawn after sprite 3: + +```C +TLN_SetNextSprite(3, 0); +``` + +The list becomes: + + +```C +2 -> 1 -> 3 -> 0 +``` + +Now sprite 0 overlaps sprite 3 + +## Sprite masking + +Sprite masking allows defining a rectangular region that spans the whole frame width, where selected sprites won't be drawn when they cross this region. + +To define the masking area, call \ref TLN_SetSpritesMaskRegion, passing the bottom and top scanlines that define the exclusion area. For example to create a masking area that goes from y = 120 to y = 160, call: + +```C +TLN_SetSpritesMaskRegion(120, 160); +``` + +To disable masking region, pass 0, 0: + +```C +TLN_SetSpritesMaskRegion(0, 0); +``` + +Only sprites flagged with TLN_MASKED flag will disappear inside the mask region. Use \ref TLN_EnableSpriteFlag to enable or disable FLAG_MASKED flag. + +## Animation +Although it is possible to animate a sprite manually using the \ref TLN_SetSpritePicture function at timed intervals, tilengine has built-in animation support. To animate a sprite it is necessary to have a \ref TLN_Sequence object describing the animation. See the chapter [Sequences](sequences.md) to see how to create a \ref TLN_Sequence object from a \ref TLN_Spriteset object. + +For example, assuming a spriteset called `spriteset`, containing frames numbered sequentially from `walk1` to `walk8`, you can create the sequence with these frames, with a cadence of 6 fps at 60 Hz: + +```c +TLN_Sequence walk_sequence = TLN_CreateSpriteSequence ("walking", spriteset, "walk", 60/6); +``` + +Once created, set the animation to the sprite with \ref TLN_SetSpriteAnimation, indicating the index of the sprite, the \ref TLN_Sequence object, and the number of times the animation should loop, indicating 0 for it to repeat indefinitely. For example, to animate sprite 0 with the `walk_sequence` previously created and to be repeated continuously: + +```c +TLN_SetSpriteAnimation (0, walk_sequence, 0); +``` + +**NOTE**: the sprite must have been previously assigned with the same spriteset used to create the sequence. + +To finish playing an ongoing animation, call \ref TLN_DisableSpriteAnimation passing the index of the sprite to stop animation: + +```c +TLN_DisableSpriteAnimation(0); +``` + +## Disabling +To disable a sprite so it is not rendered, just call \ref TLN_DisableSprite passing the sprite index: +```c +TLN_DisableSprite (0); +``` + +## Summary +This is a quick reference of related functions in this chapter: + +|Function | Quick description +|--------------------------------|------------------------------------- +|\ref TLN_SetSpriteSet |Assigns the spriteset and its palette to a given sprite +|\ref TLN_EnableSpriteFlag |Sets flags for a given sprite +|\ref TLN_SetSpritePosition |Sets the sprite position inside the viewport +|\ref TLN_SetSpritePivot |Sets the pivot of the sprite +|\ref TLN_SetSpritePicture |Sets the actual graphic to the sprite +|\ref TLN_SetSpritePalette |Assigns a palette to a sprite +|\ref TLN_SetSpriteBlendMode |Sets the blending mode (transparency effect) +|\ref TLN_SetSpriteScaling |Sets the scaling factor of the sprite +|\ref TLN_ResetSpriteScaling |Disables scaling for a given sprite +|\ref TLN_GetSpritePicture |Returns the index of the assigned picture from the spriteset +|\ref TLN_GetAvailableSprite |Returns the first available (unused) sprite +|\ref TLN_EnableSpriteCollision |Enable sprite collision checking at pixel level +|\ref TLN_GetSpriteCollision |Gets the collision status of a given sprite +|\ref TLN_SetSpritesMaskRegion |Defines masking region to hide FLAG_MASKED sprites +|\ref TLN_SetSpriteAnimation |Starts a sprite animation +|\ref TLN_DisableSpriteAnimation |Disables animation of sprite +|\ref TLN_GetSpritePalette |Returns the current palette of a sprite +|\ref TLN_DisableSprite |Disables the sprite so it is not drawn diff --git a/docs/10_tilesets.md b/docs/spritesets.md similarity index 79% rename from docs/10_tilesets.md rename to docs/spritesets.md index 39d958b7..33c89d11 100644 --- a/docs/10_tilesets.md +++ b/docs/spritesets.md @@ -1,4 +1,4 @@ -# Tilesets +# Spritesets [TOC] @@ -6,10 +6,9 @@ ## Create at runtime -## Setting pixel data +## Getting sprite info ## Delete ## Summary This is a quick reference of related functions in this chapter: - diff --git a/docs/03_starting.md b/docs/starting.md similarity index 93% rename from docs/03_starting.md rename to docs/starting.md index feffb183..4b048d58 100644 --- a/docs/03_starting.md +++ b/docs/starting.md @@ -22,19 +22,19 @@ To set a solid color there is the function \ref TLN_SetBGColor that takes the th TLN_SetBGColor (0,32,96); ``` -It is also possible to set the background color defined inside a tilemap object with the \ref TLN_SetBGColorFromTilemap function. Tilemaps may specify a default background color that can be used here. To see how to load and manipulate tilemaps, please refer to [chapter 10](11_tilemaps.md). For now, to load a tilemap called "tilemap.tmx" and use its default background color, you have to do the following: +It is also possible to set the background color defined inside a tilemap object with the \ref TLN_SetBGColorFromTilemap function. Tilemaps may specify a default background color that can be used here. To see how to load and manipulate tilemaps, please refer to [Tilemaps section](tilemaps.md). For now, to load a tilemap called "tilemap.tmx" and use its default background color, you have to do the following: ```c TLN_Tilemap tilemap = TLN_LoadTilemap ("tilemap.tmx"); TLN_SetBGColorFromTilemap (tilemap); ``` ### Bitmap background -To set a bitmap, there is the function \ref TLN_SetBGBitmap that takes the \ref TLN_Bitmap reference of a loaded bitmap. To see how to load an manipulate bitmaps, please refer to [chapter 14](15_bitmaps.md). For now, to load a bitmap called "Background.png" and set it as the background, you have to do the following: +To set a bitmap, there is the function \ref TLN_SetBGBitmap that takes the \ref TLN_Bitmap reference of a loaded bitmap. To see how to load an manipulate bitmaps, please refer to [Bitmaps section](bitmaps.md). For now, to load a bitmap called "Background.png" and set it as the background, you have to do the following: ```c TLN_Bitmap background = TLN_LoadBitmap ("Background.png"); TLN_SetBGBitmap (background); ``` -It's possible to change the default palette provided by the bitmap. To do so, use the \ref TLN_SetBGPalette function that takes a \ref TLN_Palette object. To see how to load and manipulate palettes, please refer to [chapter 12](13_palettes.md). Assuming you have an alternative palette file called "Background.act", do the following to set it: +It's possible to change the default palette provided by the bitmap. To do so, use the \ref TLN_SetBGPalette function that takes a \ref TLN_Palette object. To see how to load and manipulate palettes, please refer to [Palettes section](palettes.md). Assuming you have an alternative palette file called "Background.act", do the following to set it: ```c TLN_Palette palette = TLN_LoadPalette ("Background.act"); TLN_SetBGPalette (palette); diff --git a/docs/11_tilemaps.md b/docs/tilemaps.md similarity index 100% rename from docs/11_tilemaps.md rename to docs/tilemaps.md diff --git a/docs/tilesets.md b/docs/tilesets.md new file mode 100644 index 00000000..1ab7d77a --- /dev/null +++ b/docs/tilesets.md @@ -0,0 +1,21 @@ +# Tilesets + +[TOC] + +## Types of tilesets + +### Tile-based tileset + +### Image-based tileset + +## Load from file + +## Create at runtime + +## Setting pixel data + +## Delete + +## Summary +This is a quick reference of related functions in this chapter: + diff --git a/docs/04_windowing.md b/docs/windowing.md similarity index 83% rename from docs/04_windowing.md rename to docs/windowing.md index a568d537..33e3f7db 100644 --- a/docs/04_windowing.md +++ b/docs/windowing.md @@ -20,31 +20,29 @@ Supported creation flags are as follows: |Flag value |Effect |--------------|---------------------------- -|CWF_FULLSCREEN|create a fullscreen window +|CWF_FULLSCREEN|create a full-screen window |CWF_VSYNC |sync frame updates with vertical retrace |CWF_Sn |force integer upscale factor (n is 1-5) -|CWF_NEAREST |sharp upscaling without bilinear filter +|CWF_NEAREST |start with CRT/RF effect disabled The following key combinations are used to control the window: |Key |Effect |----------------------|------------------------------------- -|Alt + Enter|Toggle windowed/fullscreen mode +|Alt + Enter|Toggle windowed/full-screen mode |Backspace |Toggle CRT/RF effect or on off |Escape |Close the window -Once the window is created, it must be processed calling \ref TLN_ProcessWindow for each frame. This function returns true while the window is alive, or false when the user has requested to terminate it. Draw the frames with \ref TLN_DrawFrame. This function takes an optional integer value, that represents a timestamp used by the animation engine. If there are not animations it can be left as 0. +Once the window is created, it must be processed calling \ref TLN_ProcessWindow for each frame. This function returns true while the window is alive, or false when the user has requested to terminate it. Draw the frames with \ref TLN_DrawFrame. This function takes an optional integer value, that represents a frame counter used by the animation engine. This basic sample show how to initialize the engine, create the window and do the window loop until the user requests to exit: + ```c -int frame = 0; TLN_Init (400,240, 2,8,0); /* init the engine */ TLN_CreateWindow (NULL, 0); /* create the window */ while (TLN_ProcessWindow()) /* the window loop */ -{ - TLN_DrawFrame (frame); /* draw next frame */ - frame += 1; -} + TLN_DrawFrame (0); /* draw next frame */ + TLN_Deinit (); /* release resources */ ``` @@ -56,9 +54,8 @@ This is the same sample with the multi-threaded window: TLN_Init (400,240, 2,8,0); /* init the engine */ TLN_CreateWindowThread (NULL, 0); /* create the window */ while (TLN_IsWindowActive()) /* check window state */ -{ TLN_WaitRedraw (); /* optionally sync to window drawing for fps control */ -} + TLN_Deinit (); /* release resources */ ``` @@ -114,7 +111,7 @@ if (TLN_GetInput(INPUT_P2 + INPUT_START)) ``` ### Enhanced input with SDL2 -If the built-in input layout of tilengine is not enough, because your game needs mouse input, analog sticks, more keys, etc, Tilengine can catch the events delivered by the underlying SDL2 library and pass them to your own input handling. This is done by a user callback, that can be registered with \ref TLN_SetSDLCallback function. The user callback must have the format `void sdl_callback(SDL_Event*);`. For example, to check mouse click: +If the built-in input layout of tilengine is not enough, because your game needs mouse input, analog sticks, more keys, etc. Tilengine can catch the events delivered by the underlying SDL2 library and pass them to your own input handling. This is done by a user callback, that can be registered with \ref TLN_SetSDLCallback function. The user callback must have the format `void sdl_callback(SDL_Event*);`. For example, to check mouse click: ```c #include ... @@ -148,10 +145,22 @@ The same output but with default CRT enabled:
By default the CRT effect is enabled when the window is created, but it can be disabled with the \ref TLN_DisableCRTEffect function. It can be toggled pressing the Escape key, too. -The effect is highly configurable with the \ref TLN_EnableCRTEffect function. It takes many parameters to customize its appearance and strength. The default parameters are: -```c -TLN_EnableCRTEffect (TLN_OVERLAY_APERTURE, 128, 192, 0,64, 64,128, false, 255); -``` +There are some variations of the effect. There's a choice between three types of rgb subpixels, and optional RF blur. To configure or enable the effect, use the function \ref TLN_ConfigCRTEffect. + +This function takes two parameters: + +* type of RGB emulation +* optional RF (horizontal) blur + +Possible types of RGB emulation are the following constants of \ref TLN_CRT enum: + +|Value |Effect +|-----------------|------------------------------------------------------ +|TLN_CRT_SLOT |Slot mask without scanlines, similar to legacy (pre 2.10) effect +|TLN_CRT_APERTURE |Aperture grille with scanlines, matrix-like dot arrangement. Similar to arcade monitors +|TLN_CRT_SHADOW |Shadow mask with scanlines, diagonal subpixel arrangement. Similar to TV sets + +The optional `blur` parameter applies an horizontal blur that blends adjacent pixels. This blur on actual systems was extensively exploited by Sega megadrive/genesis developers, that interleaved vertical strips of different color to simulate more colors and/or transparency blending, knowing that the discrete colors would be mixed on the RF signal. ## Summary This is a quick reference of related functions in this chapter: @@ -171,7 +180,7 @@ This is a quick reference of related functions in this chapter: |\ref TLN_DrawFrame |Renders a new frame to the window |\ref TLN_WaitRedraw |Syncs to redraw in multi-threaded window |\ref TLN_DeleteWindow |Destroys the window -|\ref TLN_EnableCRTEffect |Configures the CRT/RF video effect +|\ref TLN_ConfigCRTEffect |Configures the CRT/RF video effect |\ref TLN_DisableCRTEffect |Disables the CRT/RF video effect |\ref TLN_SetSDLCallback |Sets function to call when SDL input events happen |\ref TLN_Delay |Waits for the specified amount of milliseconds diff --git a/docs/world.md b/docs/world.md new file mode 100644 index 00000000..0edf9e0c --- /dev/null +++ b/docs/world.md @@ -0,0 +1,107 @@ +# World usage + +Release 2.9.0 introduces the world mode. In previous versions, each layer and sprite coordinates work in screen space. In "classic" mode, each layer must be loaded and set up independently. Scrolling requires manually repositioning all layers and sprites on each update. + +In world mode, selected layers and sprites are tied to global "world" position, so when the world position is updated, all layers and sprites are updated accordingly. + +## Load + +World mode starts by loading a full tmx file with \ref TLN_LoadWorld, specifying the name of the tmx file, and the index of the starting layer that will hold all the layers contained in the tmx file. This index is useful to reserve top-most layers that won't be affected by world position, mainly the HUD + +These are the Tiled features loaded: + +* background color +* all types of layers: tile, bitmap and object +* all related resources (tilemaps, tilesets and bitmaps) +* layer parallax factor +* layer offset +* layer opacity rounded to 0%, 25%, 50%, 75% or 100% +* layer visible state + +![Level open in Tiled](img/world_tiled.png) + +Load and setup the map file `assets/forest/map.tmx` that contains a background color, four parallax layers (an objects one, a tiled one, and two bitmapped), leaving layer 0 for the HUD: + +```C +TLN_SetLoadPath("assets/forest"); +TLN_LoadWorld("map.tmx", 1); +``` + +![Level loaded](img/world_layers.png) + +Layers 1 to 4 are assigned with the layers inside the tmx file: + +* layer 0 is not assigned to the world (reserved for HUD) +* layer 1 has "Object Layer" with objects/props +* layer 2 has "Main Layer" with foreground tiled layer +* layer 3 has "middle" with middle-ground bitmap +* layer 4 has "bottom" with background bitmap + +## Setting sprite position + +Positioning sprites is done with \ref TLN_SetSpriteWorldPosition, that takes the sprite index, and the horizontal and vertical coordinates. This function behaves like the classic \ref TLN_SetSpritePosition, but coordinates are in world space instead of screen space. When world position is updated, location of sprite in screen will be updated accordingly. + +Set sprite 3 at x=2400, y=50 in world space: + +```C +TLN_SetSpriteWorldPosition(3, 2400, 50); +``` + +**NOTE**: once a sprite is set in world space, to revert it to classic screen space it must be disabled with \ref TLN_DisableSprite and re-assigned with \ref TLN_ConfigSprite again. + +## Scrolling + +Moving the global world position is done with \ref TLN_SetWorldPosition, that takes the x and y coordinates in world space. For example to move to x=2200 and y=0: + +```C +TLN_SetWorldPosition(2200, 0); +``` + +When the world position is updated: + +* layer positions are updated according to their parallax and offset factors +* sprite screen positions are updated according to their world position + +![scrolling](img/world_scroll.gif) + +## Layer parallax factor + +Parallax factor for each layer is obtained from the .tmx file. However it may be useful to set it at runtime to use in raster effects, modifying it a select scanlines to implement multiple strips or linescroll. Use \ref TLN_SetLayerParallaxFactor setting the x and y factor in floating point format. + +* 1.0 will make the layer scroll at the same speed than the world (layer position = world position) +* < 1.0 values will make the layer scroll slower than the world (layer position < world position) +* > 1.0 values will make the layer scroll faster than the world (layer position > world position) + +For example to set layer 2 scroll factor to x=0.5, y=0: + +```C +TLN_SetLayerParallaxFactor(2, 0.5f, 0); +``` + +This will make layer 2 to scroll at half speed than world position horizontally, and keep fixed vertically + +## Release world resources + +When you're done working with the loaded level, release all its resources with \ref TLN_ReleaseWorld. It will: + +* disable all used layers +* release all loaded assets + +## Full example + +`samples/Forest` example showcases world mode for both layers and sprites. + +Graphic assets produced by **Ansimuz**:
+https://ansimuz.itch.io/sunnyland-forest + +## Summary + +This is a quick reference of related functions in this chapter: + +|Function | Quick description +|--------------------------------|------------------------------------- +|\ref TLN_LoadWorld | Loads full tmx file +|\ref TLN_SetSpriteWorldPosition | Sets sprite position in world space +|\ref TLN_SetWorldPosition | Sets current world position +|\ref TLN_SetLayerParallaxFactor | Overrides default layer parallax factor +|\ref TLN_ReleaseWorld | Releases world resources diff --git a/include/Tilengine.h b/include/Tilengine.h index 226b32ab..f364be79 100644 --- a/include/Tilengine.h +++ b/include/Tilengine.h @@ -12,8 +12,7 @@ #define _TILENGINE_H /** - * \defgroup types - * \brief Common data types + * \defgroup types Common data types * @{ */ /* Tilengine shared */ @@ -59,8 +58,8 @@ /* version */ #define TILENGINE_VER_MAJ 2 -#define TILENGINE_VER_MIN 4 -#define TILENGINE_VER_REV 1 +#define TILENGINE_VER_MIN 16 +#define TILENGINE_VER_REV 0 #define TILENGINE_HEADER_VERSION ((TILENGINE_VER_MAJ << 16) | (TILENGINE_VER_MIN << 8) | TILENGINE_VER_REV) #define BITVAL(n) (1<<(n)) @@ -73,17 +72,12 @@ typedef enum FLAG_FLIPY = BITVAL(14), /*!< vertical flip */ FLAG_ROTATE = BITVAL(13), /*!< row/column flip (unsupported, Tiled compatibility) */ FLAG_PRIORITY = BITVAL(12), /*!< tile goes in front of sprite layer */ + FLAG_MASKED = BITVAL(11), /*!< sprite won't be drawn inside masked region */ + FLAG_TILESET = (15 << 7), /*!< tileset index (0 - 15) */ + FLAG_PALETTE = (7 << 4), /*!< palette index (0 - 7) */ } TLN_TileFlags; -/* fixed point helper */ -typedef int fix_t; -#define FIXED_BITS 16 -#define float2fix(f) (fix_t)(f*(1 << FIXED_BITS)) -#define int2fix(i) ((int)(i) << FIXED_BITS) -#define fix2int(f) ((int)(f) >> FIXED_BITS) -#define fix2float(f) (float)(f)/(1 << FIXED_BITS) - /*! * layer blend modes. Must be one of these and are mutually exclusive: */ @@ -102,6 +96,18 @@ typedef enum } TLN_Blend; +/*! + * layer type retrieved by \ref TLN_GetLayerType + */ +typedef enum +{ + LAYER_NONE, /*!< undefined */ + LAYER_TILE, /*!< tilemap-based layer */ + LAYER_OBJECT, /*!< objects layer */ + LAYER_BITMAP, /*!< bitmapped layer */ +} +TLN_LayerType; + /*! Affine transformation parameters */ typedef struct { @@ -120,7 +126,25 @@ typedef union Tile struct { uint16_t index; /*!< tile index */ - uint16_t flags; /*!< attributes (FLAG_FLIPX, FLAG_FLIPY, FLAG_PRIORITY) */ + union + { + uint16_t flags; /*!< attributes (FLAG_FLIPX, FLAG_FLIPY, FLAG_PRIORITY) */ + struct + { + uint8_t unused : 4; + uint8_t palette : 3; + uint8_t tileset : 4; + + // COMPILER ERROR: although whole struct fits in 32 bits, compiler expands to 64, causing addressing errors + /* + bool masked : 1; + bool priority : 1; + bool rotated : 1; + bool flipy : 1; + bool flipx : 1; + */ + }; + }; }; } Tile; @@ -185,6 +209,22 @@ typedef struct } TLN_TileInfo; +/*! Object item info returned by TLN_GetObjectInfo() */ +typedef struct +{ + uint16_t id; /*!< unique ID */ + uint16_t gid; /*!< graphic ID (tile index) */ + uint16_t flags; /*!< attributes (FLAG_FLIPX, FLAG_FLIPY, FLAG_PRIORITY) */ + int x; /*!< horizontal position */ + int y; /*!< vertical position */ + int width; /*!< horizontal size */ + int height; /*!< vertical size */ + uint8_t type; /*!< type property */ + bool visible; /*!< visible property */ + char name[64]; /*!< name property */ +} +TLN_ObjectInfo; + /*! Tileset attributes for TLN_CreateTileset() */ typedef struct { @@ -193,23 +233,29 @@ typedef struct } TLN_TileAttributes; -/*! ObjectList item for TLN_CreateObjectList() */ -typedef struct +/* kept for backwards compatibility with pre-2.10 release */ +#define TLN_OVERLAY_NONE 0 +#define TLN_OVERLAY_SHADOWMASK 0 +#define TLN_OVERLAY_APERTURE 0 +#define TLN_OVERLAY_SCANLINES 0 +#define TLN_OVERLAY_CUSTOM 0 + +/*! types of built-in CRT effect for \ref TLN_ConfigCRTEffect */ +typedef enum { - int id; /*!< object unique identifier */ - int gid; /*!< graphic identifier (tile index in Tiled) */ - int x; - int y; - int width; - int height; + TLN_CRT_NONE, /*!< no overlay */ + TLN_CRT_SLOT, /*!< slot mask */ + TLN_CRT_APERTURE, /*!< aperture grille */ + TLN_CRT_SHADOW, /*!< shadow mask */ + TLN_CRT_HORIZONTAL, /*!< horizontal subpixels */ } -TLN_Object; +TLN_CRT; /*! pixel mapping for TLN_SetLayerPixelMapping() */ typedef struct { - int16_t dx; /*! horizontal pixel displacement */ - int16_t dy; /*! vertical pixel displacement */ + int16_t dx; /*!< horizontal pixel displacement */ + int16_t dy; /*!< vertical pixel displacement */ } TLN_PixelMap; @@ -224,6 +270,15 @@ typedef struct SequencePack* TLN_SequencePack; /*!< Opaque sequence pack refere typedef struct Bitmap* TLN_Bitmap; /*!< Opaque bitmap reference */ typedef struct ObjectList* TLN_ObjectList; /*!< Opaque object list reference */ +/*! Image Tile items for TLN_CreateImageTileset() */ +typedef struct +{ + TLN_Bitmap bitmap; + uint16_t id; + uint8_t type; +} +TLN_TileImage; + /*! Sprite state */ typedef struct { @@ -231,7 +286,7 @@ typedef struct int y; /*!< Screen position y */ int w; /*!< Actual width in screen (after scaling) */ int h; /*!< Actual height in screen (after scaling) */ - TLN_TileFlags flags; /*!< flags */ + uint32_t flags; /*!< flags */ TLN_Palette palette; /*!< assigned palette */ TLN_Spriteset spriteset; /*!< assigned spriteset */ int index; /*!< graphic index inside spriteset */ @@ -241,8 +296,25 @@ typedef struct TLN_SpriteState; /* callbacks */ +typedef union SDL_Event SDL_Event; typedef void(*TLN_VideoCallback)(int scanline); typedef uint8_t(*TLN_BlendFunction)(uint8_t src, uint8_t dst); +typedef void(*TLN_SDLCallback)(SDL_Event*); +typedef void(*TLN_TaskCallback)(uint32_t frame); + +/*! CreateWindow flags. Can be none or a combination of the following: */ +enum +{ + CWF_FULLSCREEN = (1 << 0), /*!< create a fullscreen window */ + CWF_VSYNC = (1 << 1), /*!< sync frame updates with vertical retrace */ + CWF_S1 = (1 << 2), /*!< create a window the same size as the framebuffer */ + CWF_S2 = (2 << 2), /*!< create a window 2x the size the framebuffer */ + CWF_S3 = (3 << 2), /*!< create a window 3x the size the framebuffer */ + CWF_S4 = (4 << 2), /*!< create a window 4x the size the framebuffer */ + CWF_S5 = (5 << 2), /*!< create a window 5x the size the framebuffer */ + CWF_NEAREST = (1 << 6), /* major version + bits 15: 8 -> minor version + bits 7: 0 -> bugfix revision + \remarks Compare this number with the TILENGINE_HEADER_VERSION macro to check that both versions match! +*/ TLNAPI uint32_t TLN_GetVersion (void); + +/*! + \brief Returns the number of layers specified during initialisation + \see TLN_Init() +*/ TLNAPI int TLN_GetNumLayers (void); + +/*! + \brief Returns the number of sprites specified during initialisation + \see TLN_Init() +*/ TLNAPI int TLN_GetNumSprites (void); + +/*! + \brief Sets the background color + \param r red component (0-255) + \param g green component (0-255) + \param b blue component (0-255) + + The background color is the color of the pixel when there isn't any layer or sprite at + that position. + + \remarks This funcion can be called during a raster callback to create gradient backgrounds +*/ TLNAPI void TLN_SetBGColor (uint8_t r, uint8_t g, uint8_t b); + +/*! + \brief Sets the background color from a Tilemap defined color + \param tilemap Reference to the tilemap with the background color to set +*/ TLNAPI bool TLN_SetBGColorFromTilemap (TLN_Tilemap tilemap); + +/*! + \brief Disales background color rendering. If you know that the last background layer will always cover the entire screen, you can disable it to gain some performance + \see TLN_SetBGColor() +*/ TLNAPI void TLN_DisableBGColor (void); + +/*! + \brief Sets a static bitmap as background + \param bitmap Reference to bitmap for the background. Set NULL to disable + + Sets an optional bitmap instead of a solid color where there is no layer or sprite. + Unlike tilemaps or sprites, this bitmap cannot be moved and has no transparency + + \see TLN_SetBGPalette() +*/ TLNAPI bool TLN_SetBGBitmap (TLN_Bitmap bitmap); + +/*! + \brief Changes the palette for the background bitmap + \param palette Reference to palette + \see TLN_SetBGBitmap() +*/ TLNAPI bool TLN_SetBGPalette (TLN_Palette palette); -TLNAPI void TLN_SetRasterCallback (TLN_VideoCallback); -TLNAPI void TLN_SetFrameCallback (TLN_VideoCallback); + +/* \brief Sets one of the eight global palettes used by tiled layers + \param index Palette index [0 - 7] + \param palette Reference of palette to set, or NULL to disable it + \returns true if success, or false if error + \see TLN_GetGlobalPalette() +*/ +TLNAPI bool TLN_SetGlobalPalette(int index, TLN_Palette palette); + +/* + \brief Returns one of the eight global palettes + \param index Index of global palette to query [0 - 7] + \returns TLN_Palette reference or NULL if not set + \see TLN_SetGlobalPalette +*/ +TLNAPI TLN_Palette TLN_GetGlobalPalette(int index); + +/*! + \brief Specifies the address of the funcion to call for each drawn scanline + \param callback Address of the function to call + + Tilengine renders its output line by line, just as the 2D graphics chips did. The + raster callback is a way to simulate the "horizontal blanking interrupt" of those systems, + where many parameters of the rendering can be modified per line. + + \remarks Setting a raster callback is optional, but much of the fun of using Tilengine comes from the use of raster effects +*/ +TLNAPI void TLN_SetRasterCallback (TLN_VideoCallback callback); + +/*! + \brief Specifies the address of the funcion to call for each drawn frame + \param callback Address of the function to call +*/ +TLNAPI void TLN_SetFrameCallback (TLN_VideoCallback callback); + +/*! + \brief Sets the output surface for rendering + \param data Pointer to the start of the target framebuffer + \param pitch Number of bytes per each scanline of the framebuffer + + Sets the output surface for rendering. Tilengine doesn't provide a windowing or hardware + video access. The application is responsible of allocating and maintaining the surface where + tilengine does the rendering. It can be a SDL surface, a locked DirectX surface, an OpenGL texture, + or whatever the application has access to. + + \remarks The render target pixel format must be 32 bits RGBA + \see TLN_UpdateFrame() +*/ TLNAPI void TLN_SetRenderTarget (uint8_t* data, int pitch); -TLNAPI void TLN_UpdateFrame (int time); -TLNAPI void TLN_BeginFrame (int time); -TLNAPI bool TLN_DrawNextScanline (void); + +/*! + \brief Draws the frame to the previously specified render target + \param frame Optional frame number. Set to 0 to autoincrement from previous value + \see TLN_SetRenderTarget() +*/ +TLNAPI void TLN_UpdateFrame (int frame); + +/*! + \brief Sets base path for TLN_LoadXXX functions. + \param path Base path. Files will load at path/filename. Can be NULL +*/ TLNAPI void TLN_SetLoadPath (const char* path); -TLNAPI void TLN_SetCustomBlendFunction (TLN_BlendFunction); + +/*! + \brief Sets custom blend function to use when BLEND_CUSTOM mode is selected + \param blend_function pointer to a user-provided function that takes two parameters: source component intensity, destination component intensity, and returns the desired intensity. This function is called for each RGB component when blending is enabled + \remarks This function is not called in realtime, but its result is precomputed into a look-up table when TLN_SetCustomBlendFunction() is called, so the performance impact is minimal, just as low as the other built-in blending modes + \see TLN_SetSpriteBlendMode()|TLN_SetLayerBlendMode() +*/ +TLNAPI void TLN_SetCustomBlendFunction (TLN_BlendFunction blend_function); +/*! + \brief Sets logging level for current instance + \param log_level value to set, member of the TLN_LogLevel enumeration +*/ TLNAPI void TLN_SetLogLevel(TLN_LogLevel log_level); + +/*! + \brief Open the resource package with optional aes-128 key and binds it + \param filename file with the resource package (.dat extension) + \param key optional null-terminated ASCII string with aes decryption key + \return true if package opened and made current, or false if error + \remarks + When the package is opened, it's globally bind to all TLN_LoadXXX functions. + The assets inside the package are indexed with their original path/file as when + they were plain files. As long as the structure used to build the package + matches the original structure of the assets, the TLN_SetLoadPath() and the TLN_LoadXXX + functions will work transparently, easing the migration with minimal changes. + \sa TLN_CloseResourcePack +*/ TLNAPI bool TLN_OpenResourcePack(const char* filename, const char* key); + +/*! + \brief Closes current resource package and unbinds it + \sa TLN_OpenResourcePack +*/ TLNAPI void TLN_CloseResourcePack(void); /*! Player index for input checking */ @@ -348,203 +626,1649 @@ TLN_Input; TLNAPI void SetFrameCallbackName(const char* name); TLNAPI void SetRasterCallbackName(const char* name); TLNAPI bool CheckRetroInput(TLN_Player port, TLN_Input input); + /**@}*/ /** - * \defgroup errors - * \brief Basic setup and management + * \defgroup errors Basic setup and management * @{ */ + +/*! + \brief Sets the global error code of tilengine. Useful for custom loaders that need to set the error state. + \param error Error code to set + \see TLN_GetLastError() +*/ TLNAPI void TLN_SetLastError (TLN_Error error); + +/*! + \brief Returns the last error after an invalid operation + \see TLN_Error +*/ TLNAPI TLN_Error TLN_GetLastError (void); + +/*! + \brief Returns the string description of the specified error code + \param error Error code to get description + \see TLN_GetLastError() +*/ TLNAPI const char *TLN_GetErrorString (TLN_Error error); /**@}*/ /** - * \defgroup spriteset - * \brief Spriteset resources management for sprites + * \defgroup windowing Built-in window and input management * @{ */ + +/*! + \brief Creates a window for rendering + \param overlay Deprecated parameter in 2.10, kept for compatibility. Set to NULL + \param flags Mask of the possible creation flags: CWF_FULLSCREEN, CWF_VSYNC, CWF_S1 - CWF_S5 (scaling factor, none = auto max) + \returns True if window was created or false if error + + Creates a host window with basic user input for tilengine. If fullscreen, it uses the desktop + resolution and stretches the output resolution with aspect correction, letterboxing or pillarboxing + as needed. If windowed, it creates a centered window that is the maximum possible integer multiply of + the resolution configured at TLN_Init() + + \remarks + Using this feature is optional, Tilengine is designed to output its rendering to a user-provided surface + so it can be used as a backend renderer of an already existing framework. But it is provided for convenience, + so it isn't needed to provide external components to run the examples or do engine tests. + + \see TLN_DeleteWindow(), TLN_ProcessWindow(), TLN_GetInput(), TLN_DrawFrame() +*/ +TLNAPI bool TLN_CreateWindow (const char* overlay, int flags); + +/*! + \brief Creates a multithreaded window for rendering + \param overlay Deprecated parameter in 2.10, kept for compatibility. Set to NULL + \param flags Mask of the possible creation flags: CWF_FULLSCREEN, CWF_VSYNC, CWF_S1 - CWF_S5 (scaling factor, none = auto max) + \returns True if window was created or false if error + + Creates a host window with basic user input for tilengine. If fullscreen, it uses the desktop + resolution and stretches the output resolution with aspect correction, letterboxing or pillarboxing + as needed. If windowed, it creates a centered window that is the maximum possible integer multiply of + the resolution configured at TLN_Init() + + \remarks Unlike TLN_CreateWindow, This window runs in its own thread + \see TLN_DeleteWindow(), TLN_IsWindowActive(), TLN_GetInput(), TLN_UpdateFrame() +*/ +TLNAPI bool TLN_CreateWindowThread (const char* overlay, int flags); + +/*! + \brief Sets window title + \param title Text with the title to set +*/ +TLNAPI void TLN_SetWindowTitle (const char* title); + +/*! + \brief Does basic window housekeeping in signgle-threaded window + \returns True if window is active or false if the user has requested to end the application (by pressing Esc key or clicking the close button) + + If a window has been created with TLN_CreateWindow, this function must be called periodically (call it inside + the main loop so it gets called regularly). If the window was created with TLN_CreateWindowThread, do not use it + + \see TLN_CreateWindow() +*/ +TLNAPI bool TLN_ProcessWindow (void); + +/*! + \brief Checks window state + \returns True if window is active or false if the user has requested to end the application (by pressing Esc key or clicking the close button) + \see TLN_CreateWindow(), TLN_CreateWindowThread() +*/ +TLNAPI bool TLN_IsWindowActive (void); + +/*! + \brief Returns the state of a given input + + \param input Input to check state. It can be one of the following values: + * INPUT_UP + * INPUT_DOWN + * INPUT_LEFT + * INPUT_RIGHT + * INPUT_BUTTON1 - INPUT_BUTTON6, + * INPUT_START + * Optionally combine with INPUT_P1 to INPUT_P4 to request input for specific player + + \returns True if that input is pressed or false if not + + If a window has been created with TLN_CreateWindow, it provides basic user input. + It simulates a classic arcade setup, with 4 directional buttons (INPUT_UP to INPUT_RIGHT), + 6 action buttons (INPUT_BUTTON1 to INPUT_BUTTON6) and a start button (INPUT_START). + By default directional buttons are mapped to keyboard cursors and joystick 1 D-PAD, + and the first four action buttons are the keys Z,X,C,V and joystick buttons 1 to 4. + + \see TLN_CreateWindow(), TLN_DefineInputKey(), TLN_DefineInputButton() +*/ +TLNAPI bool TLN_GetInput (TLN_Input input); + +/*! + \brief Enables or disables input for specified player + \param player Player number to enable (PLAYER1 - PLAYER4) + \param enable Set true to enable, false to disable +*/ +TLNAPI void TLN_EnableInput (TLN_Player player, bool enable); + +/*! + \brief Assigns a joystick index to the specified player + \param player Player number to configure (PLAYER1 - PLAYER4) + \param index Joystick index to assign, 0-based index. -1 = disable + */ +TLNAPI void TLN_AssignInputJoystick (TLN_Player player, int index); + +/*! + \brief Assigns a keyboard input to a player + \param player Player number to configure (PLAYER1 - PLAYER4) + \param input Input to associate to the given key + \param keycode ASCII key value or scancode as defined in SDL.h +*/ +TLNAPI void TLN_DefineInputKey (TLN_Player player, TLN_Input input, uint32_t keycode); + +/*! + \brief Assigns a button joystick input to a player + \param player Player number to configure (PLAYER1 - PLAYER4) + \param input Input to associate to the given button + \param joybutton Button index +*/ +TLNAPI void TLN_DefineInputButton (TLN_Player player, TLN_Input input, uint8_t joybutton); + +/*! + \brief Draws a frame to the window + \param frame Optional frame number. Set to 0 to autoincrement from previous value + \remarks + If a window has been created with TLN_CreateWindow(), it renders the frame to it. This function is a wrapper to + TLN_UpdateFrame which also automatically sets the render target for the window, so when calling this function it is + not needed to call TLN_UpdateFrame() too. + + \see TLN_CreateWindow(), TLN_UpdateFrame() + */ +TLNAPI void TLN_DrawFrame (int frame); + +/*! + \brief Thread synchronization for multithreaded window. Waits until the current frame has ended rendering + \see TLN_CreateWindowThread() +*/ +TLNAPI void TLN_WaitRedraw (void); + +/*! + \brief Deletes the window previoulsy created with TLN_CreateWindow() or TLN_CreateWindowThread() + \see TLN_CreateWindow() +*/ +TLNAPI void TLN_DeleteWindow (void); + +/*! + \brief Configures CRT simulation post-processing effect to give true retro appeareance + \param type One possible value of \ref TLN_CRT enumeration + \param blur simulate RF (horizontal) blur + \param scanlines simulate horizontal scanlines +*/ +TLNAPI void TLN_ConfigCRTEffect(TLN_CRT type, bool blur, bool scanlines); + +/*! + \brief Disables the CRT post-processing effect + \see TLN_ConfigCRTEffect +*/ +TLNAPI void TLN_DisableCRTEffect (void); + +/*! + \brief Registers a user-defined callback to capture internal SDL2 events + \param callback pointer to user funcion with signature void (SDL_Event*) +*/ +TLNAPI void TLN_SetSDLCallback(TLN_SDLCallback callback); + +/*! + \brief Suspends execition for a fixed time + \param time Number of milliseconds to wait +*/ +TLNAPI void TLN_Delay (uint32_t time); + +/*! \brief Returns the number of milliseconds since application start */ +TLNAPI uint32_t TLN_GetTicks (void); + +/*! \brief Returns averaged fps being rendered on the built-in window, updated each 500 ms */ +TLNAPI uint32_t TLN_GetAverageFps(void); + +/*! \brief Returns horizontal dimension of window after scaling */ +TLNAPI int TLN_GetWindowWidth(void); + +/*! \brief Returns vertical dimension of window after scaling */ +TLNAPI int TLN_GetWindowHeight(void); + +/*! + \brief Returns current window scaling factor. + \remarks This value can be set during call to TLN_CreateWindow() (flags CWF_S1 to CWF_S5), calling TLN_SetWindowScaleFactor(), or pressing ALT-1 to ALT-5 at runtime +*/ +TLNAPI int TLN_GetWindowScaleFactor(void); + +/*! \brief Sets current window scaling factor */ +TLNAPI void TLN_SetWindowScaleFactor(int); + +/*! + \brief Set task to execute for each frame. Required when targeting HTML5 + \param pointer to user-provided function to execute on each frame + This function blocks until window is closed +*/ +TLNAPI void TLN_SetMainTask(TLN_TaskCallback); + +/**@}*/ + +/** + * \defgroup spriteset Spriteset resources management for sprites + +* @{ */ + +/*! + \brief Creates a new spriteset + \param bitmap Bitmap containing the sprite graphics + \param data Array of TLN_SpriteData structures with sprite descriptions + \param num_entries Number of entries in data[] array + \returns Reference to the created spriteset, or NULL if error + \see TLN_DeleteSpriteset() +*/ TLNAPI TLN_Spriteset TLN_CreateSpriteset (TLN_Bitmap bitmap, TLN_SpriteData* data, int num_entries); + +/*! + \brief Loads a spriteset from an image png and its associated atlas descriptor + \param name Base name of the files containing the spriteset, with or without .png extension + \returns Reference to the newly loaded spriteset or NULL if error + + \remarks + The spriteset comes in a pair of files: an image file (bmp or png) and a standarized atlas descriptor (json, csv or txt) + The supported json format is the array. +*/ TLNAPI TLN_Spriteset TLN_LoadSpriteset (const char* name); + +/*! + \brief Creates a duplicate of the specified spriteset and its associated palette + \param src Spriteset to clone + \returns A reference to the newly cloned spriteset, or NULL if error + \see TLN_LoadSpriteset() +*/ TLNAPI TLN_Spriteset TLN_CloneSpriteset (TLN_Spriteset src); + +/*! + \brief Query the details about the specified sprite inside a spriteset + \param spriteset Reference to the spriteset to get info about + \param entry The entry index inside the spriteset [0, num_sprites - 1] + \param info Pointer to application-allocated TLN_SpriteInfo structure that will receive the data + \returns true if success or false if error + */ TLNAPI bool TLN_GetSpriteInfo (TLN_Spriteset spriteset, int entry, TLN_SpriteInfo* info); + +/*! + \brief Returns a reference to the palette associated to the specified spriteset + \param spriteset Spriteset to obtain the palette + \remarks + The palette of a spriteset is created at load time and cannot be modified. When TLN_ConfigSprite + function is used to setup a sprite, the palette associated with the specified spriteset is automatically + assigned to that sprite, but it can be later replaced with TLN_SetSpritePalette + + \see TLN_SetSpritePalette() +*/ TLNAPI TLN_Palette TLN_GetSpritesetPalette (TLN_Spriteset spriteset); + +/*! + \brief Returns a reference to the palette associated to the specified spriteset + \param spriteset Spriteset where to find the sprite + \param name Name of the sprite to findo + \returns sprite index (0 -> num_sprites - 1) if found, or -1 if not found +*/ TLNAPI int TLN_FindSpritesetSprite (TLN_Spriteset spriteset, const char* name); + +/*! + \brief Sets attributes and pixels of a given sprite inside a spriteset + \param spriteset Spriteset to set the data + \param entry The entry index inside the spriteset to modify [0, num_sprites - 1] + \param data Pointer to a user-provided TLN_SpriteData structure with sprite description + \param pixels Pointer to source pixel data + \param pitch Number of bytes per scanline of the source pixel data + \see TLN_CreateSpriteset() +*/ TLNAPI bool TLN_SetSpritesetData (TLN_Spriteset spriteset, int entry, TLN_SpriteData* data, void* pixels, int pitch); -TLNAPI bool TLN_DeleteSpriteset (TLN_Spriteset Spriteset); + +/*! + \brief Deletes the specified spriteset and frees memory + \param spriteset Spriteset to delete + \remarks Don't delete a spriteset currently attached to a sprite! + \see TLN_LoadSpriteset(), TLN_CloneSpriteset() +*/ +TLNAPI bool TLN_DeleteSpriteset (TLN_Spriteset spriteset); /**@}*/ /** - * \defgroup tileset - * \brief Tileset resources management for background layers + * \defgroup tileset Tileset resources management for background layers * @{ */ + +/*! + \brief Creates a tile-based tileset + \param numtiles Number of tiles that the tileset will hold + \param width Width of each tile (must be multiple of 8) + \param height Height of each tile (must be multiple of 8) + \param palette Reference to the palette to assign + \param sp Optional reference to the optional sequence pack with associated tileset animations, can be NULL + \param attributes Optional array of attributes, one for each tile. Can be NULL + \returns Reference to the created tileset, or NULL if error + \see TLN_SetTilesetPixels() +*/ TLNAPI TLN_Tileset TLN_CreateTileset (int numtiles, int width, int height, TLN_Palette palette, TLN_SequencePack sp, TLN_TileAttributes* attributes); + +/*! + \brief Creates a multiple image-based tileset + \param numtiles Number of tiles that the tileset will hold + \param images Array of image structures, one for each tile. Can be NULL + \returns Reference to the created tileset, or NULL if error +*/ +TLNAPI TLN_Tileset TLN_CreateImageTileset(int numtiles, TLN_TileImage* images); + +/*! + \brief Loads a tileset from a Tiled .tsx file + \param filename TSX file to load + \returns Reference to the newly loaded tileset or NULL if error + \remarks An associated palette is also created, it can be obtained calling TLN_GetTilesetPalette() +*/ TLNAPI TLN_Tileset TLN_LoadTileset (const char* filename); + +/*! + \brief Creates a duplicate of the specified tileset and its associated palette + \param src Tileset to clone + \returns A reference to the newly cloned tileset, or NULL if error + \see TLN_LoadTileset() + */ TLNAPI TLN_Tileset TLN_CloneTileset (TLN_Tileset src); + +/*! + \brief Sets pixel data for a tile in a tile-based tileset + \param tileset Reference to the tileset + \param entry Number of tile to set [0, num_tiles - 1] + \param srcdata Pointer to pixel data to set + \param srcpitch Bytes per line of source data + \returns true if success, or false if error + \remarks Care must be taken in providing pixel data and pitch as it can crash the aplication + \see TLN_CreateTileset() +*/ TLNAPI bool TLN_SetTilesetPixels (TLN_Tileset tileset, int entry, uint8_t* srcdata, int srcpitch); -TLNAPI bool TLN_CopyTile (TLN_Tileset tileset, int src, int dst); + +/*! + \brief Returns the width in pixels of each individual tile in the tileset + \param tileset Reference to the tileset to get info from + \see TLN_GetTileHeight() +*/ TLNAPI int TLN_GetTileWidth (TLN_Tileset tileset); + +/*! + \brief Returns the height in pixels of each individual tile in the tileset + \param tileset Reference to the tileset to get info from + \see TLN_GetTileWidth() +*/ TLNAPI int TLN_GetTileHeight (TLN_Tileset tileset); + +/*! + \brief Returns the number of different tiles in tileset + \param tileset Reference to the tileset to get info from +*/ TLNAPI int TLN_GetTilesetNumTiles(TLN_Tileset tileset); + +/*! + \brief Returns a reference to the palette associated to the specified tileset + \param tileset Reference to the tileset to get the palette + \remarks The palette of a tileset is created at load time and cannot be modified. When TLN_SetLayer + function is used to attach a tileset to a layer, the palette associated with the specified tileset is automatically + assigned to that layer, but it can be later replaced with TLN_SetLayerPalette + + \see TLN_LoadTileset(), TLN_SetLayerPalette() + */ TLNAPI TLN_Palette TLN_GetTilesetPalette (TLN_Tileset tileset); + +/*! + \brief Returns a reference to the optional sequence pack associated to the specified tileset + \param tileset Reference to the tileset to get the palette + \see TLN_LoadTileset(), TLN_CreateTileset() +*/ TLNAPI TLN_SequencePack TLN_GetTilesetSequencePack (TLN_Tileset tileset); + +/*! + \brief Returns number of animations in given tileset + \param tileset Reference to the tileset to get the number of animations + \returns Number of animations in the tileset, or 0 if none +*/ +TLNAPI int TLN_GetTilesetNumAnimations(TLN_Tileset tileset); + +/*! + \brief Pauses animation for the given tileset + \param tileset Reference of the tileset to pause animation + \param index Id of the animation to pause (0 <= id < num_animations) + \see Animations TLN_ResumeTilesetAnimation, TLN_GetTilesetNumAnimations +*/ +TLNAPI bool TLN_PauseTilesetAnimation(TLN_Tileset tileset, int index); + +/*! + \brief Restores animation for the given tileset + \param tileset Reference of the tileset to resume animation + \param index Id of the animation to resume (0 <= id < num_animations) + \see Animations TLN_PauseTilesetAnimation, TLN_GetTilesetNumAnimations +*/ +TLNAPI bool TLN_ResumeTilesetAnimation(TLN_Tileset tileset, int index); + +/*! + \brief Deletes the specified tileset and frees memory + \param tileset Tileset to delete + \remarks Don't delete a tileset currently attached to a layer! + \see TLN_LoadTileset(), TLN_CloneTileset() +*/ TLNAPI bool TLN_DeleteTileset (TLN_Tileset tileset); /**@}*/ /** - * \defgroup tilemap - * \brief Tilemap resources management for background layers + * \defgroup tilemap Tilemap resources management for background layers * @{ */ + +/*! + \brief Creates a new tilemap + \param rows Number of rows (vertical dimension) + \param cols Number of cols (horizontal dimension) + \param tiles Array of tiles with data (see struct Tile) + \param bgcolor Background color value (RGB32 packed) + \param tileset Optional reference to associated tileset, can be NULL + \returns Reference to the created tilemap, or NULL if error + \remarks Make sure that the tiles[] array is has at least rows*cols items or application may crash + \see TLN_DeleteTilemap(), struct Tile +*/ TLNAPI TLN_Tilemap TLN_CreateTilemap (int rows, int cols, TLN_Tile tiles, uint32_t bgcolor, TLN_Tileset tileset); + +/*! + \brief Loads a tilemap layer from a Tiled .tmx file + \param filename TMX file with the tilemap + \param layername Optional name of the layer inside the tmx file to load. NULL to load the first layer + \returns Reference to the newly loaded tilemap or NULL if error + \remarks + A tmx map file from Tiled can contain one or more layers, each with its own name. TLN_LoadTilemap() + doesn't load a full tmx file, only the specified layer. The associated *external* tileset (TSX file) is + also loaded and associated to the tilemap +*/ TLNAPI TLN_Tilemap TLN_LoadTilemap (const char* filename, const char* layername); + +/*! + \brief Creates a duplicate of the specified tilemap + \param src Reference to the tilemap to clone + \returns A reference to the newly cloned tilemap, or NULL if error + \see TLN_LoadTilemap() +*/ TLNAPI TLN_Tilemap TLN_CloneTilemap (TLN_Tilemap src); + +/*! + \brief Returns the number of vertical tiles in the tilemap + \param tilemap Reference of the tilemap to get info + \see TLN_GetTilemapCols() +*/ TLNAPI int TLN_GetTilemapRows (TLN_Tilemap tilemap); + +/*! + \brief Returns the number of horizontal tiles in the tilemap + \param tilemap Reference of the tilemap to get info + \see TLN_GetTilemapRows() +*/ TLNAPI int TLN_GetTilemapCols (TLN_Tilemap tilemap); + +/*! + \brief Sets default tileset to specified tilemap + \param tilemap Reference to the tilemap to modify + \param tileset Reference to the tileset being assigned + \see TLN_GetTilemapTileset() +*/ +TLNAPI bool TLN_SetTilemapTileset(TLN_Tilemap tilemap, TLN_Tileset tileset); + +/*! + \brief Returns the optional associated tileset to the specified tilemap + \param tilemap Reference of the tilemap to get info + \see TLN_CreateTilemap(), TLN_LoadTilemap() +*/ TLNAPI TLN_Tileset TLN_GetTilemapTileset (TLN_Tilemap tilemap); + +/*! + \brief Sets default tileset to specified tilemap + \param tilemap Reference to the tilemap to modify + \param tileset Reference to the tileset being assigned + \param index Index of tileset to set (0 - 7) + \see TLN_GetTilemapTileset() +*/ +TLNAPI bool TLN_SetTilemapTileset2(TLN_Tilemap tilemap, TLN_Tileset tileset, int index); + +/*! + \brief Returns the nth tileset associated tileset to the specified tilemap + \param tilemap Reference of the tilemap to get info + \param index Tileset index (0 - 7) + \see TLN_CreateTilemap(), TLN_LoadTilemap() +*/ +TLNAPI TLN_Tileset TLN_GetTilemapTileset2(TLN_Tilemap tilemap, int index); + +/*! + \brief Gets data of a single tile inside a tilemap + \param tilemap Reference of the tilemap to get the tile + \param row Vertical location of the tile (0 <= row < rows) + \param col Horizontal location of the tile (0 <= col < cols) + \param tile Reference to an application-allocated struct Tile that will get the data +*/ TLNAPI bool TLN_GetTilemapTile (TLN_Tilemap tilemap, int row, int col, TLN_Tile tile); + +/*! + \brief Sets a tile of a tilemap + \param tilemap Reference to the tilemap + \param row Row (vertical position) of the tile [0 - num_rows - 1] + \param col Column (horizontal position) of the tile [0 - num_cols - 1] + \param tile Reference to the tile to set, or NULL to set an empty tile + \returns true (success) or false (error) +*/ TLNAPI bool TLN_SetTilemapTile (TLN_Tilemap tilemap, int row, int col, TLN_Tile tile); + +/*! + \brief Copies blocks of tiles between two tilemaps + \param src Reference to the source tilemap + \param srcrow Starting row (vertical position) inside the source tilemap + \param srccol Starting column (horizontal position) inside the source tilemap + \param rows Number of rows to copy + \param cols Number of columns to copy + \param dst Reference to the target tilemap + \param dstrow Starting row (vertical position) inside the target tilemap + \param dstcol Starting column (horizontal position) inside the target tilemap + \remarks Use this function to implement tile streaming +*/ TLNAPI bool TLN_CopyTiles (TLN_Tilemap src, int srcrow, int srccol, int rows, int cols, TLN_Tilemap dst, int dstrow, int dstcol); + +/*! + \brief Returns pointer to internal tilemap data data + \param tilemap Tilemap being queried + \param row Row index + \param col Column index + \returns pointer to corresponding TLN_Tile object or NULL if error + \remarks Having direct access to internal memory is convenient for performance reasons when lots of tiles + must be updated at runtime, but wrong manipulation can lead to memory corruption or crashes. Use with caution! +*/ +TLNAPI TLN_Tile TLN_GetTilemapTiles(TLN_Tilemap tilemap, int row, int col); + +/*! + \brief Deletes the specified tilemap and frees memory + \param tilemap Reference to the tilemap to delete + \remarks Don't delete a tilemap currently attached to a layer! + \see TLN_LoadTilemap(), TLN_CloneTilemap() +*/ TLNAPI bool TLN_DeleteTilemap (TLN_Tilemap tilemap); + /**@}*/ /** - * \defgroup palette - * \brief Color palette resources management for sprites and background layers + * \defgroup palette Color palette resources management for sprites and background layers * @{ */ + +/*! + \brief Creates a new color table + \param entries Number of color entries (typically 256) + \returns Reference to the created palette or NULL if error +*/ TLNAPI TLN_Palette TLN_CreatePalette (int entries); + +/*! + \brief Loads a palette from a standard .act file + \param filename ACT file containing the palette to load + \returns A reference to the newly loaded palette, or NULL if error + \remarks + Palettes are also automatically created when loading tilesets and spritesets. + Use the functions TLN_GetTilesetPalette() and TLN_GetSpritesetPalette() to retrieve them. + + \see TLN_GetTilesetPalette(), TLN_GetSpritesetPalette() +*/ TLNAPI TLN_Palette TLN_LoadPalette (const char* filename); + +/*! + \brief Creates a duplicate of the specified palette + \param src Reference to the palette to clone + \returns A reference to the newly cloned palette, or NULL if error + \see TLN_CreatePalette() +*/ TLNAPI TLN_Palette TLN_ClonePalette (TLN_Palette src); -TLNAPI bool TLN_SetPaletteColor (TLN_Palette palette, int color, uint8_t r, uint8_t g, uint8_t b); + +/*! + \brief Sets the RGB color value of a palette entry + \param palette Reference to the palette to modify + \param index Index of the palette entry to modify (0-255) + \param r Red component of the color (0-255) + \param g Green component of the color (0-255) + \param b Blue component of the color (0-255) +*/ +TLNAPI bool TLN_SetPaletteColor (TLN_Palette palette, int index, uint8_t r, uint8_t g, uint8_t b); + +/*! + \brief Mixes two palettes to create a third one + \param src1 Reference to the first source palette + \param src2 Reference to the second source palette + \param dst Reference to the target palette + \param factor Integer with mixing factor. 0=100% src1, 255=100% src2, 128=50%/50% +*/ TLNAPI bool TLN_MixPalettes (TLN_Palette src1, TLN_Palette src2, TLN_Palette dst, uint8_t factor); + +/*! + \brief Modifies a range of colors by adding the provided color value to the selected range. The result is always a brighter color. + \param palette Reference to the palette to modify + \param r Red component of the color (0-255) + \param g Green component of the color (0-255) + \param b Blue component of the color (0-255) + \param start index of the first color entry to modify + \param num number of colors from start to modify +*/ TLNAPI bool TLN_AddPaletteColor (TLN_Palette palette, uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t num); + +/*! + \brief Modifies a range of colors by subtracting the provided color value to the selected range. The result is always a darker color. + \param palette Reference to the palette to modify + \param r Red component of the color (0-255) + \param g Green component of the color (0-255) + \param b Blue component of the color (0-255) + \param start index of the first color entry to modify + \param num number of colors from start to modify +*/ TLNAPI bool TLN_SubPaletteColor (TLN_Palette palette, uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t num); + +/*! + \brief Modifies a range of colors by modulating (normalized product) the provided color value to the selected range. The result is always a darker color. + \param palette Reference to the palette to modify + \param r Red component of the color (0-255) + \param g Green component of the color (0-255) + \param b Blue component of the color (0-255) + \param start index of the first color entry to modify + \param num number of colors from start to modify +*/ TLNAPI bool TLN_ModPaletteColor (TLN_Palette palette, uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t num); + +/*! + \brief Returns the color value of a palette entry + \param palette Reference to the palette to get the color + \param index Index of the palette entry to obtain (0-255) + \returns 32-bit integer with the packed color in internal pixel format RGBA +*/ TLNAPI uint8_t* TLN_GetPaletteData (TLN_Palette palette, int index); + +/*! + \brief Returns the number of color entries in the given palette + \param palette Reference to the palette to query + \returns number of color entries +*/ +TLNAPI int TLN_GetPaletteNumColors(TLN_Palette palette); + +/*! + \brief Deletes the specified palette and frees memory + \param palette Reference to the palette to delete + \remarks Don't delete a palette currently attached to a layer or sprite! +*/ TLNAPI bool TLN_DeletePalette (TLN_Palette palette); /**@}*/ /** - * \defgroup bitmap - * \brief Bitmap management + * \defgroup bitmap Bitmap management * @{ */ + +/*! + \brief Creates a memory bitmap + \param width Width in pixels + \param height Height in pixels + \param bpp Bits per pixel + \returns Reference to the created bitmap, or NULL if error + \see TLN_SetBGBitmap() +*/ TLNAPI TLN_Bitmap TLN_CreateBitmap (int width, int height, int bpp); + +/*! + \brief Load image file (8-bit BMP or PNG) + \param filename File name with the image + \returns Handler to the loaded image or NULL if error + \see TLN_DeleteBitmap() +*/ TLNAPI TLN_Bitmap TLN_LoadBitmap (const char* filename); + +/*! + \brief Creates a copy of a bitmap + \param src Reference to the original bitmap + \returns Reference to the created bitmap, or NULL if error + \see TLN_SetBGBitmap() + */ TLNAPI TLN_Bitmap TLN_CloneBitmap (TLN_Bitmap src); + +/*! + \brief Gets memory access for direct pixel manipulation + \param bitmap Reference to bitmap + \param x Starting x position [0, width - 1] + \param y Starting y position [0, height - 1] + \returns Pointer to pixel data starting at x,y + \remarks Care must be taken in manipulating memory directly as it can crash the application +*/ TLNAPI uint8_t* TLN_GetBitmapPtr (TLN_Bitmap bitmap, int x, int y); + +/*! + \brief Returns the width in pixels + \param bitmap Reference to the bitmap +*/ TLNAPI int TLN_GetBitmapWidth (TLN_Bitmap bitmap); + +/*! + \brief Returns the height in pixels + \param bitmap Reference to the bitmap +*/ TLNAPI int TLN_GetBitmapHeight (TLN_Bitmap bitmap); + +/*! + \brief Returns the number of bits per pixel + \param bitmap Reference to the bitmap +*/ TLNAPI int TLN_GetBitmapDepth (TLN_Bitmap bitmap); + +/*! + \brief Returns the number of bytes of each scanline (row) of the bitmap, also known as stride + \param bitmap Reference to the bitmap +*/ TLNAPI int TLN_GetBitmapPitch (TLN_Bitmap bitmap); + +/*! + \brief Gets the associated palete of a bitmap + \param bitmap Reference to bitmap + \returns Reference to the bitmap palette + \see TLN_SetBitmapPalette() +*/ TLNAPI TLN_Palette TLN_GetBitmapPalette (TLN_Bitmap bitmap); + +/*! + \brief Assigns a new palette to the bitmap + \param bitmap Reference to the bitmap + \param palette Reference to the palette to assign + \see TLN_GetBitmapPalette() +*/ TLNAPI bool TLN_SetBitmapPalette (TLN_Bitmap bitmap, TLN_Palette palette); + +/*! + \brief Deletes bitmap and frees resources + \param bitmap Reference to bitmap to delete + \see TLN_CreateBitmap89, TLN_CloneBitmap() +*/ TLNAPI bool TLN_DeleteBitmap (TLN_Bitmap bitmap); /**@}*/ /** - * \defgroup objects - * \brief ObjectList resources management + * \defgroup objects ObjectList resources management * @{ */ + + /*! + \brief Creates a TLN_ObjectList + The list is created empty, it must be populated with TLN_AddSpriteToList() + and assigned to a layer with TLN_SetLayerObjects() + \return Reference to new object or NULL if error + */ TLNAPI TLN_ObjectList TLN_CreateObjectList(void); -TLNAPI bool TLN_AddObjectToList(TLN_ObjectList list, TLN_Object* data); -TLNAPI bool TLN_AddSpriteToList(TLN_ObjectList list, TLN_Spriteset spriteset, const char* name, int id, int x, int y); -TLNAPI TLN_ObjectList TLN_LoadObjectList(const char* filename, const char* layername, int firstgid); + +/*! + \brief Adds an image-based tileset item to given TLN_ObjectList + \param list Reference to TLN_ObjectList + \param id Unique ID of the tileset object + \param gid Graphic Id (tile index) of the tileset object + \param flags Combination of FLAG_FLIPX, FLAG_FLIPY, FLAG_PRIORITY + \param x Layer-space horizontal coordinate of the top-left corner + \param y Layer-space bertical coordinate of the top-left corner + \return true if success or false if error +*/ +TLNAPI bool TLN_AddTileObjectToList(TLN_ObjectList list, uint16_t id, uint16_t gid, uint16_t flags, int x, int y); + +/*! + \brief Loads an object list from a Tiled object layer + \param filename Name of the .tmx file containing the list + \param layername Name of the layer to load + \return Reference to the loaded object or NULL if error +*/ +TLNAPI TLN_ObjectList TLN_LoadObjectList(const char* filename, const char* layername); + +/*! + \brief Creates a duplicate of a given TLN_ObjectList object + \param src Reference to the source object to clone + \return A reference to the newly cloned object list, or NULL if error +*/ TLNAPI TLN_ObjectList TLN_CloneObjectList(TLN_ObjectList src); -TLNAPI int TLN_GetObjectsInReigion(TLN_ObjectList list, int x, int y, int width, int height, int array_size, TLN_Object* objects[]); + +/*! + \brief Returns number of items in TLN_ObjectList + \param list Pointer to TLN_ObjectList to query + \return number of items +*/ +TLNAPI int TLN_GetListNumObjects(TLN_ObjectList list); + +/*! + \brief Iterates over elements in a TLN_ObjectList + \param list Reference to TLN_ObjectList to get items + \param info Pointer to user-allocated TLN_ObjectInfo struct + \return true if item returned, false if no more items left + \remarks The info pointer acts as a switch to select first/next element: + - If not NULL, starts the iterator and returns the first item + - If NULL, return the next item +*/ +TLNAPI bool TLN_GetListObject(TLN_ObjectList list, TLN_ObjectInfo* info); + +/*! + \brief Deletes object list + \param list Reference to list to delete + \return true if success or false if error +*/ TLNAPI bool TLN_DeleteObjectList(TLN_ObjectList list); /**@}*/ /** - * \defgroup layer - * \brief Background layers management + * \defgroup layer Background layers management * @{ */ + +/*! + \deprecated Use \ref TLN_SetLayerTilemap instead + \brief Configures a background layer with the specified tileset and tilemap + \param nlayer Layer index [0, num_layers - 1] + \param tileset Optional reference to the tileset to assign. If the tilemap has a reference to its own tileset, passing NULL will assign the default tileset. + \param tilemap Reference to the tilemap to assign + \remarks This function doesn't modify the current position nor the blend mode, but assigns the palette of the specified tileset + \see TLN_DisableLayer() +*/ TLNAPI bool TLN_SetLayer (int nlayer, TLN_Tileset tileset, TLN_Tilemap tilemap); + +/*! + \brief Configures a tiled background layer with the specified tilemap + \param nlayer Layer index [0, num_layers - 1] + \param tilemap Reference to the tilemap to assign + \returns true if success or false if error + \see TLN_LoadTilemap() +*/ +TLNAPI bool TLN_SetLayerTilemap(int nlayer, TLN_Tilemap tilemap); + +/*! + \brief Configures a background layer with the specified full bitmap + \param nlayer Layer index [0, num_layers - 1] + \param bitmap Reference to the bitmap to assign + \remarks This function doesn't modify the current position nor the blend mode, but assigns the palette of the specified bitmap + \see TLN_LoadBitmap() TLN_DisableLayer() +*/ TLNAPI bool TLN_SetLayerBitmap(int nlayer, TLN_Bitmap bitmap); + +/*! + \brief Sets the color palette to the layer + \param nlayer Layer index [0, num_layers - 1] + \param palette Reference to the palette to assign to the layer + + Overrides the palette of the current tileset or bitmap + + \remarks + Call this function inside a raster callback to change the palette in the middle + of the frame to get raster effect colors, like and "underwater" palette below the + water line in a partially submerged background, or a gradient palette in an area at + the top of the screen to simulate a "depth fog effect" in a pseudo 3d background +*/ TLNAPI bool TLN_SetLayerPalette (int nlayer, TLN_Palette palette); + +/*! + \brief Sets the position of the tileset that corresponds to the upper left corner + \param nlayer Layer index [0, num_layers - 1] + \param hstart Horizontal offset in the tileset on the left side + \param vstart Vertical offset in the tileset on the top side + + The tileset usually spans an area much bigger than the viewport. Use this + function to move the viewport insde the tileset. Change this value progressively + for each frame to get a scrolling effect + + \remarks + Call this function inside a raster callback to get a raster scrolling effect. + Use this to create horizontal strips of the same + layer that move at different speeds to simulate depth. The extreme case of this effect, where + the position is changed in each scanline, is called "line scroll" and was the technique used by + games such as Street Fighter II to simualte a pseudo 3d floor, or many racing games to simulate + a 3D road. +*/ TLNAPI bool TLN_SetLayerPosition (int nlayer, int hstart, int vstart); + +/*! + \brief Sets simple scaling + \param nlayer Layer index [0, num_layers - 1] + \param xfactor Horizontal scale factor + \param yfactor Vertical scale factor + + By default the scaling factor of a given layer is 1.0f, 1.0f, which means + no scaling. Use values below 1.0 to downscale (shrink) and above 1.0 to upscale (enlarge). + Call TLN_ResetLayerMode() to disable scaling + + \see TLN_ResetLayerMode() +*/ TLNAPI bool TLN_SetLayerScaling (int nlayer, float xfactor, float yfactor); + +/*! + \brief Sets affine transform matrix to enable rotating and scaling of this layer + \param nlayer Layer index [0, num_layers - 1] + \param affine Pointer to an TLN_Affine matrix, or NULL to disable it + + Enable the transformation matrix to give the layer the capabilities of the famous + Super Nintendo / Famicom Mode 7. Beware that the rendering of a transformed layer + uses more CPU than a regular layer. Unlike the original Mode 7, that could only transform + the single layer available, Tilengine can transform all the layers at the same time. The only + limitation is the available CPU power. + + \remarks + Call this function inside a raster callback to set the transformation matrix in the middle of + the frame. Setting it for each scanline is the trick used by many Super Nintendo games to fake + a 3D perspective projection. + + \see TLN_SetLayerTransform() + */ TLNAPI bool TLN_SetLayerAffineTransform (int nlayer, TLN_Affine *affine); + +/*! + \brief Sets affine transform matrix to enable rotating and scaling of this layer + \param layer Layer index [0, num_layers - 1] + \param angle Rotation angle in degrees + \param dx Horizontal displacement + \param dy Vertical displacement + \param sx Horizontal scaling + \param sy Vertical scaling + \remarks This function is a simple wrapper to TLN_SetLayerAffineTransform() without using the TLN_Affine struct + \see TLN_SetLayerAffineTransform() +*/ TLNAPI bool TLN_SetLayerTransform (int layer, float angle, float dx, float dy, float sx, float sy); + +/*! + \brief Sets the table for pixel mapping render mode + \param nlayer Layer index [0, num_layers - 1] + \param table User-provided array of hres*vres sized TLN_PixelMap items + \see TLN_SetLayerScaling(), TLN_SetLayerAffineTransform() +*/ TLNAPI bool TLN_SetLayerPixelMapping (int nlayer, TLN_PixelMap* table); + +/*! + \brief Sets the blending mode (transparency effect) + \param nlayer Layer index [0, num_layers - 1] + \param mode Member of the TLN_Blend enumeration + \param factor Deprecated as of 1.12, left for backwards compatibility but doesn't have effect. + \see Blending +*/ TLNAPI bool TLN_SetLayerBlendMode (int nlayer, TLN_Blend mode, uint8_t factor); + +/*! + \brief Enables column offset mode for this layer + \param nlayer Layer index [0, num_layers - 1] + \param offset Array of offsets to set. Set NULL to disable column offset mode + + Column offset is a value that is added or substracted (depending on the + sign) to the vertical position for that layer (see TLN_SetLayerPosition) for + each column in the tilemap assigned to that layer. + + \remarks + This feature is typically used to simulate vertical strips moving at different + speeds, or combined with a line scroll effect, to fake rotations where the angle + is small. The Sega Genesis games Puggsy and Chuck Rock II used this trick to simulate + partially rotating backgrounds +*/ TLNAPI bool TLN_SetLayerColumnOffset (int nlayer, int* offset); + +/*! + \deprecated Use \ref TLN_SetLayerWindow instead + \brief Enables clipping rectangle on selected layer + \param nlayer Layer index [0, num_layers - 1] + \param x1 left coordinate + \param y1 top coordinate + \param x2 right coordinate + \param y2 bottom coordinate +*/ TLNAPI bool TLN_SetLayerClip (int nlayer, int x1, int y1, int x2, int y2); + +/*! + \deprecated Use \ref TLN_DisableLayerWindow instead + \brief Disables clipping rectangle on selected layer + \param nlayer Layer index [0, num_layers - 1] +*/ TLNAPI bool TLN_DisableLayerClip (int nlayer); + +/*! + \brief Enables clipping window on selected layer + \param nlayer Layer index [0, num_layers - 1] + \param x1 left coordinate + \param y1 top coordinate + \param x2 right coordinate + \param y2 bottom coordinate + \param invert false=clip outer region, true=clip inner region + \see TLN_SetLayerWindowColor(), TLN_DisableLayerWindow() + */ +TLNAPI bool TLN_SetLayerWindow(int nlayer, int x1, int y1, int x2, int y2, bool invert); + +/*! + \brief Enables solid color processing on clipped region in window layer + \param nlayer Layer index [0, num_layers - 1] + \param r Red component (0-255) + \param g Green component (0-255) + \param b Blue component (0-255) + \param blend one of possible TLN_Blend modes + When color is enabled on window, the area outside the clipped region gets filled with this color. + If one of blending modes is selected, color math is performed with underlying layer + \see TLN_SetLayerWindow(), TLN_DisableLayerWindowColor() +*/ +TLNAPI bool TLN_SetLayerWindowColor(int nlayer, uint8_t r, uint8_t g, uint8_t b, TLN_Blend blend); + +/*! + \brief Disables layer window clipping + \param nlayer Layer index [0, num_layers - 1] + \see TLN_SetLayerWindow() +*/ +TLNAPI bool TLN_DisableLayerWindow(int nlayer); + +/*! + \brief Disables color processing for window on selected layer + \param nlayer Layer index [0, num_layers - 1] + \see TLN_SetLayerWindowColor() +*/ +TLNAPI bool TLN_DisableLayerWindowColor(int nlayer); + +/*! + \brief Enables mosaic effect (pixelation) for selected layer + \param nlayer Layer index [0, num_layers - 1] + \param width horizontal pixel size + \param height vertical pixel size + \see TLN_DisableLayerMosaic() +*/ TLNAPI bool TLN_SetLayerMosaic (int nlayer, int width, int height); + +/*! + \brief Disables mosaic effect for selected layer + \param nlayer Layer index [0, num_layers - 1] + \see TLN_SetLayerMosaic() +*/ TLNAPI bool TLN_DisableLayerMosaic (int nlayer); + +/*! + \brief Disables scaling or affine transform for the layer + \param nlayer Layer index [0, num_layers - 1] + \see TLN_SetLayerScaling(), TLN_SetLayerAffineTransform() +*/ TLNAPI bool TLN_ResetLayerMode (int nlayer); -TLNAPI bool TLN_SetLayerObjects(int nlayer, TLN_ObjectList objects, TLN_Spriteset spriteset, int width, int height); + +/*! + \brief Configures a background layer with a object list and an image-based tileset + \param nlayer Layer index [0, num_layers - 1] + \param objects Reference to the TLN_ObjectList to attach + \param tileset optional reference to the image-based tileset object. If NULL, object list must have an attached tileset + \see TLN_LoadObjectList() +*/ +TLNAPI bool TLN_SetLayerObjects(int nlayer, TLN_ObjectList objects, TLN_Tileset tileset); + +/*! + \brief Sets full layer priority, appearing in front of sprites + \param nlayer Layer index [0, num_layers - 1] + \param enable Enable (true) or dsiable (false) full priority +*/ TLNAPI bool TLN_SetLayerPriority(int nlayer, bool enable); + +/*! \deprecated removed, keep for ABI compatibility with old versions*/ TLNAPI bool TLN_SetLayerParent(int nlayer, int parent); + +/*! \deprecated removed, keep for ABI compatibility with old versions*/ TLNAPI bool TLN_DisableLayerParent(int nlayer); + +/*! + \brief Returns the layer width in pixels + \param nlayer Layer index [0, num_layers - 1] + \see TLN_SetLayer(), TLN_GetLayerHeight() +*/ TLNAPI bool TLN_DisableLayer (int nlayer); + +/*! + \brief Enables a layer previously disabled with \ref TLN_DisableLayer + \param nlayer Layer index [0, num_layers - 1] + \remarks The layer must have been previously configured. A layer without a prior configuration can't be enabled +*/ +TLNAPI bool TLN_EnableLayer(int nlayer); + +/*! + \brief Returns the type of the layer + \param nlayer Layer index [0, num_layers - 1] + \returns \ref TLN_LayerType enumeration + \see TLN_SetLayerTilemap(), TLN_SetLayerObjects(), TLN_SetLayerBitmap() +*/ +TLNAPI TLN_LayerType TLN_GetLayerType(int nlayer); + +/*! + \brief Returns the active palette of a layer if set with \ref TLN_SetLayerPalette(), or the palette of the first tileset, or palette of bitmap + \param nlayer Layer index [0, num_layers - 1] + \returns Reference of the palette assigned to the layer + \see TLN_SetLayerPalette() +*/ TLNAPI TLN_Palette TLN_GetLayerPalette (int nlayer); + +/*! \deprecated Returns the first tilesetof the attached layer's tilemap */ +TLNAPI TLN_Tileset TLN_GetLayerTileset(int nlayer); + +/*! + \brief Returns the active tilemap on a \ref LAYER_TILE layer type + \param nlayer Layer index [0, num_layers - 1] + \returns Reference to the active tilemap + \see TLN_SetLayerTilemap() +*/ +TLNAPI TLN_Tilemap TLN_GetLayerTilemap(int nlayer); + +/*! + \brief Returns the active bitmap on a \ref LAYER_BITMAP layer type + \param nlayer Layer index [0, num_layers - 1] + \returns Reference to the active bitmap + \see TLN_SetLayerBitmap() +*/ +TLNAPI TLN_Bitmap TLN_GetLayerBitmap(int nlayer); + +/*! + \brief Returns the active object list on a \ref LAYER_OBJECT layer type + \param nlayer Layer index [0, num_layers - 1] + \returns Reference to the active objects list + \see TLN_SetLayerObjects(), TLN_GetListObject() +*/ +TLNAPI TLN_ObjectList TLN_GetLayerObjects(int nlayer); + +/*! + \brief Gets info about the tile located in tilemap space + \param nlayer Id of the layer to query [0, num_layers - 1] + \param x horizontal position + \param y vertical position + \param info Pointer to an application-allocated TLN_TileInfo struct that will get the data + \returns true if success or false if error + \remarks Use this function to implement collision detection between sprites and the main background layer. + \see TLN_TileInfo +*/ TLNAPI bool TLN_GetLayerTile (int nlayer, int x, int y, TLN_TileInfo* info); -TLNAPI int TLN_GetLayerWidth (int nlayer); -TLNAPI int TLN_GetLayerHeight (int nlayer); + +/*! + \brief Returns the layer width in pixels + \param nlayer Layer index [0, num_layers - 1] + \see TLN_SetLayer(), TLN_GetLayerHeight() +*/ +TLNAPI int TLN_GetLayerWidth (int nlayer); + +/*! + \brief Returns the layer height in pixels + \param nlayer Layer index [0, num_layers - 1] + \see TLN_SetLayer(), TLN_GetLayerWidth() +*/ +TLNAPI int TLN_GetLayerHeight (int nlayer); + +/* + \brief returns layer's horizontal position + \param nlayer Layer index to query + \returns x position + \see TLN_SetLayerPosition() +*/ +TLNAPI int TLN_GetLayerX(int nlayer); + +/* + \brief returns layer's vertical position + \param nlayer Layer index to query + \returns y position + \see TLN_SetLayerPosition() +*/ +TLNAPI int TLN_GetLayerY(int nlayer); /**@}*/ /** - * \defgroup sprite - * \brief Sprites management + * \defgroup sprite Sprites management * @{ */ -TLNAPI bool TLN_ConfigSprite (int nsprite, TLN_Spriteset spriteset, TLN_TileFlags flags); + +/*! + \deprecated use \ref TLN_SetSpriteSet and \ref TLN_EnableSpriteFlag + \brief Configures a sprite, setting spriteset and flags at once + \param nsprite Id of the sprite [0, num_sprites - 1] + \param spriteset Reference of the spriteset containing the graphics to set + \param flags Can be 0 or a combination of FLAG_FLIPX and FLAG_FLIPY + \remarks This function also assigns the palette of the spriteset + \see TLN_SetSpritePicture() +*/ +TLNAPI bool TLN_ConfigSprite (int nsprite, TLN_Spriteset spriteset, uint32_t flags); + +/*! + \brief Assigns the spriteset and its palette to a given sprite + \param nsprite Id of the sprite [0, num_sprites - 1] + \param spriteset Reference of the spriteset containing the graphics to set + \remarks This function also assigns the palette of the spriteset and resets pivot to top left corner (default) + \see TLN_SetSpritePicture() +*/ TLNAPI bool TLN_SetSpriteSet (int nsprite, TLN_Spriteset spriteset); -TLNAPI bool TLN_SetSpriteFlags (int nsprite, TLN_TileFlags flags); + +/*! + \deprecated Use \ref TLN_EnableSpriteFlag to enable or disable individual flags + \brief Sets flags for a given sprite + \param nsprite Id of the sprite [0, num_sprites - 1] + \param flags Can be 0 or a combination of TLN_TileFlags +*/ +TLNAPI bool TLN_SetSpriteFlags (int nsprite, uint32_t flags); + +/*! + \brief Enables or disables specified flag for a sprite + \param nsprite of the sprite [0, num_sprites - 1] + \param flag flag (or combination of flags) to modfy + \param enable true for enable, false for disable +*/ +TLNAPI bool TLN_EnableSpriteFlag(int nsprite, uint32_t flag, bool enable); + +/*! + \brief Sets sprite pivot point. By default is at (0,0) = top left corner + \param nsprite Id of the sprite [0, num_sprites - 1] + \param px horizontal normalized value (0.0 = full left, 1.0 = full right) + \param py vertical normalized value (0.0 = full top, 1.0 = full bottom) + \remarks Sprite pivot is reset automatically to default position after changing the spriteset +*/ +TLNAPI bool TLN_SetSpritePivot(int nsprite, float px, float py); + +/*! + \brief Sets the sprite position in screen space + \param nsprite Id of the sprite [0, num_sprites - 1] + \param x Horizontal position of pivot (0 = left margin) + \param y Vertical position of pivot (0 = top margin) + + \remarks + Call this function inside a raster callback to so some vertical distortion effects + (fake scaling) or sprite multiplexing (reusing a single sprite at different screen heights). + This technique was used by some 8 bit games, with very few hardware sprites, to draw much more + sprites in the screen, as long as they don't overlap vertically + + \sa TLN_SetSpritePivot +*/ TLNAPI bool TLN_SetSpritePosition (int nsprite, int x, int y); + +/*! + \brief Sets the actual graphic to the sprite + \param nsprite Id of the sprite [0, num_sprites - 1] + \param entry Index of the actual picture inside the srpteset to assign (0 <= entry < num_spriteset_graphics) + \see TLN_SetSpriteSet() +*/ TLNAPI bool TLN_SetSpritePicture (int nsprite, int entry); + +/*! + \brief Assigns a palette to a sprite + \param nsprite Id of the sprite [0, num_sprites - 1] + \param palette Reference of the palete to assign +*/ TLNAPI bool TLN_SetSpritePalette (int nsprite, TLN_Palette palette); + +/*! + \brief Sets the blending mode (transparency effect) + \param nsprite Id of the sprite [0, num_sprites - 1] + \param mode Member of the TLN_Blend enumeration + \param factor Deprecated as of 1.12, left for backwards compatibility but doesn't have effect. + \see Blending +*/ TLNAPI bool TLN_SetSpriteBlendMode (int nsprite, TLN_Blend mode, uint8_t factor); + +/*! + \brief Sets the scaling factor of the sprite + \param nsprite Id of the sprite [0, num_sprites - 1] + \param sx Horizontal scale factor + \param sy Vertical scale factor + + By default the scaling factor of a given sprite is 1.0f, 1.0f, which means + no scaling. Use values below 1.0 to downscale (shrink) and above 1.0 to upscale (enlarge). + Call TLN_ResetScaling() to disable scaling + + \remarks The rendering of a sprite with scaling enabled requires somewhat more CPU power than a regular sprite. + \see TLN_ResetSpriteScaling() +*/ TLNAPI bool TLN_SetSpriteScaling (int nsprite, float sx, float sy); + +/*! + \brief Disables scaling for a given sprite + \param nsprite Id of the sprite [0, num_sprites - 1] + \see TLN_SetSpriteScaling() +*/ TLNAPI bool TLN_ResetSpriteScaling (int nsprite); -//TLNAPI bool TLN_SetSpriteRotation (int nsprite, float angle); -//TLNAPI bool TLN_ResetSpriteRotation (int nsprite); + +/*! + \brief Returns the index of the assigned picture from the spriteset + \param nsprite Id of the sprite [0, num_sprites - 1] + \see TLN_SetSpritePicture() +*/ TLNAPI int TLN_GetSpritePicture (int nsprite); + +/* + \brief returns sprite's horizontal position + \param nsprite Sprite index to query + \returns x position + \see TLN_SetSpritePosition() +*/ +TLNAPI int TLN_GetSpriteX(int nsprite); + +/* + \brief returns sprite's vertical position + \param nsprite Sprite index to query + \returns y position + \see TLN_SetSpritePosition() +*/ +TLNAPI int TLN_GetSpriteY(int nsprite); + +/*! + \brief Finds an available (unused) sprite + \returns Index of the first unused sprite (starting from 0) or -1 if none found +*/ TLNAPI int TLN_GetAvailableSprite (void); + +/*! + \brief Enable sprite collision checking at pixel level + \param nsprite Id of the sprite [0, num_sprites - 1] + \param enable Set true to enable o false to disable (default value) + + \remarks + Only sprites that have collision enabled are checked between them, + so to detect a collision between two sprites, both of them must + have collision detection enabled. Processing collision detection + sprites take more a bit more CPU time compared to non-colliding sprites, so + by default it is disabled on all sprites. + + \see TLN_GetSpriteCollision() +*/ TLNAPI bool TLN_EnableSpriteCollision (int nsprite, bool enable); + +/*! + \brief Gets the collision status of a given sprite + \param nsprite Id of the sprite [0, num_sprites - 1] + \returns true if this sprite is involved in a collision with another sprite + \remarks Collision detection must be enabled for the sprite to get checked + \see TLN_EnableSpriteCollision() + */ TLNAPI bool TLN_GetSpriteCollision (int nsprite); + +/*! + \brief Returns runtime info about a given sprite + \param nsprite Id of the sprite [0, num_sprites - 1] + \param state Pointer to a user-allocated TLN_SpriteState structure to fill with requested data +*/ TLNAPI bool TLN_GetSpriteState(int nsprite, TLN_SpriteState* state); + +/*! + \brief Sets the first sprite drawn (beginning of list) + \param nsprite Id of the sprite [0, num_sprites - 1]. Must be enabled (visible) + */ +TLNAPI bool TLN_SetFirstSprite(int nsprite); + +/*! + \brief Sets the next sprite to draw after a given sprite, builds list + \param nsprite Id of the sprite [0, num_sprites - 1]. Must be enabled (visible) + \param next Id of the sprite to draw after Id [0, num_sprites - 1]. Must be enabled (visible) +*/ +TLNAPI bool TLN_SetNextSprite(int nsprite, int next); + +/*! + \deprecated, use \ref TLN_EnableSpriteFlag (nsprite, FLAG_MASKED, enable) + \brief Enables or disables masking for this sprite, if enabled it won't be drawn inside the region set up with TLN_SetSpritesMaskRegion() + \param nsprite Id of the sprite to mask [0, num_sprites - 1]. + \param enable Enables (true) or disables (false) masking +*/ +TLNAPI bool TLN_EnableSpriteMasking(int nsprite, bool enable); + +/*! + \brief Defines a sprite masking region between the two scanlines. Sprites masked with TLN_EnableSpriteMasking() won't be drawn inside this region. + \param top_line Top scaline where masking starts + \param bottom_line Bottom scaline where masking ends +*/ +TLNAPI void TLN_SetSpritesMaskRegion(int top_line, int bottom_line); + +/*! + \brief Starts a sprite animation + \param nsprite Id of the sprite to animate (0 <= id < num_sprites) + \param sequence Reference of the sequence to assign + \param loop amount of times to loop, 0=infinite + \see Animations +*/ +TLNAPI bool TLN_SetSpriteAnimation (int nsprite, TLN_Sequence sequence, int loop); + +/*! + \brief Disables animation for the given sprite + \param index Id of the spriteto set (0 <= id < num_sprites) + \see Animations +*/ +TLNAPI bool TLN_DisableSpriteAnimation(int index); + +/*! + \brief Pauses animation for the given sprite + \param index Id of the sprite to pause animation (0 <= id < num_sprites) + \see Animations TLN_ResumeSpriteAnimation +*/ +TLNAPI bool TLN_PauseSpriteAnimation(int index); + +/*! + \brief Restores animation for the given sprite + \param index Id of the sprite to resume animation (0 <= id < num_sprites) + \see Animations TLN_PauseSpriteAnimation +*/ +TLNAPI bool TLN_ResumeSpriteAnimation(int index); + +/* \deprecated, use TLN_DisableSpriteAnimation() instead */ +TLNAPI bool TLN_DisableAnimation(int index); + +/*! + \brief Disables the sprite so it is not drawn + \param nsprite Id of the sprite [0, num_sprites - 1] + \remarks + A sprite is also automatically disabled when assigned with an invalid spriteste or palette. Disabled + sprites are returned by the function TLN_GetAvailableSprite as available +*/ TLNAPI bool TLN_DisableSprite (int nsprite); + +/*! + \brief Gets the palette assigned to a given sprite + \param nsprite Id of the sprite to query (0 <= id < max_sprites) + \returns Reference to the sprite palette + \see TLN_SetSpritePalette(), TLN_SetSpriteSet() +*/ TLNAPI TLN_Palette TLN_GetSpritePalette (int nsprite); /**@}*/ /** - * \defgroup sequence - * \brief Sequence resources management for layer, sprite and palette animations + * \defgroup sequence Sequence resources management for layer, sprite and palette animations * @{ */ + +/*! + \brief Creates a new sequence for the animation engine + \param name String with an unique name to query later + \param target For tileset animations, the tile index to animate + \param num_frames Number of frames + \param frames Array of TLN_Frame items with indexes and delays + \returns Reference to the new sequence or NULL if error + \remarks Use this function to create tileset or sprite animations + \see TLN_SetTilemapAnimation(), TLN_SetSpriteAnimation() +*/ TLNAPI TLN_Sequence TLN_CreateSequence (const char* name, int target, int num_frames, TLN_SequenceFrame* frames); + +/*! + \brief Creates a color cycle sequence for palette animation + \param name String with an unique name to query later + \param num_strips Number of color strips + \param strips Array of color strips to assign + \returns Reference to the created cycle or NULL if error + \remarks Use this function to create advanced palette animation effects + \see TLN_ColorStrip(), TLN_SetPaletteAnimation() +*/ TLNAPI TLN_Sequence TLN_CreateCycle (const char* name, int num_strips, TLN_ColorStrip* strips); -TLNAPI TLN_Sequence TLN_CreateSpriteSequence(const char* name, TLN_Spriteset spriteset, char* basename, int count, int delay); + +/*! + \brief Creates a name based sprite sequence + \param name Optional name used to retrieve it when adding to a TLN_SequencePack, can be NULL + \param spriteset Reference to the spriteset with frames to animate + \param basename Base of the sprite name for the numbered sequence + \param delay Number of ticks to delay between frame + \return Reference to the created TLN_Sequence object or NULL if error + \remarks Trailing numbers in sprite names must start with 1 and be correlative (eg basename1... basename14) +*/ +TLNAPI TLN_Sequence TLN_CreateSpriteSequence(const char* name, TLN_Spriteset spriteset, const char* basename, int delay); + +/*! + \brief Creates a duplicate of the specified sequence + \param src Sequence to clone + \returns A reference to the newly cloned sequence, or NULL if error + \see TLN_FindSequence() +*/ TLNAPI TLN_Sequence TLN_CloneSequence (TLN_Sequence src); + +/*! + \brief Returns runtime info about a given sequence + \param sequence Sequence to query + \param info Pointer to a user-provided TLN_SequenceInfo structure to hold the returned data + \see TLN_FindSequence() +*/ TLNAPI bool TLN_GetSequenceInfo (TLN_Sequence sequence, TLN_SequenceInfo* info); + +/*! + \brief Deletes the sequence and frees resources + \param sequence Reference to the sequence to be deleted + \remarks Don't delete an active sequence! + */ TLNAPI bool TLN_DeleteSequence (TLN_Sequence sequence); /**@}*/ /** - * \defgroup sequencepack - * \brief Sequence pack manager for grouping and finding sequences + * \defgroup sequencepack Sequence pack manager for grouping and finding sequences * @{ */ + +/*! + \brief Creates a new collection of sequences + \returns Reference to the created pack or NULL if error + \see TLN_AddSequenceToPack(), TLN_CreateSequence() +*/ TLNAPI TLN_SequencePack TLN_CreateSequencePack (void); + +/*! + \brief Loads a sqx file containing one or more sequences + \param filename SQX filename with the sequences to load + \returns Reference to the newly created TLN_SequencePack() or NULL if error + \remarks A SQX file can contain many sequences. This function loads all of them inside a single TLN_SequencePack(). Individual sequences can be later queried with TLN_FindSequence() + \see TLN_FindSequence() +*/ TLNAPI TLN_SequencePack TLN_LoadSequencePack (const char* filename); + +/*! + \brief Returns the nth sequence inside a sequence pack + \param sp Reference to the sequence pack containing the sequence to find + \param index Sequence number to return [0, num_sequences - 1] + \see TLN_LoadSequencePack(), TLN_GetSequencePackCount() +*/ TLNAPI TLN_Sequence TLN_GetSequence (TLN_SequencePack sp, int index); + +/*! + \brief Finds a sequence inside a sequence pack + \param sp Reference to the sequence pack containing the sequence to find + \param name Name of the sequence to find + \returns Reference to the sequence with the specified name, or NULL if not found + \see TLN_LoadSequencePack() +*/ TLNAPI TLN_Sequence TLN_FindSequence (TLN_SequencePack sp, const char* name); + +/*! + \brief Returns the number of sequences inside a sequence pack + \param sp Reference to the sequence pack to query + */ TLNAPI int TLN_GetSequencePackCount (TLN_SequencePack sp); + +/*! + \brief Adds a sequence to a sequence pack + \param sp Reference to the sequence pack + \param sequence Reference to the sequence to attach + \remarks Write remarks for TLN_AddSequenceToPack here. + \see TLN_CreateSequencePack(), TLN_CreateSequence() +*/ TLNAPI bool TLN_AddSequenceToPack (TLN_SequencePack sp, TLN_Sequence sequence); + +/*! + \brief Deletes the specified sequence pack and frees memory + \param sp Reference to the sequence pack to delete + \remarks Don't delete a sequence pack that has sequences currently attached to animations! + \remarks The attached sequences are also deleted, so they haven't to be deleted externally. + \see TLN_LoadSequencePack() +*/ TLNAPI bool TLN_DeleteSequencePack (TLN_SequencePack sp); /**@}*/ /** - * \defgroup animation - * \brief Animation engine manager + * \defgroup animation Color cycle animation * @{ */ + +/*! + \brief Starts a palette animation + \param index Id of the animation to set (0 <= id < num_animations) + \param palette Reference of the palette to be animated + \param sequence Reference of the sequence to assign + \param blend true for smooth frame interpolation, false for classic, discrete mode +*/ TLNAPI bool TLN_SetPaletteAnimation (int index, TLN_Palette palette, TLN_Sequence sequence, bool blend); -TLNAPI bool TLN_SetPaletteAnimationSource (int index, TLN_Palette); -TLNAPI bool TLN_SetTilesetAnimation (int index, int nlayer, TLN_Sequence); -TLNAPI bool TLN_SetTilemapAnimation (int index, int nlayer, TLN_Sequence); -TLNAPI bool TLN_SetSpriteAnimation (int index, int nsprite, TLN_Sequence sequence, int loop); + +/*! + \brief Sets the source palette of a color cycle animation + \param index Id of the animation to set (0 <= id < num_animations) + \param palette Reference of the palette to assign + \remarks Use this function to change the palette assigned to a color cycle animation running. This is useful to combine color cycling and palette interpolation at the same time +*/ +TLNAPI bool TLN_SetPaletteAnimationSource (int index, TLN_Palette palette); + +/*! + \brief Checks the state of the animation for given sprite + \param index Id of the sprite to check (0 <= id < num_sprites) + \returns true if animation is running, false if it's finished or inactive +*/ TLNAPI bool TLN_GetAnimationState (int index); -TLNAPI bool TLN_SetAnimationDelay (int index, int delay); + +/*! + \brief Sets animation delay for single frame of given sprite animation + \param index Id of the sprite with animation (0 <= id < num_sprites) + \param frame Id of animation frame to change delay in (0 <= id < sequence->count) + \param delay New animation frame delay to set + \see Animations +*/ +TLNAPI bool TLN_SetAnimationDelay (int index, int frame, int delay); + +/*! + \brief Finds an available (unused) animation + \returns Index of the first unused animation (starting from 0) or -1 if none found +*/ TLNAPI int TLN_GetAvailableAnimation (void); -TLNAPI bool TLN_DisableAnimation (int index); + +/*! + \brief Disables the color cycle animation so it stops playing + \param index Id of the animation to set (0 <= id < num_animations) + \see Animations +*/ +TLNAPI bool TLN_DisablePaletteAnimation(int index); +/**@}*/ + +/** + * \defgroup world World management +* @{ */ + +/*! + \brief Loads and assigns complete TMX file + \param tmxfile Filename of TMX file to load + \param first_layer Starting layer number where place the loaded tmx +*/ +TLNAPI bool TLN_LoadWorld(const char* tmxfile, int first_layer); + +/*! + \brief Sets global world position, moving all layers in sync according to their parallax factor + \param x horizontal position in world space + \param y vertical position in world space +*/ +TLNAPI void TLN_SetWorldPosition(int x, int y); + +/*! + \brief Sets layer parallax factor to use in conjunction with \ref TLN_SetWorldPosition + \param nlayer Layer index [0, num_layers - 1] + \param x Horizontal parallax factor + \param y Vertical parallax factor +*/ +TLNAPI bool TLN_SetLayerParallaxFactor(int nlayer, float x, float y); + +/*! + \brief Sets the sprite position in world space coordinates + \param nsprite Id of the sprite [0, num_sprites - 1] + \param x Horizontal world position of pivot (0 = left margin) + \param y Vertical world position of pivot (0 = top margin) + \sa TLN_SetSpritePivot +*/ +TLNAPI bool TLN_SetSpriteWorldPosition(int nsprite, int x, int y); + +/*! \brief Releases world resources loaded with TLN_LoadWorld */ +TLNAPI void TLN_ReleaseWorld(void); /**@}*/ #ifdef __cplusplus diff --git a/install b/install index ee703b11..d5f0cab2 100755 --- a/install +++ b/install @@ -8,18 +8,12 @@ tgtpath="/usr" echo "Installing $name $arch" include="Tilengine.h" -if [ $name == "Linux" ]; then +if [ "$name" == "Linux" ]; then library="libTilengine.so" incpath="$tgtpath/include" libpath="$tgtpath/lib" - if [ $arch == "i686" ]; then - srcpath="linux_i686" - elif [ $arch == "x86_64" ]; then - srcpath="linux_x86_64" - elif [ $arch == "armv7l" ]; then - srcpath="linux_arm" - fi -elif [ $name == "Darwin" ]; then + srcpath="linux_$arch" +elif [ "$name" == "Darwin" ]; then library="Tilengine.dylib" incpath="/usr/local/include" srcpath="darwin_x86_64" diff --git a/lib/Win32/SDL2.dll b/lib/Win32/SDL2.dll deleted file mode 100644 index 4666b11e..00000000 Binary files a/lib/Win32/SDL2.dll and /dev/null differ diff --git a/lib/Win32/Tilengine.dll b/lib/Win32/Tilengine.dll deleted file mode 100644 index 8f132882..00000000 Binary files a/lib/Win32/Tilengine.dll and /dev/null differ diff --git a/lib/Win32/Tilengine.lib b/lib/Win32/Tilengine.lib deleted file mode 100644 index 63089942..00000000 Binary files a/lib/Win32/Tilengine.lib and /dev/null differ diff --git a/lib/linux_arm/libTilengine.so b/lib/linux_arm/libTilengine.so deleted file mode 100755 index 89dd9fbe..00000000 Binary files a/lib/linux_arm/libTilengine.so and /dev/null differ diff --git a/lib/linux_i686/libTilengine.so b/lib/linux_i686/libTilengine.so deleted file mode 100644 index dc198194..00000000 Binary files a/lib/linux_i686/libTilengine.so and /dev/null differ diff --git a/lib/linux_x86_64/libTilengine.so b/lib/linux_x86_64/libTilengine.so deleted file mode 100644 index b0256071..00000000 Binary files a/lib/linux_x86_64/libTilengine.so and /dev/null differ diff --git a/lib/x64/SDL2.dll b/lib/x64/SDL2.dll deleted file mode 100644 index 4f6b1a69..00000000 Binary files a/lib/x64/SDL2.dll and /dev/null differ diff --git a/lib/x64/Tilengine.dll b/lib/x64/Tilengine.dll deleted file mode 100644 index b8e6c0b0..00000000 Binary files a/lib/x64/Tilengine.dll and /dev/null differ diff --git a/lib/x64/Tilengine.lib b/lib/x64/Tilengine.lib deleted file mode 100644 index 7e90a2bc..00000000 Binary files a/lib/x64/Tilengine.lib and /dev/null differ diff --git a/lua_game/game.lua b/lua_game/game.lua index f145faa4..4502b85c 100644 --- a/lua_game/game.lua +++ b/lua_game/game.lua @@ -2,7 +2,16 @@ require("tilengine_libretro") local ffi = require("ffi") -local tln = ffi.load 'tilengine_libretro' +local tln +if ffi.os == "Windows" then + tln = ffi.load("tilengine_libretro") +elseif ffi.os == "OSX" then + -- same file RetroArch already loaded (cwd is lua_game) + tln = ffi.load("./tilengine_libretro.dylib") +else + -- Linux + tln = ffi.load("./tilengine_libretro.so") +end x = 0 inputs = {0, 0} diff --git a/lua_game/libretro_test.sh b/lua_game/libretro_test.sh new file mode 100755 index 00000000..5876e705 --- /dev/null +++ b/lua_game/libretro_test.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +GAME="$ROOT/lua_game" + +case "$(uname -s)" in + Darwin) + CORE="$GAME/tilengine_libretro.dylib" + [[ -f "$CORE" ]] || CORE="$ROOT/src/tilengine_libretro.dylib" + + RETROARCH=( + /Applications/RetroArch.app/Contents/MacOS/RetroArch + ) + ;; + + Linux) + CORE="$GAME/tilengine_libretro.so" + [[ -f "$CORE" ]] || CORE="$ROOT/src/tilengine_libretro.so" + + RETROARCH=( + flatpak run org.libretro.RetroArch + ) + + flatpak override --user \ + --filesystem="$ROOT" \ + org.libretro.RetroArch + ;; + + *) + echo "Unsupported platform: $(uname -s)" >&2 + exit 1 + ;; +esac + +if [[ ! -f "$CORE" ]]; then + echo "Core not found: $CORE" >&2 + echo "Build it first: make -C src" >&2 + exit 1 +fi + +cd "$GAME" + +exec "${RETROARCH[@]}" -L "$CORE" "$GAME" -v diff --git a/samples/assets/forest/atlas-props.png b/samples/assets/forest/atlas-props.png deleted file mode 100644 index ed7bc0a1..00000000 Binary files a/samples/assets/forest/atlas-props.png and /dev/null differ diff --git a/samples/assets/forest/atlas-props.txt b/samples/assets/forest/atlas-props.txt deleted file mode 100644 index f5991c27..00000000 --- a/samples/assets/forest/atlas-props.txt +++ /dev/null @@ -1,7 +0,0 @@ -house,0,0,193,165 -mushroom-brown,79,166,25,33 -mushroom-red,155,166,23,28 -plant,42,166,36,36 -rock,105,166,49,29 -tree,0,220,213,223 -vine,0,166,41,53 \ No newline at end of file diff --git a/samples/assets/forest/atlas.json b/samples/assets/forest/atlas.json new file mode 100644 index 00000000..b4769977 --- /dev/null +++ b/samples/assets/forest/atlas.json @@ -0,0 +1,714 @@ +{"frames": [ + +{ + "filename": "bee/bee-1", + "frame": {"x":2,"y":2,"w":37,"h":39}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":39}, + "sourceSize": {"w":37,"h":39}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "bee/bee-2", + "frame": {"x":41,"y":2,"w":37,"h":39}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":39}, + "sourceSize": {"w":37,"h":39}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "bee/bee-3", + "frame": {"x":80,"y":2,"w":37,"h":39}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":39}, + "sourceSize": {"w":37,"h":39}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "bee/bee-4", + "frame": {"x":2,"y":43,"w":37,"h":39}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":39}, + "sourceSize": {"w":37,"h":39}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "bee/bee-5", + "frame": {"x":41,"y":43,"w":37,"h":39}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":39}, + "sourceSize": {"w":37,"h":39}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "bee/bee-6", + "frame": {"x":80,"y":43,"w":37,"h":39}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":39}, + "sourceSize": {"w":37,"h":39}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "bee/bee-7", + "frame": {"x":2,"y":84,"w":37,"h":39}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":39}, + "sourceSize": {"w":37,"h":39}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "bee/bee-8", + "frame": {"x":41,"y":84,"w":37,"h":39}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":39}, + "sourceSize": {"w":37,"h":39}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "carrot/carrot-1", + "frame": {"x":80,"y":84,"w":17,"h":19}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":17,"h":19}, + "sourceSize": {"w":17,"h":19}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "carrot/carrot-2", + "frame": {"x":99,"y":84,"w":17,"h":19}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":17,"h":19}, + "sourceSize": {"w":17,"h":19}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "carrot/carrot-3", + "frame": {"x":2,"y":125,"w":17,"h":19}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":17,"h":19}, + "sourceSize": {"w":17,"h":19}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "carrot/carrot-4", + "frame": {"x":21,"y":125,"w":17,"h":19}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":17,"h":19}, + "sourceSize": {"w":17,"h":19}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "chest/chest-1", + "frame": {"x":40,"y":125,"w":36,"h":25}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":36,"h":25}, + "sourceSize": {"w":36,"h":25}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "chest/chest-2", + "frame": {"x":78,"y":125,"w":36,"h":25}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":36,"h":25}, + "sourceSize": {"w":36,"h":25}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "enemy-death/enemy-death-1", + "frame": {"x":2,"y":152,"w":31,"h":29}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":31,"h":29}, + "sourceSize": {"w":31,"h":29}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "enemy-death/enemy-death-2", + "frame": {"x":35,"y":152,"w":31,"h":29}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":31,"h":29}, + "sourceSize": {"w":31,"h":29}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "enemy-death/enemy-death-3", + "frame": {"x":68,"y":152,"w":31,"h":29}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":31,"h":29}, + "sourceSize": {"w":31,"h":29}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "enemy-death/enemy-death-4", + "frame": {"x":2,"y":183,"w":31,"h":29}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":31,"h":29}, + "sourceSize": {"w":31,"h":29}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "enemy-death/enemy-death-5", + "frame": {"x":35,"y":183,"w":31,"h":29}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":31,"h":29}, + "sourceSize": {"w":31,"h":29}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "enemy-death/enemy-death-6", + "frame": {"x":68,"y":183,"w":31,"h":29}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":31,"h":29}, + "sourceSize": {"w":31,"h":29}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "hud/hud-1", + "frame": {"x":2,"y":214,"w":56,"h":11}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":56,"h":11}, + "sourceSize": {"w":56,"h":11}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "hud/hud-2", + "frame": {"x":60,"y":214,"w":56,"h":11}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":56,"h":11}, + "sourceSize": {"w":56,"h":11}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "hud/hud-3", + "frame": {"x":2,"y":227,"w":56,"h":11}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":56,"h":11}, + "sourceSize": {"w":56,"h":11}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "hud/hud-4", + "frame": {"x":60,"y":227,"w":56,"h":11}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":56,"h":11}, + "sourceSize": {"w":56,"h":11}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant-attack/piranha-plant-attack-1", + "frame": {"x":2,"y":240,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant-attack/piranha-plant-attack-2", + "frame": {"x":65,"y":240,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant-attack/piranha-plant-attack-3", + "frame": {"x":2,"y":287,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant-attack/piranha-plant-attack-4", + "frame": {"x":65,"y":287,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant/piranha-plant-1", + "frame": {"x":2,"y":334,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant/piranha-plant-2", + "frame": {"x":65,"y":334,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant/piranha-plant-3", + "frame": {"x":2,"y":381,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant/piranha-plant-4", + "frame": {"x":65,"y":381,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "piranha-plant/piranha-plant-5", + "frame": {"x":2,"y":428,"w":61,"h":45}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":61,"h":45}, + "sourceSize": {"w":61,"h":45}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-climb/player-climb-1", + "frame": {"x":65,"y":428,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-climb/player-climb-2", + "frame": {"x":2,"y":475,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-climb/player-climb-3", + "frame": {"x":41,"y":475,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-climb/player-climb-4", + "frame": {"x":80,"y":475,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-duck/player-duck-1", + "frame": {"x":2,"y":509,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-duck/player-duck-2", + "frame": {"x":41,"y":509,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-duck/player-duck-3", + "frame": {"x":80,"y":509,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-duck/player-duck-4", + "frame": {"x":2,"y":543,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-fall/player-fall-1", + "frame": {"x":41,"y":543,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-fall/player-fall-2", + "frame": {"x":80,"y":543,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-fall/player-fall-3", + "frame": {"x":2,"y":577,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-fall/player-fall-4", + "frame": {"x":41,"y":577,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-hurt/player-hurt-1", + "frame": {"x":80,"y":577,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-hurt/player-hurt-2", + "frame": {"x":2,"y":611,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-1", + "frame": {"x":41,"y":611,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-2", + "frame": {"x":80,"y":611,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-3", + "frame": {"x":2,"y":645,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-4", + "frame": {"x":41,"y":645,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-5", + "frame": {"x":80,"y":645,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-6", + "frame": {"x":2,"y":679,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-7", + "frame": {"x":41,"y":679,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-8", + "frame": {"x":80,"y":679,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-idle/player-idle-9", + "frame": {"x":2,"y":713,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-jump/player-jump-1", + "frame": {"x":41,"y":713,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-jump/player-jump-2", + "frame": {"x":80,"y":713,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-jump/player-jump-3", + "frame": {"x":2,"y":747,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-jump/player-jump-4", + "frame": {"x":41,"y":747,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-skip/player-skip-1", + "frame": {"x":80,"y":747,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-skip/player-skip-2", + "frame": {"x":2,"y":781,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-skip/player-skip-3", + "frame": {"x":41,"y":781,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-skip/player-skip-4", + "frame": {"x":80,"y":781,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-skip/player-skip-5", + "frame": {"x":2,"y":815,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-skip/player-skip-6", + "frame": {"x":41,"y":815,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-skip/player-skip-7", + "frame": {"x":80,"y":815,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "player-skip/player-skip-8", + "frame": {"x":2,"y":849,"w":37,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":37,"h":32}, + "sourceSize": {"w":37,"h":32}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "slug/slug-1", + "frame": {"x":41,"y":849,"w":32,"h":21}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":21}, + "sourceSize": {"w":32,"h":21}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "slug/slug-2", + "frame": {"x":75,"y":849,"w":32,"h":21}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":21}, + "sourceSize": {"w":32,"h":21}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "slug/slug-3", + "frame": {"x":2,"y":883,"w":32,"h":21}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":21}, + "sourceSize": {"w":32,"h":21}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "slug/slug-4", + "frame": {"x":36,"y":883,"w":32,"h":21}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":21}, + "sourceSize": {"w":32,"h":21}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "star/star-1", + "frame": {"x":70,"y":883,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "star/star-2", + "frame": {"x":85,"y":883,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "star/star-3", + "frame": {"x":100,"y":883,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "star/star-4", + "frame": {"x":2,"y":906,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "star/star-5", + "frame": {"x":17,"y":906,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "star/star-6", + "frame": {"x":32,"y":906,"w":13,"h":13}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":13,"h":13}, + "sourceSize": {"w":13,"h":13}, + "pivot": {"x":0.5,"y":0.5} +}], +"meta": { + "app": "http://www.codeandweb.com/texturepacker", + "version": "1.0", + "image": "atlas.png", + "format": "RGBA8888", + "size": {"w":128,"h":921}, + "scale": "1", + "smartupdate": "$TexturePacker:SmartUpdate:ba434a42332a93478f3b65eaa1729445:a5092b8b472b90b6dd0b0453be087dc8:cbce6b53f0f49e0bf15173c25c41f876$" +} +} diff --git a/samples/assets/forest/atlas.png b/samples/assets/forest/atlas.png new file mode 100644 index 00000000..5e68927a Binary files /dev/null and b/samples/assets/forest/atlas.png differ diff --git a/samples/assets/forest/background.png b/samples/assets/forest/background.png index b368e8b2..c897f677 100644 Binary files a/samples/assets/forest/background.png and b/samples/assets/forest/background.png differ diff --git a/samples/assets/forest/house.png b/samples/assets/forest/house.png new file mode 100644 index 00000000..f760dd5b Binary files /dev/null and b/samples/assets/forest/house.png differ diff --git a/samples/assets/forest/map.tmx b/samples/assets/forest/map.tmx index f684b977..e4b9e744 100644 --- a/samples/assets/forest/map.tmx +++ b/samples/assets/forest/map.tmx @@ -1,47 +1,30 @@ - - - - + + + + + + + + + + - eJztmQuOgzAMREvbVXv/E69WqrQ0SmKPEwcb5kmREOQzmCEEZ7uRrSiWNlKZrdF7vBUcofVhvObJ1jiW2iB1R6H/5o8bJWYZ/Nfj6dy/F/Tfv579sUaL5VvtBf2HjTcy7st4rUepRaMPvQfP7yX99829UaLMdyUtLT2tM+9hhv8ix7eFl85s7+MZ/Ff2Q//FpjVXaOeQSP6r9XNl/2WgN+dJ7WZ/4+i/6zHiv9nU/kfKtbO2n9pxVOg/7PyqeO3HQNYyZ/Vfa19Cs1+B5BpW5gQt/lvFKv8h+03auu9P0WDJoaLeqOXWkLpIvgzJsVn6sCLp6mnb++9HKJb5T6MNuXeL/6warG16erS60fbIezAbdE3Z8h8S75HnWtPnVVbzN6bm/ZX6QM7Xxs3iP6QfsgbP53PEe66tT//FoPd8Rv0R+RlH1paBqOuGo9ckWiJrywDjNwbjNwbjZyf63JwBxs8O/TfOVeI30ytZ1qYZyBI/aa/pIdSp/VNLuUnmXPzxzKOfuRBfGOfr8guWyQLX + eJztWsuKZEUQDcQHMrj3N3pABjci6IyIuBFBHZ1Fr7XVT7hj4wPtbS18UO0PqMNUL50RH/SgqDiKj41SioW/YQR1gz73VGTezK7bM11TFXDIvJl5M7IyTkZG3G6R0yn/KmaKmy32W9wsxM+EXzK6RgTUGc1t7d8rflDsFGC3fW9G8+TWxOL6uPxW8V3QvpG8nKUyEuTgpEUJ94wfNfxzGyL/Zgl9+237VPG33Dr+uT4u/1T8FbRvJC2psxvJT7Loi9iO7CeNH8g96/8vsx63Ifs/1uPnYCbH83/Rmr/Ib1WRPDjAHOskU4nPbiTOv1Lfh+NKuGeCd6+fCZzzQLq82Zd6/zeSeL0237IcXDf+/SPy8Y8KLnkc3621MYpx75rEd2Df/eu27eOeCfLPz0Qfx2v8307w24eM15B/uXhmXeRuxT2Ke6G8T+pilM8lzb2J5DnoPrCEeyYc/yGcH6zPf0Mp//i31+xFnxj/HlA8rrjQ4okl51xlWfY+MO7luOUczPmo0tjedGH8x35pKnN+sL5a/9d3Py8jvt+N4nKLZedcZVmWfx7zpXKMSaK9Jq4yX3Fe5r4C/d1Uun4plfd4/xD8e7F6h2JpZMO/IcT457Yu/eYXjUv5wOcUzyteUFyUrt/jPMG57vNzzn4a/J9LI13+DZFbr6PgNxe2f+5eRv+Uyz1eVrwic9u/Kl2/l8pTfR2csw/BP4P7YoP5w/sVZ2QeS58p3LdGuvwb6vvOqsuWlOdlmHdgntEX781kkX+5vW9kbnfMPaJvLxzvcTkU/9wXG15TvN6usyaWsd/E/LvTOLjVolQ8J7PzbTZO5WWPSTcm8/F9vHN+joO+65Le+0bmdsfcI/ftZSpx3joU/0ZQN//3UrvOIfiXy8W2AGeD+q36noO6kF/ehjyK8nt+39HI/Ezb+bY9TsU6HJN5yTmC8zLKRUZydIchzgfrtXWh7fl7S2lZyq/3MuCxKf93Vw/sN/XxD/ll8ki7P3juL1Dbo8H7KXv38ZXHRHrdFthuz8iLHerHd3Gui4QU/zAmi+4l1G9zsN+z5ybQh++kdOEdPJX8dzouS/OLhxTnWnC9ofEp/8c+gLlymRCN4/IZ2mcsvf5sRmduPal+rEd62d5uR95X7k/NxfzLnd9Se6LfG7fP+P4bwTspXbnvf7X+bxS0me4nFU+14HpD75v/ezcA24S5wvxL2YPLmvg04meqL8eHGt1DInVHuo8t5d9Vwlhi/o1g3hE9c9tx/B/fpT4PPlt5qV3jOKhHc1jfPozz31e6Rxvkwf6ptA/5ty9dDqIN92SRDxEvDOzTzkn6rjxuPcc/H/OwHPHOfs9EujxsToHdVhlo8z1Jx+N7kj/nzr+JHPm+q9LlTQ2m0vVpyOncvVlTz/HPxzwtXd45/Lk5BTZcZezSvvq5flPxluJtxTsFex3dv+wL+9oRkf4UV06qbuvclqOzNIa6w/Zkc/8uxz/fW//bhj2/r/hA8aHiI9jrEv65jSZBW+QjOWZM6fe+bRh/kvVJW8eYD9eJfx+83XZcVSBvMLb5RPGp4jPFFanzfweyyK3atkg/+qeDdvxJ17dlMebztU5l2O/d6wjMGzC2+VLxleJrxTdS7v/GBM4X+S5LtbH+Ze9f91W1725D3Xnoc+X2Y4M6/uFdafhV8Zvid8UfUnf/InjeUkT6HZeOUZ/K3Fd5u3MoNd77t2VxbT5Xbj82KIPFLoeKGxl4f1/+6zY7hPcO6dnnQXD/IfSVtt/I9GGd/4cVfzvWvR/bMd7jb5i3247rjl2pi4f4m3PtmTnuOmvWmHr3Tufb/xEu1Bc= - - - eJztWsuKZEUQjbX4QAb39zd6QAY3IsxDRNyIoI7OotZOjX7CjI0PtLcFvqj6Ah9M9VJn8EEPioqj+NgoKhb+ghF0Bh11KiJvZt3b01PWDThk3nxX5smTkdlNdHfaP4wF43bCLOF2IX4E/JTpawKwfXptS/q3jO8Y4wLspnoLaCc3JjTtD8OvGd846YPl7TSEnlkOzhNKuCf8qOGfrqHl3yLob5bS/2T8RXeOf9ofhr8z/nDSB4st2rue/UCrWoTriDop/LDck/x/M+PRNUT9w350HyxoPf3zxnwzP1VF1vTQxjZZtHc9U/6Vap8tV8I9MXv26p6wbe7TMm9mVK9/E/LHK+115WDTsf6m2d9E73zPwBDL4dla66MI9z4j/wxsO391bdu4J2b5p3uijeM1+jd2fnuf/lpj4jl/ZlvsXsZ9jPtN+ADV+SifUsy9OeU5qBpYwj0x9P8slB/YX43+jZ3f3qe/1jAeYpxjXEh4vGObm2xNx/rCvRy3lIM5jSr17aUv6/+hLik/sL9a/Ws7n7tYk8KrjGsJXdvcZGs61lefL7pjzIP0Gr9KtOI8HWqF1TvUpejeU6t/ObywziQ5NvCvHxP+6VqXvvl55SINfJbxHON5xkVa1j28JyjXtX28s98N+qeG/Ovjbr2NZt9ccP1z57LVp9zd4yXGZTpc+yu0rHvRPVXHgXf2PvgnUC0WiB4+yDhFh770qcJ5Q/719b6z6bZD5fcye++w94w2f29Bq/zLzb2slay7vXt4by/o72HYF/9UiwUvM15J42wK501/E/Lv/8bBnYRS0zuZ7G9Z4+hedpaWfTIt38Y75efUybtB8dwr/+zdI/f2Et1b++LfxMRF/15M42yC8Ue/yeNf7i62Y3Daid+p9xzbl+WXplkeefd7rK+QOZE9Lftb5jjyddAn0xDvCMpL7y4yoaMzzOK8M17l35j895bSsJRfb2eAZSP9u6cFJfyz/BJ7NM2P3fcXIO0xp3603m18xTJev7oWNl2+LS/GkG/r2rYuAiL+WZ/MO5ds/9IG6p58K9cRWifqy57Bbe906+if9P0w40wCxq9C+Uj/UAOQK9cAXjkMn4Z5tqHGn8n0mRtPlG/jXr+43rqOOK+YH7WF/Mvt39L1tLo3Td+2/qtOnaiv3Ptfrf5NnDTp+wnGkwkYRy0W/XvLAa4JcgX5F60HhjX+qcfPKC/Hh5q++0R0RqrGlvLvOmBKPv8mpt0JfGPaOvqHZ6m2Y78lvJTGOHXiXhuSNzPl9PeVztGAPFCfSvMs/2a0zEG7hnu0ygePFwLUtDMUn5XrxnP80zKP0BHv5PfMaZmHpefDAB92zfco9sf3KL/PlX9zOtK+67TMmxqgpllO587NmniOf1rmKVrmnUK/B/51wy7Mq+7r1xivM95gvFkw1975i1rYlm7h9R9x5bjiMs4RHe2lqYkrhvO3O/90bvVvG/L9LuM9xvuMD8xcl/BP12jupHkaiT5j1L/mjUz544zPU9z6fHac9u+DJ72OmwrLG+vbfMj4iPEx4xOq0799WuVWbZrXv9Wn/VT+uOMjWvX5dKx9v3dvI+y9wfo2nzO+YHzJ+IrK9W8KwPsinmVRGvbf9fxVraqtOzJx5aG2Nfh+/fHPnpWCnxm/MH5l/EZ1568FtlsKr3/FpTXiqlWarhyKymv+iFbHpm0N/OsO8V0OGLcy0Py2+6+u2YGpdwDf2o4F5h+YvNL0W5k8G8f/YbW/3cY136Zbfw/fME96Hbcdu1TnD+Gbc+2eWXecXXw2+/9gJz3fx4n/AMQ6LDY= - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/samples/assets/forest/middleground.png b/samples/assets/forest/middleground.png index c97cc147..e5977bd2 100644 Binary files a/samples/assets/forest/middleground.png and b/samples/assets/forest/middleground.png differ diff --git a/samples/assets/forest/mushroom-brown.png b/samples/assets/forest/mushroom-brown.png new file mode 100644 index 00000000..147e5886 Binary files /dev/null and b/samples/assets/forest/mushroom-brown.png differ diff --git a/samples/assets/forest/mushroom-red.png b/samples/assets/forest/mushroom-red.png new file mode 100644 index 00000000..57d32b44 Binary files /dev/null and b/samples/assets/forest/mushroom-red.png differ diff --git a/samples/assets/forest/plant.png b/samples/assets/forest/plant.png new file mode 100644 index 00000000..df1e33ff Binary files /dev/null and b/samples/assets/forest/plant.png differ diff --git a/samples/assets/forest/player.png b/samples/assets/forest/player.png deleted file mode 100644 index c01a7cfe..00000000 Binary files a/samples/assets/forest/player.png and /dev/null differ diff --git a/samples/assets/forest/player.txt b/samples/assets/forest/player.txt deleted file mode 100644 index 0c4145ba..00000000 --- a/samples/assets/forest/player.txt +++ /dev/null @@ -1,17 +0,0 @@ -player-idle-1,0,0,37,32 -player-idle-2,0,32,37,32 -player-idle-3,37,0,37,32 -player-idle-4,37,32,37,32 -player-idle-5,0,64,37,32 -player-idle-6,37,64,37,32 -player-idle-7,74,0,37,32 -player-idle-8,74,32,37,32 -player-idle-9,74,64,37,32 -player-skip-1,0,96,37,32 -player-skip-2,37,96,37,32 -player-skip-3,74,96,37,32 -player-skip-4,111,0,37,32 -player-skip-5,111,32,37,32 -player-skip-6,111,64,37,32 -player-skip-7,111,96,37,32 -player-skip-8,0,128,37,32 \ No newline at end of file diff --git a/samples/assets/forest/props.tsx b/samples/assets/forest/props.tsx new file mode 100644 index 00000000..1cfe4170 --- /dev/null +++ b/samples/assets/forest/props.tsx @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/assets/forest/rock.png b/samples/assets/forest/rock.png new file mode 100644 index 00000000..02c2dd4b Binary files /dev/null and b/samples/assets/forest/rock.png differ diff --git a/samples/assets/forest/tileset.png b/samples/assets/forest/tileset.png index 7284db49..4eff6dd0 100644 Binary files a/samples/assets/forest/tileset.png and b/samples/assets/forest/tileset.png differ diff --git a/samples/assets/forest/tileset.tsx b/samples/assets/forest/tileset.tsx index aed1869c..1eff197e 100644 --- a/samples/assets/forest/tileset.tsx +++ b/samples/assets/forest/tileset.tsx @@ -1,4 +1,4 @@ - + diff --git a/samples/assets/forest/tree.png b/samples/assets/forest/tree.png new file mode 100644 index 00000000..da3eeb57 Binary files /dev/null and b/samples/assets/forest/tree.png differ diff --git a/samples/assets/forest/vine.png b/samples/assets/forest/vine.png new file mode 100644 index 00000000..0c3b9807 Binary files /dev/null and b/samples/assets/forest/vine.png differ diff --git a/samples/assets/sc4/Simon.png b/samples/assets/sc4/Simon.png deleted file mode 100644 index c60ffc5b..00000000 Binary files a/samples/assets/sc4/Simon.png and /dev/null differ diff --git a/samples/assets/sc4/Simon.sqx b/samples/assets/sc4/Simon.sqx deleted file mode 100644 index cf9f2c64..00000000 --- a/samples/assets/sc4/Simon.sqx +++ /dev/null @@ -1,9 +0,0 @@ - - - - - 1,2,3,4,5,6,0 - - - - diff --git a/samples/assets/sc4/Simon.txt b/samples/assets/sc4/Simon.txt deleted file mode 100644 index 54243c88..00000000 --- a/samples/assets/sc4/Simon.txt +++ /dev/null @@ -1,8 +0,0 @@ -idle = 0 0 32 48 -walk1 = 32 0 32 48 -walk2 = 64 0 32 48 -walk3 = 96 0 32 48 -walk4 = 128 0 32 48 -walk5 = 160 0 32 48 -walk6 = 192 0 32 48 -jump = 224 0 32 48 \ No newline at end of file diff --git a/samples/assets/shots/zss1.png b/samples/assets/shots/zss1.png new file mode 100644 index 00000000..bf28f3fc Binary files /dev/null and b/samples/assets/shots/zss1.png differ diff --git a/samples/assets/shots/zss2.png b/samples/assets/shots/zss2.png new file mode 100644 index 00000000..f03cdb44 Binary files /dev/null and b/samples/assets/shots/zss2.png differ diff --git a/samples/assets/smw/sequences.sqx b/samples/assets/smw/sequences.sqx deleted file mode 100644 index b1482943..00000000 --- a/samples/assets/smw/sequences.sqx +++ /dev/null @@ -1,7 +0,0 @@ - - - - - 0,1,2 - - \ No newline at end of file diff --git a/samples/assets/smw/smw_sprite.txt b/samples/assets/smw/smw_sprite.txt index c35e5701..e2857255 100644 --- a/samples/assets/smw/smw_sprite.txt +++ b/samples/assets/smw/smw_sprite.txt @@ -1,13 +1,3 @@ -image0,0,0,16,32 -image1,16,0,16,32 -image2,32,0,16,32 -image3,48,0,16,32 -image4,64,0,16,32 -image5,80,0,16,32 -image6,96,0,16,32 -image7,112,0,16,32 -image8,128,0,16,32 -image9,144,0,16,32 -image10,160,0,16,32 -image11,176,0,16,32 -image12,192,0,16,32 +walking1,0,0,16,32 +walking2,16,0,16,32 +walking3,32,0,16,32 diff --git a/samples/assets/sotb/SOTB.sqx b/samples/assets/sotb/SOTB.sqx deleted file mode 100644 index fd8fd25d..00000000 --- a/samples/assets/sotb/SOTB.sqx +++ /dev/null @@ -1,7 +0,0 @@ - - - - - 1,2,3,4,5,6 - - diff --git a/samples/assets/sotb/SOTB_fg.tmx b/samples/assets/sotb/SOTB_fg.tmx index 9e7ec487..71d5904c 100644 --- a/samples/assets/sotb/SOTB_fg.tmx +++ b/samples/assets/sotb/SOTB_fg.tmx @@ -1,9 +1,9 @@ - + - + - eJztnQec1MQXx2cPsaCgCKgo6lEEBLtiFw+xIioWUKzYQBFRbFhQV7EjqCig2HvD3hX1/Nt777333sXy/73LPPJ2biaT3c1y6M77fL6fZHeTvDKTNy+T5E6pIEGCBAkSJEiQIEGCBAkSJEiQIEGCBKm85EANaGaszwGagznBXGBuvT63Zh6x3iLaNi/3nRfMV4I9LUGr6Jh5toeXQYIECRIkSJAgQcqX+cECoLVYX1DFtR2ob4Ml0Ra0M9bxex2xkNh3YbCIPmZ7sChYLKU9VDt2UFH9x/bwslolxKQy4ur7i2v+61Lt/s8OEuIfJEiQphDUbHmq75bAOkE13ZKgFnRUUZ1HNSDVeZ1AZ9AFLKW3odquq4ryV2u9XTf9XTe9Txe97J7CnqVBD9VQ59T3xHIZsCxYDiyvvyc9K4AVwUpgZbGclbL0LNQzu8VkVvleSZ1Jfb+7StdfZ6VUu/8k5v2JeVPsI+9F0P6rgF6VMlDZbbTdB6F7K3Q992+Kf5AgQbKVprjO49xDNcSqKso/nHtWU1Htt7pe1urfadu2en0NFdV+VNt10/vSso3eh+rANfV3tE8n/ZnmAV3+Uo2zFlgbrAN6q+i+M91nXlcfi+rP5TV0rUz3o+v0clbVgFyP9QHrqWg86ZuwfTnt2xQxSZqnnZW+V0pnmr5PfbWc8TdtDZBGgv8zr38K5sj4HgM/eyL3kXNq8l4EfV4fbKDic8PctxI2yvm91tF2ea6/OVfWquziz5JlOwQJEiRb4VzMOSyLXOSS9cQ65SLKPbKGoBqNP9eqaExgaJygOm5DsJGK8hPPA3YzoH07arguXE3vR3Uj5zUaz2gc21jF+XMT0A9sqqJ6R95nJp3t9fF5Dqw/2AxsruK6p1URMaSYNFdxO6SJP4/BW4ABKhpPttS2b2Vs62tfGYPFVGVj0tLhm2nD1np9Pcu2WfqeVmw6B2hK1enr+0zS9YpL6pS7BihFqtl/Ogfo+mcbMFBF1z5y/nvVyJeGZ0/ITtucWjsVXzPJffncsNWPJdrYMD9vs7F/ZOfMudZaVVhzU27MKv4sdSrbfhgkSJBshO9B8DUg57BK1X9cS9AzZPRuha7/CmqIruIz5yZznonyFI0VbcU+XVRcC/J8IH0/SMP5jfzk+o9qDhrPqHbYVsVzXNupKM8PVlG9Y+ZI1sP5fHuwg4ryK9U7O4KdioiLWf9RG9iuj7lGojjyGKznR/O8hN66nVUU5zTti7bIyxjsUuGY7BwNCTOlpcOGrfX6AOF3S8N3qkeHiGWxvqcVqXNXsJtecrwHlKAzTd+fR38vr1dcwjGi2rku2qfOHP9pzKdrp2JrjWr1n98B43NgdxXVgHTtY85/ryHsdM2p8TWTrTZr4TMmpY10fvZ22Li9Kpxr5Tm+LOLPUsl+GCRIkOyE70HwGE7nP+fsPTLUQ/UN1XycowgaK+jat1Y1riHo85L6sznPVKvi+UBa76T36aS35XqP7/vuqT9zfYTcWDcU68NUVOfsBfYWy+FgHzBCsy8YqY9j2oJj1cvPXPNQfUS1DsWQ72P2SYgNxYLGDIoLjRuu620aY7cUcdwP7K8axqQ8LbkO4t/TtC+NHaN0PA7Q+xcTkwON2PpiQvVfS6P+tNmwi/BnCx0/nuNg37ke5WWxvqeVLONNkrbv12p84y9fV3H9TPu008fhPttZxXNsbFO1+59Ue3BddRA4WEX9i65jBqp4DlDWVr45NTo3ZP4w5wLJpmLrIZuN1DY9o/OwzvaMhm2Otdz4s1SiHdKKvH/AObWXappnhIME+TcIj92cp6guolrpEBXl6x5FHMucw+Iahs5Lqv1oDKc6Yi8NjRW2GgLUUw2hr08baoo1o/X61bWd/B4wr9M1aidNrYprwLb62IPEsXrq+o++Gw0OVfE4Nlysk42HqSi3Ur1zuGqc19uoxmMY3/ekWofHRaoBB0AvPzvVR8eXclMvHYu1dQwIzrecFznP0zZDVeOxmG3mOoji11Gla1+q70bpWAzT/hcTkyO0njaCpJjIOUD2y2aDrOvGgCNFPI8CRzt859+y6Nsy7lnFm86DtH2ftqH+6ht/OS5DNdxP6bg83rbTS6oHqC5bNPhvfb+Vfc6rqE/S9cgx2k+urwaqOJ9w/eabU6NtZf6Qc4Fco6Wty9PYqONZYONyyj7HV278SY5VhTmq3HZIK1xzyvsHPFdN1yJcFwYJEqRQOKcP0ks6Zzkfc04tdQ6Lcwafl1Q7yDqCxvyx4DhVOLb0jGq0errPcryK8s+B2i7KJxupuPbqKtbp93ba9qW0T7Wq8RhzApYngpO0DQSPYyeLdbZ1hFg/RcVjGudW+Tdp6DPXOuPEtlvoWFJuGiliwjGWcwN8DDkW5FVUQ5+qbWd7OH5sM80BrBPFrCF+adp3lPZ7PJigj9GvgjE5TUXP6fURftlsGK910meqQQ/V/pPe08EZDt/5tyz6NtvHvk4wdE4oId7c79L0ff6N+oxtTpjrAI7NiSru23wtYc69cF2yRvDf+n4r+3yQivvixMi+hmcYuL5ie805tlpln1PjPCbnymk7WZvxeWJ7jjUrG9kens9kW+bSNlL8bb8lXS9wO8gcVW47pJEzVdSvzlJRDcz3D7i/TlJRLDmeoQ4MEiSWISo+Twfpdc65PKaXOodlu77ncYPmjqgOo7GDrlt319/xeXu8/ryvXj8w2r+eryeJrgKa99tIb8f6aEljzGR9HOkT3YscJuCxTNaoZCvVEWepeLybIuLFuVXWPP1VnMOHiW2Js8E5Kh73Jqs4v3KdJO+bkl98P4jz61RVWJtx7M7Vth2vfZM50Ne+fH/3ZGGjHN+zjgmNDzvpfsTX7jYbzgPnax08Lzg6Y9+L7dsTNNy3J2h7ZN/Oou+PFj4foI9jm4PJq2jMvUDFfWOI1sM+yrG3s6B98N/6fiufa2zbYL3kPEHfU501yeGn67kI1/dkI9dm9KzEhSp+jtUl5djI8bfNFQ4R38vfeus5QJfkVfbtkCTsP0G5iPom1cBcC5vXqxzPLYvQESTIf134HhuNjZRv+Xw5RsVjZzFzWHwfaKxyX9+b4/VEbceOqrDu4Gta2renzpOUi/j+Ii3NZ8cpv1Ceo+tb/rsxrG+y8Il8pJpjmNbFfp+hbeDag5dDVHxfktYp51ykj0s2cYx4uYM+7mBxjIvBJSqeAztXxISvi2X9R9fpPAeYV1F+3UfbJ+PD71ZQ7TRGNc6BvvbdWxWOtbuLYwyvQExGaJ3Uf7jv2GyYJI7N84JyfjAL34vt23Q8W98mvdy3s+j74y2+kl1mDcTjID+fyccYavGRx16qNWr1str9r1WF77fy8WT/mqqX0ha22fRzswTfbd+zbXze03XSpcr+npTpc6k2cvxtc4Wi7iv4jWtA13PJWbeDT/IqmvujvMF5x5Yb2A7ON7551SBBqkmoDuH5Fjm3It+rTJrD4u34OZGRep1yDc+78bX+aH1sflaFxniuheR8Do37l4HLwRXgShXljc30srf4PyCErP8of/L8A93HYN2TVeEcFumjHMg5g78za5yrwNX6t4vEdnsZNnKMrhF6hujj8DHY12vBNBXVg3IO0Hxujp/XNscm0n0duB7coO2XsbpR28H6fO17kyrMnXIcOSOjmJyk25XrtQmCAxw2yJqAa8ObM/a9lL7Ndpt9m33Juu+zT/I+KEteRdcFXP/ycxXmmCvXa1Vcb1S7/+b7rXyOyWNN1ZyqbbXZRv5NEf6RncO1T1yf3CJ+5xqF7eQcwO9KoU6p28pT/5Vro22ucJReN3/jGtb1HGDW7ZAk7L+py9VvuB/Tc8s8DxhqwCBBCsdSOf6a71W65rD4d9u9DfNan48/WO9PSxpz+unjyvmcW8Ft4HZwB7hTRePJNuL+CuWQ/pGuhqWeH2wYfzpqSPdkw8+7wN2ae8B0cK/gPof++1X0dy/qwQPG+v/Agyr6mxgcE4JrpYdUXPfJ+k/OAcq5MjmvyTkxr6Ice5aw82HdHhMstk5M2b6PqMbzbbLem6Z94OWj4DGH/xQ3Wx6mvx0zQhzzQa3vEd2uNhto/ByjYdsfz9j3Uvr2QNX4nrPs25Xo+1y/Era/n0PjIc27cK3xhIgjjblUO/FYPFL4Uu3+16rC91v5WHQc6n9T9Trd1zzUYdv9qrB/kd9jVTwXxjbJuSnbXCC/S0XLNPN/5dpomyuk4w1z/KbfS6vr7rEri3ZIkryK8qAZX5ffA4UNtJ3v3nqQINUiZl30pIrrIjnvkzSHRWMG5TOe96NznM9reU+PriX7ac4W+9frY8ga6inwNHgGPAueA8+DF8CLKr5fQfTXurcRS8bmH69T3eeqY1z6zfknOs5L4GXwCnjV0EU1ymvgdfAGeBO8Bd4G74B3VZQjzflDs5ZeKPqbKTPn/9LEalzK9qXay5xvu1q0+dW6nabpmCXVf2SLLQ+TjvdUYQ3IyykOG6ifjdFLV61dru+l9m2+By/7dpK+6aq8vj9exeOlbXzsoArnQt4XcTRtGit8qXb/aSnntfL6WHQcc37pAodt1N9s5wTvzzlwoiUmcp8PVPQuFS2T5v+ytNGsoT5U9tqP6/oWsKl5tLvTrizawSctLb67/Jbvr9F2SbV1kCDVJHIuRdYpH6l4XPDNYfG4Ia9p+bw23yk9TcXX/B+DT1SU/80a6lPwGfgcfAG+BF+Br8E34FvwHfge/AB+VFG99xP4WUX3fvt5/PtF2PCrYYNL/3TLMTmv2XT9Bn4Hf4AZ4E/wF/gb/EMNkItyZA7LGtAsF/lvzg2g7svnVXzd67LP5+8cucY+Uw3K8zV0H/YJ4xgXpziGjJsrD3MNaM4F3uSwgZanaf02XUn9ZJwnFpXo277Yl9v3bfc/WejeIo3dwzUcP5dN/H5Ftftvvl8s3ysw57FcdpnPnbk+F5N/fM+pyXugNP9HNRC/A+2y03bemjWUb46ylXL//x6SrNrBJ2RAB6Fv7wS/yY7muYiLVHJtHSRINcmcOCfmyjWuU+bGd/Pk4mvyFlifF8yXc89h0XneEr+3yjXeh7ahsYTy/bWa+fH9AqA1WBC0AW1BO7AQWBgsAtqDRcFioANYHCwBlgS1oCPoBDqDLmAp0DUXXfP5/HPZ4NKfFAefrm4J+shnGUM+Jl+fd4jqv3z3XJTzXPaVYgONy7IOpWWpfpBdrjzM4zvXgea8k2kDfU+6qF4otp+Ma4K+7dNXbt+nMdI1d0F/e437xtK56Dmu4R6bxgb/Z9Y3fNy8iuoK2zyWy66k2JWaf3z3KPPaPlm/8bu3Ljt9+SepH5h5yGVXVu2Q4PpMofc/uP4b5fC7XhXeM2Bfk2rYIKVLD8S2J1gGLAuWA8uDFcCKuaa2LogpK6FNVgargF5gVbAaWB2skYvH7DWxvhZYO5d+Dkvuw2M8QecqjQXr4PveYF1QB/qA9UBfsD7YAGwINgIbg01AP7Ap6A82A5uDLcAAsCXYCmwNtgEDc37/XDa49CfFwadrUIK+vkYM+Zic3zcRbUY51mVfqTbQ3/igvLhtGcfguLnGH+5LZvxorKMxgvtITvcXYlqJ/WRcE/Rtn75y+/4tyj93QX2DxkWef/HZVO3+0zbmcXlujeexuMZw2ZUUu1LzD8XBNdfmev+B28dlpy//JPUD1jXKFfgKtENaPVRvbufwm+Y5ueaj8519zYf6L3MZjLhuD3YAO4KdwM5gFzAE7Ap2yzW1lUGk7I722APsCYaCYWAvsDedk7n43KG6YB8wAuwLRoL9wP5gFDgAHAgOAgdb9uHaj/LUhZpD8P1ocCg4DBwOjgBjwJHgKHA0navgGHAsGAuOA8eDE8CJ4CRwMjgFjAOngvG5KJ/4/HPZ4NKfFAefrgkJ+sYYMZSxPdPSbi77SrWB6j/Ki6eV6QfZZcvDsr4040d9YZSAY0Bjgi9mtjhQvM5sgr7t01dO3yc5NuU5TdvxnIjPpmr3/1jLcfMqmv/jeSyuMVznXFLsSs0/FAfXXBvbx3Wp2T4uO33nkq8f0Dm1XS5lI2TQDsWKzW/qq1zz0Rwg+xokezkdcT0DTKS+As4Ck8BkMAWcDc4JsZ+tZCra41xwHjg/Fz0jcQG4EFwkzh2qCy4Gl4BLwWXgcnAFuBJcBa4G14BrLfvQvQDO/5QPaJtp4DpwPbgB3AhuAjdT3gW3gtvA7eAOcCe4C9wN7gHTwb3gPnA/qAcP0HkOHtQ5xeefywaX/qQ4+HQ95PHZjCHXMTZx2VeqDVSfUV58uEw/SGx5WNaXrvhR3zBj4IuZLQ4ct1ndt336yun7xQrPifhsqnb/fSJrEtc5lxS7UvMPxSHtvVazfVx2+s4lXz8oRbJqhzRi85v6Ktd89Gwl+xqkMvIIYvsoeAw8Dp4AT4KnwNMh7rOdPIM2eRY8B54HL4AXwUu5hv89Ub+eUnU8Tr4MXgGvgtfA6+AN8CZ4C7wN3gHv5uJagvfha0de0jbvgffBB+BD8BH4GHwCPgWfgc/BF+BL8BX4GnwDvgXfge/BD+BH8BP4GfwCfgW/5ZL9+91jg02/9MmMg0/XHx59ZgyTxBWfUm1gmZGBH7Y8LOvLYvqRT5crDpXq2yy2vu2LWzl9vxLnN9lU7f4XI66+lhS7cvJPWjHPGZedvnPJd26WI1m2g0tcfnPNtxK2WTkbVUEs8idi/Bf4G/xDbVqjVA7UgGY1TW1dEJvMgXZpDuYEc4G5wTygRU3D/4Gqo21oSWP3vPhuPtAStALzgwVAa7AgaAPagnY10fk2Q+zT2tL+C+G7hcEioD1YFCwGOtRE+y6O5RJgSVAr9u+I9U6gM+gClgJdQTfQHSwNeoCeNcn+LVOTbINN/4yEOPh0LevRZ8YwSVzxKdUGluUy8MNlm9kn0vSjUnUV07dpWYxNZj/gvu2LWxZ9v1jx2VTt/qeVGTm3XUmxKyf/pBWzfVx2pjmXknwpR7JqhyRx+b0xXedF/9uwrm9477disjzivQJYEawEVgargF5gVbAaWD2jtg6SjayB9lgTrAXWBuuA3mBdo51o7K7Dd31ozAB9wfpgA7Ch3nYjLDcGm9RE15NUS/A+LumH3zYF/cFmYHOx7RZYHwC2BFuBrfVv22A5EAwC24LtwGCwPdgB7Ah2Ajvr7X3+uWyw6Zc+mXFIG8skn2UMfWKzLwsbsjiGaRtLUvxs/agUXdxP0vpBkkXfLkZfqT4VK2ltqnb/04rNrqTYpdGfFIe0Yp4zSfFz6UtzbpYqWbeDS7LqN0GKl10Q6yFgV7Ab2B3sAfYEQ8EwsFdoj9lS9ka7DAf7gBFgX0s7jcR3+4H9wShwADgQHKS3PRjLQ8Domuh68tCaeB+XHIbfDgdHgDHgSHCU3v5oLPPgGHAsGAuOA8eDE8CJ4CRwMjgFjAOngvFgAjhN6E3yz2WDTb/0yYxD2lgm+Sxj6BObfVnYkMUxXLYlxc/Wj8rRldYPkiz7dhp95fpUrPhsqnb/04rNLl/sfPrTnIs+Mc+ZpPi59KU5N8uVrNrBJVn3myDp5XTE+gwwEZwJzgKTwGQwBZwNzgntMdvKVLTNueA8cL6lnS7AdxeCi8DF4BJwqdjuMqxfDq6oabyPTa6Mck79VVheDa4B19Y0zOPX8zbT8Pk6cD24AdwIbgI3g1tqCuu/W8Ft4HZwB7jT0Gvzz2eDqT9NHJJi6dNni2GS2Owr14asjuGyrdh+VI4unx+l2iT3sYlPXxY+FStJNlW7/8WIaVeafODSX8y5mCS29rHFz6cvrS/lSFbt4JKs+02QdHIXYn03uAdMB/eC+8D9oB48AP4X2mO2lQfRNg+Bh8Ej4FHRVo9h/XHwBHgSPAWeBs+IbZ7F+nPg+ZrG+5jyAr57EbwEXgavgFfBa+B1sf0bWH8TvAXeBu+Ad8F74H3wAfhQ8xH4GHwCPgWfGXpt/vlsMPWniYNLVxqfzRj6xLQvCxuyOobNtmL7UTm6fH6wZN23ffqy8qlYcdlU7f4XK6ZdafKBS38x52KS2PKGaacv7mlzW7mSVTu4JOt+EySdfI5YfwG+BF+Br8E34FvwHfge/BDa418nP6LNfgI/g190+/2K5W/gd/AHmAH+BH+Bv8E/tF0z+/Fy+L7G8VvQH/QH/UF/0B/0B/1Bf9DfNPqb4fc5QHMwJ5gLzA3mAS3AvGA+0BK0AvODBUBrsCBoA9qCdlrPQoa+hfF5kQQbgv6gP+gP+oP+oD/oD/qD/srobw8WBYuBDmBxsARYEtSCjqAT6Ay6gKVAV9ANdAdLgx6gJ1gGLAuWA8uDFbTOFbFcCawsbFgF672C/qA/6A/6g/6gP+gP+oP+Wa7//wLDLDM= + eJztnQeUFEUTx3sPyYIiQQnqkVEUTIgRD1BRRMUAioqiKElEARUD6gpiQkAQUDHnhAFzQD0/c84555yzGL5/XXcxdX3dM7O7cwd3TL33ezO3OzMVurqru3dQpVJJJZVUUkkllVRSSSWVVFJJJZXqJBlQBGpZ5yuB2qAOqAvqmfN6hvrivIG+NivvbQhWzsOeRqCxfmaW7eFjKqmkkkoqqdRUWQWsCpqI89VUUGtBaVMciWaguXWO70uIFuLe1cEa5pktQSvQOqY9VMvbKF2P2R4+rqiSxqRyxJf7axpquqzo/i8PksY/FRbU0CzV27VwTlCNXRsUg7ZK112qyVR324H2oAPoaK6hWttJ6XxqYq7rbD7rbO7pYI5dYtizDlhXldWd0q44rgfWB91Ad/M56dkAbAg2AhuLY1XKOlWoZ3mLSVX5Xpk6w3K/i4qXr1UpK7r/JPb+XcMY98i9Orp/E9CjsgxUbhtd+4S090jz6+oU/5oscdutMoRzgcb0TZXOB86FnkrX4s3Msdh8T9c2M+ebK12LqdZ2NvfSsam5h+ryFuYzuqed+ZvWyb55H9WcLcFWYGvQS+l9ctoX38Y8i+YD3Q00l6T98xJzrKqazPWxN+ijdP/uG3J9IfPcZRGTsH2MqvS9snTGyX3K1ULGwyT7dur/0vlouTUk78Hxb2XyHrnmlHt19Pe2YDsV9A373sqwUa5/m+jrsjwf4rGyWCUXf5ZlWWOqk5Qof7tVlvQR56SbckGO6VQz+e9ipfsoQ/2W6ur2oJ/S+cLr5M4WdG9bA9fpnuY+quOcZzS+0LiygwryeUfQH+ykdP2R++Kks6V5Pq8RB4CdwS4qqEONc+hfFJPaKhij4vRNHhN3BQOV7t+7Gdt3t67l5/KYYT9fxqC1qtyYNPL4Ztuwhznv47g2Sd/jikvnQEO+OqNynwmbP/qkRCXbt1dk/6kP0Hx0TzBI6bmo3B/aVPtS9lsZ2elaczZXwRxW3st9w1XP87SxbP/KZeMAbefSvYhiVX4ORGNjUvFnKVFVX2Oqi/CYR+NdiSqrSyV2m1E7Ub0rND9cwmM7/QZJ70qZelxuTO8k/uZcsddhlDfUd5uJezqooDbzepk+H2zgfKN85HpM8aDxhcbyvVSwBtxb6X43ROn6Y+cs6+H+tQ/YV+l8p/qzHxiaQ1zsekz92jV/5PajOPKYaPYPsnyE3pL9lY4z74nxHJjHDNmuaIusjMEBlRyT/XXqLZVGHhv2MOcDhd+NLN9pfjBMHHP1Pa5InQeCg8yR4z0wD51xcr+++VzOH31SmX17RfWf3+nkPjBc6ZpMc1F7f2hzYadvzclzWFetbBBlTEwbqX/28ti4jyq/F8Fr4CTiz7Ksa0x1Ea6FPOYVKx17ijvnSXsVzFO5LyUhVG+oBnPOENR36yj3mN7T2NVBVVyHFatgvVxsbO1ojnQt11/epz7E/M31CrlaMgLnI5WuO6PAaHEcAw4FYw2HgXHmObYteFap/JtrENUrqj0Hq2DftXdIbCgW1IcpLtSPffNRbj+O4+HgCFU2RmTpyHWJv+c9MY5vJxW068HmmdSXx5t4TDD35xKTiVZso2JC9biRNR9w2XCA8GdXEz9eA7DvPD/gY66+x5Uk400SN/eLDVHjYWX37Zrqf1gt4Dp3JDhK6fyieeUgFayRZa2LWnNS35Djh71WJptyrU8uG6ltuup+WOL6Tcm1B1Fo/Fkqox3iitxf4zG1h1o275jEEe4zIwycMzSWcoyamyO1IfWlVjk8317jcU2hOFEtpjGVxvVRBuq7rjEdlNKYbuZvZWP8Fvq8lK6h3OH3rPmc5nDtDOQT1+Rm5tmDxbO6mnpMn00Cx6hgXBkjzsnGY5XOdao/x6mK/aypqjim8D7tUBFzqslYQ5Twb290pPGJcqWHicVWJgYE5z/nKfc7bj97bGSbuS5R/NqKuLJ97PfRRj/1Haq3400sRhr/c4nJ8UZPU0FYTOQamf1y2SDr7GRwgojnieAkj+/8XRzfo0TGPal4Uz+Im/t0DeVr1HhYWX27pvvven+Yfc4qnZM0PzzZ+Mn1bpAKxhOup1FrTrpWjh9yrcw1M+48KY6NJp7lbOym3GvgQuNPMkWVH6MKbYe4wnMAub/Gezk0N+Q6vbwItx2NsWeB0wynq2D8t+cvnEvUDvmu8bgNOU40lstxncbgqeAUVb6vd9U1s5T2haYpnQ/UZycaO/upoBZ2Euf0fXPjD83zaBwoVhX7/KnC/9EGHlfOEOds61hxfqYKxhjOdflvsOhvrj3TxbUUA4oL5co4ERMeR+TcmZ8h+2ZW6TkNtx/bw/Fjm2mOvLWOWVn8OK6DzXGEiAXrHm/8ngFmmmf0r8SYzFL6d97ewi+XDTOMTvqb5gScv6T3bDDb4zt/F8f3qNxm+9jXmZbOmXnEm/MuTu7zd5Qzrj2Tyu7bNd1/1/vD7PORKsjFOdq+st9cuN6xvfYatFi515w8jsm9JLpO1kruJ673IJKyke3h9T7bUtfYSPF3fRc2f+N2kGNUoe0QR85ROq/mKj0n4f01ztd5SseS47k81OWs0nG6GCxQga0Ucx5HZbzaCyhe+a7xXPNf7se0tqK6SH2Z5nXDzWds2zTz92HmfKK+v5TnW0QnAa2L+5nrWB8dqc/PN8/h8YH8pb3TkQIeW+ScgWylcX2uCsafc1WQZ5zrsgYNUEGfGimuJc4D56tgHJqvgnznuiX3eckv3r/ifOf2Y3s4dhcY26YZ32ROsg2DzTmPcRwL3o8+Q9gox9ukY0L9dajJI57bumy4EFxkdPC6eVLCvuea2zMNnNszjT0yt5PI/UnC5wnmOa41SlZVbd+uaf673h/mvsa2DTFHHifoc6p78zx++n7H8X1ONnKtpN92LlHBexA+KcRGjr9rLT1MfC6/62XWyD7JquTbIUzYf4LGIspNmpPw3MReP3A8d8tBR2UJ286/AbKtFCcerzhmHC/Kj2JzzGWNx/tWU5V//muPn3OMPfup8nWA53x0b1eTt5QbvB9KR/tdELKf8o7mf/zvpFjffOHvMKVrwEiji9tttrGBawEfh6lgH5XOKQcuNc8lmzhGfNzXPHeIeMZl4HIVrBEvEDHheaOsxzSP5TVyVul85/aT8eF3paiWTVYVc3KQ8XuSsP9kVX6PQI59w8UzxlRCTMYanZQ/nDsuG+aJZ/O6Wa6fk/A919ym57lym/RybieR+zMcvpJddk2q6r5d0/wvVuXfH+bnyfxaYI7SFrbZ9nPnEN9dn7Nt3O9p3nqFcr/3aPucr40cf9daWtThct9xTfa915J0O0RJVum1MY0bPO64xga2g8ebqH2HqpCs0mM5j1m8V2zHSZ4XqyBHwtZ4PMbx7wzjzDm1Pa9LeS5MYyLFhn/roDGXa5Nc79A4fCW4ClwNrjE27WyOvcR/p4uQ9ZjymefntO/Cuuer8ms80kc5yW3In9k151pwnfnuUnHdKMtGjtH1Qs8w8xx+Bvt6A1iodH2Wa2T7d1d+/8IeK0j3jeAmcLOxX8bqFmMH6+M2k2tL+V7yIlU+l2W/np1QTE437cr1c6ZggscGOUZzrb41Yd/zyW22285t9iXp3Gef5L4tS1ZVfd+uSf7b7w9zH5PPWmA4y9jqso38O1f4R3aOMT5xvbhNfM81g+3kMYDffUTdKNk9oh4XaqNrLT3enNvf8ZzC9zty0u0QJuy/rcuXN5zH9N4Lr5OXdU1mH2juwvnxlApspThRvnP8KJ94fyNsjcdjnGsvxp4L83g7xNxPx/OMDfRcud65HdwB7gR3gbuV7t97iv0gatMBWlfZ0ayfy8aDtgbSTWODHMPvAfca7gOLwf2CBzz6H1T633mUgoes8/+Bh5X+NyAcE4Jr1yMqqMOyHss1slxLynU/52hW6ZyfK+x81LTHTIetcyy/Zb2T68fHVMX1qKy/C40PfHwcPOHxn+Lm6hf0b6XGimc+bPQ9ZtrVZQONZ5MNbPuTCfueT24PUhX3yGVuV0bu83yCcP17sars2zXJ/2JV/v1hfhY9h/JvgTmnfdhjPLY9qMrnF/k9VQVrRbZJrt1ca2V+N5KOcdbHhdroWkvT80Z6vjPvmZZ0ibAriXYIk6zS46AdX5/fg4QNdF3UbwFVJeSDnE98qII+J2vV00rnC+dK1BrvcnP9OHEPx1nuQdJcq7/hPHF/qXmGrGnPgGfBc+B58AJ4EbwEXlbB/goxwOjeUxwZuw4/Lc6pDvvqik+/vT6j57wCXgWvgdctXVQz3gBvgrfA2+Ad8C54D7yvdM7a62t7btNC/xuhpevjOLGa7vGf5yE8DlMttNej14k2v86000ITs7B6TLa4+gXp+ECVr8l8PNdjA+XZZHP0zX0K9T3f3ObfDGRuh+lbrArL/RkqGL9c41VV9+2a4j8d5bova55Fz7HXXxd7bKN8c/UJvp/HwDmOmMh7PlL63Ug6hq2Pk7TRrmkfK3ct5nlWA9hUW9/utSuJdoiSRg7ffX7L91HpurC5TlUK7ZdQvMcYqA7QPqFcb8jawe9URK3xuB/LOR/H2X5nd5YK5sSfgs+U7o92TfscfAG+BF+Br8E34FvwHfge/AB+BD+Bn5Wuv7+AX5Xeq+4f4tsn4Ddhw++WDT79ix3P5Dxz6foD/An+AkvA3+Af8C/4jxolo3M2g2MRqJXR/ttzZ9ThbFYF80KffVH+rpSp6DPNCXg9s8jkhXzGZTGeIePm6xdck+218iKPDXScZfS7dIXlyfSIWHBOJ5nbUbEvNPdd+7Usy0Pfro7+2+9vy/eE7HWezy77d0vf37mMP1G/c8o9W1ofU03id8x9drr6rV3TotbwjZX/v69HklQ7RAkZ0EboGx3iN9lRO6O5VIXPdapS6N+HdcloH9bJ6N8KyNY6OK+bqVg76mV0m9CctQHOG4KVM/41HsW9Eb5vnKl4D11DfZv63w2GVfD5qqAJWA00Bc1Ac9ACrA7WAC1BK9AatAFrgrXA2qAYtAXtQHvQAXQEnTJ6ThTmW/2M3waf/rA4ROnqHKKPfJYx5Gfy/LWNrsdZbj+fffnYQOOknBfQMV8/yC5fv+DxluuyvS6zbaDPSReN37nmyfQY7cHrsaRyO0pfoblPY5Zvbr889O3q6D/XG35uVulx3rXO89kVFrt8x5+oPdWssU/WU3632Wdn1PgTlgf2OOSzK6l2CHF9qdD7XFyPx3v8LlXl99TYV9ecYl183hWsB9YH3UB3sAHYMBPHovxkivGF5zAbQdfGYBPQA2wKeoLNwOYZPYZugeOWYKtM/DWevIfH3FkmdtQ3t8bnvcA2oAT0Bn1AX7At2A5sD/qBHcCOoD/YCQwAO4NdwK5gINgN7A72AHuCQZlo33w2+PSHxSFK1+AQfX2tGPIzub/taLWfz758baB/00J5ulcBz+C4+cYDrsd2/GjsoTzkHMmYfCEW5pkn02O0B9uTVG5H6Ss0929T0XP7Zdm3q6P/dI39XF578jqPx3yfXWGxy3f8oTj41qK+95m4fXx2Ro0/YXnAusb7Al8J7RBXD9X/vT1+0z4A12Dq7+xr1orrEHy2D9gX7AeGgv3BAWAYOBAclIlrVe5CfvC8Yjj0HAwOASPASDAKjKY4ZYJx+lAwFhwGxoHDwRFgPJgAJoIjwVGOe7gWU95cYjgan08Cx4BjwXHgeDAZnABOBCdR7MDJYAqYCk4B08Cp4DRwOjgDnAmmg7PAjIxu3yjffDb49IfFIUrXzBB9k60Yytie42g/n3352kD1mPJ0VoF+kF2ufiHrvR2/S0weMhwD6qNRMXPFgeJ1Toy2536aVG5H6Ssk97nPLs99uzr6P8Xx3KzS62Ne5/GY7+tzYbHLd/yhOPjWomwfzxPs9vHZGdWXovKA+tTemZiNkEA75CouvylXuQbTGpl9teVsfDYbzCE/wVwwD8wH54LzwPk5+J6P8LxiAbgAXAguyuh99ovBJeDSTDBOXwYuB1eAK8FV4GpwDbgWXAeuBzc47qG9C+6P1D50zUJwI7gJ3AxuAYvArdQPwO3gDnAnuAvcDe4B94L7wGJwP3gAPAhKwUMUd/CwaeMo33w2+PSHxSFK1yMRPtsx5LriEp99+dpA9ZLy9NEC/SBx9QtZ733xo9ywYxAVM1ccOG5RfnA/TSq3o/QVkvvVoW9XR/+jRNYIX58Li12+4w/FIe7esN0+Pjuj+lJUHuQjSbVDHHH5TbnKNZh+m2dfXfIYPn8cPAGeBE+Bp8Ez4NmEbIwjz0HX8+AF8CJ4CbwMXgF/ZoJx61XwGngdvAHeBG+Bt8E74F3wHng/E4ztfA/PrfhI13wAPgQfgY/BJ+BT8Bn4HHwBvgRfga/BN+Bb8B34HvwAfgQ/gZ/BL+BX8Bv4HfyRifYtzAaXfumTHYcoXX9F6LNjGCa++ORrA8uSBPxw9QtZ73PJoyhdvjhE5XUDTN/6KFWSa26zuHI7Km6F5P7y0rdrmv+5iC/XwmJXyPgTV+w+47Mzqi9F9c1CJMl28InPb67BYf8P9r/x/T/gX/Af2VOkVAYUgVpFydgXV1aCvtqgDqgL6oH6oAFYr6jsv59XQmNpQ5yvDBqBxmAVsCpoAlYDTUEz0LxI+79E3NPE4VMLfLY6WAO0BK1Aa9CmSN+7Jo5rgbVBsbi/Lc7bgfagA+gIOoHOoAtYB6wLuhZF+xZmg0v/kpA4ROlaP0KfHcMw8cUnXxtYuiXgh882Oyfi5FG+uqJi0cf8RsXHXGyy84BzOypuSeR+rhKnb6/I/seVJRm/XWGxK2T8iSt2+/jsjNOXwnwpRJJqhzDx+b0Dzbv1fwu2pK/nt+nuuHYDsCHYCGwMNgE9wKagJ9gsITujZHPo2QJsCbYCW4NeYBuhn8bSEvzdm/ow6Au2BduB7c11/XDcAexYpOdbNLbzPT7pj+92AgPAzmAXce2uOB8IdgO7gz3Md3viOAgMBnuBvcEQsA/YF+wHhoL9zfVRvvlscOmXPtlxiBPHKJ9lDKPEZV8SNiTxDNs2lrD4ufIoH12cJ3H9IEkit3PRl69PuUpcm1Z0/+OKy66w2MXRHxaHuGL3mbD4+fTF6Zv5StLt4JN88+YAXDcMHAgOAsPBweAQMAKMBKMStjVKRkPfGHAoGAsOs/SPw9+HgyPAeDABTARHmuuOwvFoMKlIz7eOKQru8cmx+O44cDyYDE4AJ5rrT8IxC04GU8BUcAqYBk4Fp4HTwRngTDAdnAVmgJlgltAb5pvPBpd+6ZMdh7hxDPNZxjBKXPYlYUMSz/DZFhY/Vx4VoiuuHyRJ5nYcfYX6lKtUZd+ujv7HFZddUbGL0h+nL0aJ3WfC4ufTF6dvFipJtYNP8s2bs3HdbDAHnAPmgnlgPjgXnAfOT9jWOLIAOi8AF4KLLP0X4+9LwKXgMnA5uEJccyXOrwJXF1W8xyXX6BwovRbH68D14Iaisn2HUr5mIf6+EdwEbga3gEXgVnBbUfl6fDu4A9wJ7gJ3W3pdvkXZYOuPE4ewOEbpc8UwTFz2FWpDUs/w2ZZrHhWiK8qPfG2S97gkSl8SPuUqVdm3q5v/uYhtV5zxwKc/l74YJq72ccUvSl9cXwqRpNrBJ/nkzT247l5wH1gM7gcPgAdBKXgI/K8SbI2Sh6HzEfAoeAw8bmx4AscnwVPgafAMeBY8J2x8HucvgBcd99jyEj57GbwCXgWvgdfBG+BNcf1bOH8bvAPeBe+B98EH4EPwEfjY8An4FHwGPgdfWHpdvkXZYOuPEwefrjg+2zGMEtu+JGxI6hku23LNo0J0RfnBknRuR+lLyqdcpar6dnXzP1ex7YozHvj059IXw8Q1bth2RsU97thWqCTVDj7JJ2++xHVfga/BN+Bb8B34HvwAfgQ/JWxnvvIz7PgF/Ap+Mzb9juMf4E/wF1gC/gb/gH/Bf3RdLffzMvi8yPNdqj/Vn+pP9af6U/01VX8+UgvPWgnUBnVAXVAP1AcNQEOwMmgEGoNVwKqgCVgNNAXNQHNjUwvLttXx9xoh9qb6U/2p/lR/qj/VXxP1+6QlrmsFWoM2YE2wFlgbFIO2oB1oDzqAjqAT6Ay6gHXAuqArWA+sD7qB7mADY8OGOG4ENhY2bYLzHrVS/an+VH+qP9Wf6l8x9P8f9V3Ziw== diff --git a/samples/assets/tf4/TF4_seq.sqx b/samples/assets/tf4/TF4_seq.sqx deleted file mode 100644 index 2649dd38..00000000 --- a/samples/assets/tf4/TF4_seq.sqx +++ /dev/null @@ -1,24 +0,0 @@ - - - - - 7,8,9,10,11,12,13,14 - - - - 15,16,17,18 - - - - 19,20,21,22 - - - - 28,29,30,31,32,33 - - - - 34,35,34,35,34,35,36,37,38,39 - - - \ No newline at end of file diff --git a/src/Animation.c b/src/Animation.c index 5ad96f63..65b63044 100644 --- a/src/Animation.c +++ b/src/Animation.c @@ -20,6 +20,7 @@ #include "Engine.h" #include "Palette.h" #include "Tables.h" +#include "Debug.h" /* linear interploation */ static int lerp (int x, int x0, int x1, int fx0, int fx1) @@ -29,141 +30,98 @@ static int lerp (int x, int x0, int x1, int fx0, int fx1) static inline void blendColors (uint8_t* srcptr0, uint8_t* srcptr1, uint8_t* dstptr, uint8_t f0, uint8_t f1) { - dstptr[0] = blendfunc(engine->mod_table, srcptr0[0], f0) + blendfunc(engine->mod_table, srcptr1[0], f1); - dstptr[1] = blendfunc(engine->mod_table, srcptr0[1], f0) + blendfunc(engine->mod_table, srcptr1[1], f1); - dstptr[2] = blendfunc(engine->mod_table, srcptr0[2], f0) + blendfunc(engine->mod_table, srcptr1[2], f1); + dstptr[0] = blendfunc(engine->blend_table, srcptr0[0], f0) + blendfunc(engine->blend_table, srcptr1[0], f1); + dstptr[1] = blendfunc(engine->blend_table, srcptr0[1], f0) + blendfunc(engine->blend_table, srcptr1[1], f1); + dstptr[2] = blendfunc(engine->blend_table, srcptr0[2], f0) + blendfunc(engine->blend_table, srcptr1[2], f1); } static void SetAnimation (Animation* animation, TLN_Sequence sequence, animation_t type); static void ColorCycle (TLN_Palette srcpalette, TLN_Palette dstpalette, struct Strip* strip); static void ColorCycleBlend (TLN_Palette srcpalette, TLN_Palette dstpalette, struct Strip* strip, int t); -static void ReplaceTiles (TLN_Tilemap tilemap, int srctile, int dsttile); -/* main loop tasks */ -void UpdateAnimations (int time) +/* updates animation state */ +void UpdateAnimation(Animation* animation, int time) { - int c; - TLN_Sequence sequence; - TLN_SequenceFrame* frames; - struct Strip* strips; - - for (c=0; cnumanimations; c++) - { - Animation* animation = &engine->animations[c]; - if (animation->enabled==false) - continue; + TLN_Sequence sequence = animation->sequence; + TLN_SequenceFrame* frames = NULL; - sequence = animation->sequence; - if (animation->type == TYPE_PALETTE) + if (animation->type == TYPE_PALETTE) + { + int i; + struct Strip* strips = (struct Strip*)&sequence->data; + for (i = 0; i < sequence->count; i++) { - int i; - strips = (struct Strip*)&sequence->data; - for (i=0; icount; i++) + struct Strip* strip = &strips[i]; + /* next frame */ + if (time >= strip->timer) { - struct Strip* strip = &strips[i]; - /* next frame */ - if (time >= strip->timer) - { - strip->timer = time + strip->delay; - strip->pos = (strip->pos + 1) % strip->count; - strip->t0 = time; - if (!animation->blend) - ColorCycle (animation->srcpalette, animation->palette, strip); - } - - /* interpolate */ - if (animation->blend) - ColorCycleBlend (animation->srcpalette, animation->palette, strip, time); + strip->timer = time + strip->delay; + strip->pos = (strip->pos + 1) % strip->count; + strip->t0 = time; + if (!animation->blend) + ColorCycle(animation->srcpalette, animation->palette, strip); } - continue; + + /* interpolate */ + if (animation->blend) + ColorCycleBlend(animation->srcpalette, animation->palette, strip, time); } + return; + } - if (time < animation->timer) - continue; + if (time < animation->timer) + return; - frames = (TLN_SequenceFrame*)&sequence->data; - animation->timer = time + frames[animation->pos].delay; - switch (animation->type) - { - case TYPE_TILEMAP: - ReplaceTiles (engine->layers[animation->idx].tilemap, - frames[animation->pos].index, - frames[animation->pos + 1].index % sequence->count); - break; - - case TYPE_SPRITE: - TLN_SetSpritePicture (animation->idx, frames[animation->pos].index); - break; + frames = (TLN_SequenceFrame*)&sequence->data; + animation->timer = time + frames[animation->pos].delay; + switch (animation->type) + { + case TYPE_SPRITE: + TLN_SetSpritePicture(animation->nsprite, frames[animation->pos].index); + break; - case TYPE_TILESET: - TLN_CopyTile (engine->layers[animation->idx].tileset, frames[animation->pos].index, sequence->target); - break; + case TYPE_TILESET: + animation->tileset->tiles[sequence->target] = frames[animation->pos].index; + break; - /* Fall through */ + /* Fall through */ /* Stop warning GNU C compiler */ - case TYPE_NONE: - case TYPE_PALETTE: - break; - } + case TYPE_NONE: + case TYPE_PALETTE: + break; + } - /* next frame */ - animation->pos++; - if (animation->pos == sequence->count) + /* next frame */ + animation->pos++; + if (animation->pos == sequence->count) + { + if (animation->loop > 1) { - if (animation->loop > 1) - { - animation->loop--; - animation->pos = 0; - } - else if (animation->loop == 1) - animation->enabled = false; - else if (animation->loop == 0) - animation->pos = 0; + animation->loop--; + animation->pos = 0; } + else if (animation->loop == 1) + animation->enabled = false; + else if (animation->loop == 0) + animation->pos = 0; } } -/** - * \brief - * Checks the state of the specified animation - * - * \param index - * Id of the animation to check (0 <= id < num_animations) - * - * \returns - * true if animation is running, false if it's finished or inactive - */ bool TLN_GetAnimationState (int index) { - if (index >= engine->numanimations) + if (index >= engine->numsprites) { - TLN_SetLastError (TLN_ERR_IDX_ANIMATION); + TLN_SetLastError (TLN_ERR_IDX_SPRITE); return false; } TLN_SetLastError (TLN_ERR_OK); - return engine->animations[index].enabled; + return engine->sprites[index].animation.enabled; } -/*! - * \brief - * Starts a palette animation - * - * \param index - * Id of the animation to set (0 <= id < num_animations) - * - * \param palette - * Reference of the palette to be animated - * - * \param sequence - * Reference of the sequence to assign - * - * \param blend - * true for smooth frame interpolation, false for classic, discrete mode - */ bool TLN_SetPaletteAnimation (int index, TLN_Palette palette, TLN_Sequence sequence, bool blend) { - Animation* animation; + Animation* animation = NULL; int c; struct Strip* strips; @@ -175,15 +133,16 @@ bool TLN_SetPaletteAnimation (int index, TLN_Palette palette, TLN_Sequence seque return false; } - animation = &engine->animations[index]; - - if (animation->sequence == sequence) + if (engine->animations[index].sequence == sequence) return true; /* validate type */ if (!CheckBaseObject (palette, OT_PALETTE) || !CheckBaseObject (sequence, OT_SEQUENCE)) return false; + animation = &engine->animations[index]; + if (!animation->enabled) + ListAppendNode(&engine->list_animations, index); SetAnimation (animation, sequence, TYPE_PALETTE); animation->palette = palette; animation->blend = blend; @@ -204,23 +163,9 @@ bool TLN_SetPaletteAnimation (int index, TLN_Palette palette, TLN_Sequence seque return true; } -/*! - * \brief - * Sets the source palette of a color cycle animation - * - * \param index - * Id of the animation to set (0 <= id < num_animations) - * - * \param palette - * Reference of the palette to assign - * - * \remarks - * Use this function to change the palette assigned to a color cycle animation running. - * This is useful to combine color cycling and palette interpolation at the same time - */ bool TLN_SetPaletteAnimationSource (int index, TLN_Palette palette) { - Animation* animation; + Animation* animation = NULL; if (index >= engine->numanimations) { @@ -239,77 +184,36 @@ bool TLN_SetPaletteAnimationSource (int index, TLN_Palette palette) return true; } -/*! - * \brief - * Starts a tileset animation - * - * \param index - * Id of the animation to set (0 <= id < num_animations) - * - * \param nlayer - * Id of the layer to animate (0 <= id < num_layers) - * - * \param sequence - * Reference of the sequence to assign - * - * \see - * Animations - */ -bool TLN_SetTilesetAnimation (int index, int nlayer, TLN_Sequence sequence) +bool SetTilesetAnimation(TLN_Tileset tileset, int index, TLN_Sequence sequence) { - Animation* animation; + Animation* animation = NULL; - if (index >= engine->numanimations) + if (index >= tileset->sp->num_sequences) { TLN_SetLastError (TLN_ERR_IDX_ANIMATION); return false; } - if (nlayer >= engine->numlayers) - { - TLN_SetLastError (TLN_ERR_IDX_LAYER); - return false; - } /* validate type */ if (!CheckBaseObject (sequence, OT_SEQUENCE)) return false; - animation = &engine->animations[index]; - SetAnimation (animation, sequence, TYPE_TILESET); - animation->idx = nlayer; + animation = &tileset->animations[index]; + SetAnimation(animation, sequence, TYPE_TILESET); + animation->tileset = tileset; TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Starts a tilemap animation - * - * \param index - * Id of the animation to set (0 <= id < num_animations) - * - * \param nlayer - * Id of the layer to animate (0 <= id < num_layers) - * - * \param sequence - * Reference of the sequence to assign - * - * \see - * Animations - */ -bool TLN_SetTilemapAnimation (int index, int nlayer, TLN_Sequence sequence) +bool TLN_SetSpriteAnimation (int index, TLN_Sequence sequence, int loop) { - Animation* animation; + Sprite* sprite; + Animation* animation = NULL; - if (index >= engine->numanimations) - { - TLN_SetLastError (TLN_ERR_IDX_ANIMATION); - return false; - } - if (nlayer >= engine->numlayers) + if (index >= engine->numsprites) { - TLN_SetLastError (TLN_ERR_IDX_LAYER); + TLN_SetLastError (TLN_ERR_IDX_SPRITE); return false; } @@ -317,107 +221,42 @@ bool TLN_SetTilemapAnimation (int index, int nlayer, TLN_Sequence sequence) if (!CheckBaseObject (sequence, OT_SEQUENCE)) return false; - animation = &engine->animations[index]; - SetAnimation (animation, sequence, TYPE_TILEMAP); - animation->idx = nlayer; + sprite = &engine->sprites[index]; + animation = &sprite->animation; + SetAnimation (animation, sequence, TYPE_SPRITE); + animation->nsprite = index; + animation->loop = loop; TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Starts a sprite animation - * - * \param index - * Id of the animation to set (0 <= id < num_animations) - * - * \param nsprite - * If of the sprite to animate (0 <= id < num_sprites) - * - * \param sequence - * Reference of the sequence to assign - * - * \param loop - * amount of times to loop, 0=infinite - * - * \see - * Animations - */ -bool TLN_SetSpriteAnimation (int index, int nsprite, TLN_Sequence sequence, int loop) +bool TLN_SetAnimationDelay(int index, int frame, int delay) { Animation* animation; - - if (index >= engine->numanimations) - { - TLN_SetLastError (TLN_ERR_IDX_ANIMATION); - return false; - } - if (nsprite >= engine->numsprites) + TLN_SequenceFrame* frames = NULL; + + if (index >= engine->numanimations || index < 0) { - TLN_SetLastError (TLN_ERR_IDX_SPRITE); + TLN_SetLastError(TLN_ERR_IDX_SPRITE); return false; } - /* validate type */ - if (!CheckBaseObject (sequence, OT_SEQUENCE)) - return false; - - animation = &engine->animations[index]; - SetAnimation (animation, sequence, TYPE_SPRITE); - animation->idx = nsprite; - animation->loop = loop; - - TLN_SetLastError (TLN_ERR_OK); - return true; -} + animation = &engine->sprites[index].animation; + frames = (TLN_SequenceFrame*)animation->sequence->data; -/*! - * \brief - * Sets the playback speed of a given animation - * - * \param index - * Id of the animation to set (0 <= id < num_animations) - * - * \param delay - * New delay between frames to assign - * - * Each sequence has its own delay set in the sqx file. However sometimes it's convenient - * to speed up or down the playback speed in realtime, for example for a character that is - * accelerating. - * - * \remarks - * The meaning of the delay value (i.e. its units) is application dependant. It can usually be frames - * or milliseconds. Make sure that its units match the ones insode the sqx file and the ones - * passed to the TLN_UpdateFrame() - * - * \see - * Animations, TLN_UpdateFrame() - */ -bool TLN_SetAnimationDelay (int index, int delay) -{ - Animation* animation; - - if (index >= engine->numanimations) + if (frame >= animation->sequence->count || frame < 0) { - TLN_SetLastError (TLN_ERR_IDX_ANIMATION); + TLN_SetLastError(TLN_ERR_IDX_ANIMATION); return false; } - - animation = &engine->animations[index]; - animation->delay = delay; - TLN_SetLastError (TLN_ERR_OK); + frames[frame].delay = delay; + + TLN_SetLastError(TLN_ERR_OK); return true; } -/*! - * \brief - * Finds an available (unused) animation - * - * \returns - * Index of the first unused animation (starting from 0) or -1 if none found - */ int TLN_GetAvailableAnimation (void) { int c; @@ -431,17 +270,7 @@ int TLN_GetAvailableAnimation (void) return -1; } -/*! - * \brief - * Disables the animation so it stops playing and returns it to the list of available animations - * - * \param index - * Id of the animation to set (0 <= id < num_animations) - * - * \see - * Animations - */ -bool TLN_DisableAnimation (int index) +bool TLN_DisablePaletteAnimation (int index) { Animation* animation; @@ -452,14 +281,113 @@ bool TLN_DisableAnimation (int index) } animation = &engine->animations[index]; + if (animation->enabled) + ListUnlinkNode(&engine->list_animations, index); + animation->enabled = false; animation->type = TYPE_NONE; animation->sequence = NULL; - + ListUnlinkNode(&engine->list_animations, index); TLN_SetLastError (TLN_ERR_OK); return true; } +static bool enableTilesetAnimation(TLN_Tileset tileset, int index, bool enable) +{ + if (tileset == NULL) + { + TLN_SetLastError(TLN_ERR_REF_TILESET); + return false; + } + + if (tileset->animations == NULL || tileset->sp == NULL) + { + TLN_SetLastError(TLN_ERR_REF_SEQUENCE); + return false; + } + + if (index >= tileset->sp->num_sequences) + { + TLN_SetLastError(TLN_ERR_IDX_ANIMATION); + return false; + } + + tileset->animations[index].paused = enable; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_PauseTilesetAnimation(TLN_Tileset tileset, int index) +{ + return enableTilesetAnimation(tileset, index, true); +} + +bool TLN_ResumeTilesetAnimation(TLN_Tileset tileset, int index) +{ + return enableTilesetAnimation(tileset, index, false); +} + +bool TLN_PauseSpriteAnimation(int index) +{ + Sprite* sprite; + Animation* animation; + + if (index >= engine->numsprites) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + + sprite = &engine->sprites[index]; + animation = &sprite->animation; + animation->paused = true; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_ResumeSpriteAnimation(int index) +{ + Sprite* sprite; + Animation* animation; + + if (index >= engine->numsprites) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + + sprite = &engine->sprites[index]; + animation = &sprite->animation; + animation->paused = false; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_DisableSpriteAnimation(int index) +{ + Sprite* sprite; + Animation* animation; + + if (index >= engine->numsprites) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + + sprite = &engine->sprites[index]; + animation = &sprite->animation; + animation->enabled = false; + animation->type = TYPE_NONE; + animation->sequence = NULL; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_DisableAnimation(int index) +{ + return TLN_DisableSpriteAnimation(index); +} + /* animation commons */ static void SetAnimation (Animation* animation, TLN_Sequence sequence, animation_t type) { @@ -521,22 +449,9 @@ static void ColorCycleBlend (TLN_Palette srcpalette, TLN_Palette dstpalette, str idx1 = (c + steps + 1) % count; } - srcptr0 = GetPaletteData (srcpalette, strip->first + idx0); - srcptr1 = GetPaletteData (srcpalette, strip->first + idx1); - dstptr = GetPaletteData (dstpalette, strip->first + c); + srcptr0 = (uint8_t*)GetPaletteData (srcpalette, strip->first + idx0); + srcptr1 = (uint8_t*)GetPaletteData (srcpalette, strip->first + idx1); + dstptr = (uint8_t*)GetPaletteData (dstpalette, strip->first + c); blendColors (srcptr0, srcptr1, dstptr, f0, f1); } } - -/* tile substitution */ -static void ReplaceTiles (TLN_Tilemap tilemap, int srctile, int dsttile) -{ - int c; - int size = tilemap->rows * tilemap->cols; - - for (c=0; ctiles[c].index == srctile) - tilemap->tiles[c].index = dsttile; - } -} diff --git a/src/Animation.h b/src/Animation.h index 54d54494..9a14db55 100644 --- a/src/Animation.h +++ b/src/Animation.h @@ -13,13 +13,13 @@ #include "Tilengine.h" #include "Sequence.h" +#include "List.h" -#define MAX_COLOR_STRIPS 32 +#define MAX_COLOR_STRIPS 64 typedef enum { TYPE_NONE, - TYPE_TILEMAP, TYPE_SPRITE, TYPE_PALETTE, TYPE_TILESET, @@ -31,18 +31,21 @@ typedef struct { animation_t type; TLN_Sequence sequence; + TLN_Tileset tileset; /* tileset for tileset animations */ bool enabled; + bool paused; /* animation paused */ int loop; int pos; int timer; - int delay; - int idx; /* indice de capa, sprite */ + int nsprite; /* sprite number for sprite animation */ bool blend; TLN_Palette palette; TLN_Palette srcpalette; + ListNode list_node; } Animation; -void UpdateAnimations (int time); +bool SetTilesetAnimation(TLN_Tileset tileset, int index, TLN_Sequence sequence); +void UpdateAnimation(Animation* animation, int time); #endif diff --git a/src/Bitmap.c b/src/Bitmap.c index acc87b52..d337d9e8 100644 --- a/src/Bitmap.c +++ b/src/Bitmap.c @@ -18,25 +18,6 @@ #include "Palette.h" #include "Bitmap.h" -/*! - * \brief - * Creates a memory bitmap - * - * \param width - * Width in pixels - * - * \param height - * Height in pixels - * - * \param bpp - * Bits per pixel - * - * \returns - * Reference to the created bitmap, or NULL if error - * - * \see - * TLN_SetBGBitmap() - */ TLN_Bitmap TLN_CreateBitmap (int width, int height, int bpp) { TLN_Bitmap bitmap; @@ -58,19 +39,6 @@ TLN_Bitmap TLN_CreateBitmap (int width, int height, int bpp) return NULL; } -/*! - * \brief - * Creates a copy of a bitmap - * - * \param src - * Reference to the original bitmap - * - * \returns - * Reference to the created bitmap, or NULL if error - * - * \see - * TLN_SetBGBitmap() - */ TLN_Bitmap TLN_CloneBitmap (TLN_Bitmap src) { TLN_Bitmap bitmap; @@ -88,21 +56,11 @@ TLN_Bitmap TLN_CloneBitmap (TLN_Bitmap src) return NULL; } -/*! - * \brief - * Deletes bitmap and frees resources - * - * \param bitmap - * Reference to bitmap to delete - * - * \see - * TLN_CreateBitmap89, TLN_CloneBitmap() - */ bool TLN_DeleteBitmap (TLN_Bitmap bitmap) { if (CheckBaseObject (bitmap, OT_BITMAP)) { - if (ObjectOwner (bitmap)) + if (ObjectOwner (bitmap) && bitmap->palette) TLN_DeletePalette (bitmap->palette); DeleteBaseObject (bitmap); TLN_SetLastError (TLN_ERR_OK); @@ -112,25 +70,6 @@ bool TLN_DeleteBitmap (TLN_Bitmap bitmap) return false; } -/*! - * \brief - * Gets memory access for direct pixel manipulation - * - * \param bitmap - * Reference to bitmap - * - * \param x - * Starting x position [0, width - 1] - * - * \param y - * Starting y position [0, height - 1] - * - * \returns - * Pointer to pixel data starting at x,y - * - * \remarks - * Care must be taken in manipulating memory directly as it can crash the application - */ uint8_t* TLN_GetBitmapPtr (TLN_Bitmap bitmap, int x, int y) { uint8_t *srcptr; @@ -149,19 +88,6 @@ uint8_t* TLN_GetBitmapPtr (TLN_Bitmap bitmap, int x, int y) return srcptr; } -/*! - * \brief - * Gets the associated palete of a bitmap - * - * \param bitmap - * Reference to bitmap - * - * \returns - * Reference to the bitmap palette - * - * \see - * TLN_SetBitmapPalette() - */ TLN_Palette TLN_GetBitmapPalette (TLN_Bitmap bitmap) { if (CheckBaseObject (bitmap, OT_BITMAP)) @@ -173,19 +99,6 @@ TLN_Palette TLN_GetBitmapPalette (TLN_Bitmap bitmap) return NULL; } -/*! - * \brief - * Assigns a new palette to the bitmap - * - * \param bitmap - * Reference to the bitmap - * - * \param palette - * Reference to the palette to assign - * - * \see - * TLN_GetBitmapPalette() - */ bool TLN_SetBitmapPalette (TLN_Bitmap bitmap, TLN_Palette palette) { if (!CheckBaseObject (bitmap, OT_BITMAP) || !CheckBaseObject (palette, OT_PALETTE)) @@ -196,13 +109,6 @@ bool TLN_SetBitmapPalette (TLN_Bitmap bitmap, TLN_Palette palette) return true; } -/*! - * \brief - * Returns the width in pixels - * - * \param bitmap - * Reference to the bitmap - */ int TLN_GetBitmapWidth (TLN_Bitmap bitmap) { if (CheckBaseObject (bitmap, OT_BITMAP)) @@ -214,13 +120,6 @@ int TLN_GetBitmapWidth (TLN_Bitmap bitmap) return 0; } -/*! - * \brief - * Returns the height in pixels - * - * \param bitmap - * Reference to the bitmap - */ int TLN_GetBitmapHeight (TLN_Bitmap bitmap) { if (CheckBaseObject (bitmap, OT_BITMAP)) @@ -232,13 +131,6 @@ int TLN_GetBitmapHeight (TLN_Bitmap bitmap) return 0; } -/*! - * \brief - * Returns the bits per pixel - * - * \param bitmap - * Reference to the bitmap - */ int TLN_GetBitmapDepth (TLN_Bitmap bitmap) { if (CheckBaseObject (bitmap, OT_BITMAP)) @@ -250,13 +142,6 @@ int TLN_GetBitmapDepth (TLN_Bitmap bitmap) return 0; } -/*! - * \brief - * Returns the number of bytes per scaline (also known a stride) - * - * \param bitmap - * Reference to the bitmap - */ int TLN_GetBitmapPitch (TLN_Bitmap bitmap) { if (CheckBaseObject (bitmap, OT_BITMAP)) diff --git a/src/Blitters.c b/src/Blitters.c index c6da8c99..3533fbee 100644 --- a/src/Blitters.c +++ b/src/Blitters.c @@ -18,73 +18,10 @@ #define BLIT_BLEND 0 #define BLIT_SCALING 1 #define BLIT_KEY 2 -#define BLIT_BPP 3 - -/* 8 to 8 BPP blitters ----------------------------------------------------- */ - -static void blitFast_8_8 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) -{ - uint8_t* dstpixel = (uint8_t*)dstptr; - while (width) - { - *dstpixel++ = *srcpixel; - srcpixel += dx; - width--; - } -} - -static void blitFastScaling_8_8 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) -{ - uint8_t* dstpixel = (uint8_t*)dstptr; - while (width) - { - uint32_t src = *(srcpixel + offset/(1 << FIXED_BITS)); - *dstpixel++ = src; - offset += dx; - width--; - } -} - -static void blitKey_8_8 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) -{ - uint8_t* dstpixel = (uint8_t*)dstptr; - while (width) - { - if (*srcpixel) - *dstpixel = *srcpixel; - srcpixel += dx; - dstpixel++; - width--; - } -} - -static void blitKeyScaling_8_8 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) -{ - uint8_t* dstpixel = (uint8_t*)dstptr; - while (width) - { - uint32_t src = *(srcpixel + offset/(1 << FIXED_BITS)); - if (src) - *dstpixel = src; - - offset += dx; - dstpixel++; - width--; - } -} /* 8 to 32 BPP blitters ----------------------------------------------------- */ -static void blitColor_8_32 (void* dstptr, uint32_t color, int width) -{ - uint32_t* dstpixel = (uint32_t*)dstptr; - while (width) - { - *dstpixel++ = color; - width--; - } -} - +/* paints scanline without checking color key (always solid) */ static void blitFast_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) { uint32_t* dstpixel = (uint32_t*)dstptr; @@ -97,6 +34,7 @@ static void blitFast_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, } } +/* paints scanline without checking color key (always solid) with blending */ static void blitFastBlend_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) { uint8_t *src, *dst; @@ -114,6 +52,7 @@ static void blitFastBlend_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* ds } } +/* paints scanline without checking color key (always solid) with scaling */ static void blitFastScaling_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) { uint32_t* dstpixel = (uint32_t*)dstptr; @@ -127,6 +66,7 @@ static void blitFastScaling_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* } } +/* paints scanline without checking color key (always solid) with scaling and blending */ static void blitFastBlendScaling_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) { uint8_t *src, *dst; @@ -145,6 +85,7 @@ static void blitFastBlendScaling_8_32 (uint8_t *srcpixel, TLN_Palette palette, v } } +/* paints scanline skipping empty pixels */ static void blitKey_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) { uint32_t* dstpixel = (uint32_t*)dstptr; @@ -159,6 +100,7 @@ static void blitKey_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, } } +/* paints scanline skipping empty pixels with blending */ static void blitKeyBlend_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) { uint8_t *src, *dst; @@ -179,6 +121,7 @@ static void blitKeyBlend_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dst } } +/* paints scanline skipping empty pixels with scaling */ static void blitKeyScaling_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) { uint32_t* dstpixel = (uint32_t*)dstptr; @@ -195,6 +138,7 @@ static void blitKeyScaling_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* d } } +/* paints scanline skipping empty pixels with scaling and blending */ static void blitKeyBlendScaling_8_32 (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend) { uint8_t *src, *dst; @@ -216,17 +160,9 @@ static void blitKeyBlendScaling_8_32 (uint8_t *srcpixel, TLN_Palette palette, vo } } +/* blitter table selector */ static const ScanBlitPtr blitters[]= { - blitFast_8_8, - NULL, - blitFastScaling_8_8, - NULL, - blitKey_8_8, - NULL, - blitKeyScaling_8_8, - NULL, - blitFast_8_32, blitFastBlend_8_32, blitFastScaling_8_32, @@ -237,71 +173,135 @@ static const ScanBlitPtr blitters[]= blitKeyBlendScaling_8_32 }; -ScanBlitPtr GetBlitter (int bpp, bool key, bool scaling, bool blend) +/* returns suitable blitter for specified conditions */ +ScanBlitPtr SelectBlitter (bool key, bool scaling, bool blend) { - int index; - - if (bpp == 32) - bpp = 1; - else - bpp = 0; - - index = (bpp << BLIT_BPP) + (key << BLIT_KEY) + (scaling << BLIT_SCALING) + (blend << BLIT_BLEND); + int index = (key << BLIT_KEY) + (scaling << BLIT_SCALING) + (blend << BLIT_BLEND); return blitters[index]; } -void BlitColor (void* dstptr, uint32_t color, int width) +/* paints constant color */ +void BlitColor(void* dstptr, uint32_t color, int width, uint8_t* blend) { - blitColor_8_32 (dstptr, color, width); + /* blend */ + if (blend != NULL) + { + uint8_t* src = (uint8_t*)&color; + uint8_t* dst = (uint8_t*)dstptr; + while (width) + { + dst[0] = blendfunc(blend, src[0], dst[0]); + dst[1] = blendfunc(blend, src[1], dst[1]); + dst[2] = blendfunc(blend, src[2], dst[2]); + dst += sizeof(uint32_t); + width--; + } + } + + /* regular*/ + else + { + uint32_t* dstpixel = (uint32_t*)dstptr; + while (width) + { + *dstpixel++ = color; + width--; + } + } } -void BlitMosaicSolid (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int size) +/* perfoms direct 32 -> 32 bpp blit with opcional blend */ +void Blit32_32(uint32_t *src, uint32_t* dst, int width, uint8_t* blend) { - uint32_t* dstpixel = (uint32_t*)dstptr; - uint32_t* color = (uint32_t*)palette->data; - while (width) + Color* srcpixel = (Color*)src; + Color* dstpixel = (Color*)dst; + + /* blending */ + if (blend != NULL) { - if (size > width) - size = width; + while (width > 0) + { + if (srcpixel->a != 0) + { + dstpixel->r = blendfunc(blend, srcpixel->r, dstpixel->r); + dstpixel->g = blendfunc(blend, srcpixel->g, dstpixel->g); + dstpixel->b = blendfunc(blend, srcpixel->b, dstpixel->b); + } + srcpixel += 1; + dstpixel += 1; + width -= 1; + } + } - if (*srcpixel) + /* regular */ + else + { + while (width > 0) { - const uint32_t value = color[*srcpixel]; - int c; - for (c=0; ca != 0) + dstpixel->value = srcpixel->value; + srcpixel += 1; + dstpixel += 1; + width -= 1; } - else - dstpixel += size; - srcpixel += size; - width -= size; } } -void BlitMosaicBlend (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int size, uint8_t* blend) +/* performs mosaic effect with optional blend */ +void BlitMosaic(uint32_t *src, uint32_t* dst, int width, int size, uint8_t* blend) { - uint8_t* dstpixel = (uint8_t*)dstptr; - uint32_t* color = (uint32_t*)palette->data; - while (width) + Color* srcpixel = (Color*)src; + Color* dstpixel = (Color*)dst; + + /* blending */ + if (blend != NULL) { - if (size > width) - size = width; + while (width > 0) + { + if (size > width) + size = width; - if (*srcpixel) + if (srcpixel->a != 0) + { + int block = size; + while (block != 0) + { + dstpixel->r = blendfunc(blend, srcpixel->r, dstpixel->r); + dstpixel->g = blendfunc(blend, srcpixel->g, dstpixel->g); + dstpixel->b = blendfunc(blend, srcpixel->b, dstpixel->b); + dstpixel += 1; + block -= 1; + } + } + else + dstpixel += size; + srcpixel += size; + width -= size; + } + } + + /* regular */ + else + { + while (width > 0) { - const uint8_t* value = (uint8_t*)&color[*srcpixel]; - int c; - for (c=0; c width) + size = width; + + if (srcpixel->a != 0) { - dstpixel[0] = blendfunc(blend, value[0], dstpixel[0]); - dstpixel[1] = blendfunc(blend, value[1], dstpixel[1]); - dstpixel[2] = blendfunc(blend, value[2], dstpixel[2]); - dstpixel += sizeof(uint32_t); + int block = size; + while (block != 0) + { + dstpixel->value = srcpixel->value; + dstpixel += 1; + block -= 1; + } } + else + dstpixel += size; + srcpixel += size; + width -= size; } - else - dstpixel += (sizeof(uint32_t)*size); - srcpixel += size; - width -= size; } } diff --git a/src/Blitters.h b/src/Blitters.h index 0b931aec..387efa83 100644 --- a/src/Blitters.h +++ b/src/Blitters.h @@ -13,13 +13,28 @@ #include "Tilengine.h" -typedef void (*ScanBlitPtr) \ +/* blitter callback signature */ +typedef void(*ScanBlitPtr) \ (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int dx, int offset, uint8_t* blend); -ScanBlitPtr GetBlitter (int bpp, bool key, bool scaling, bool blend); +#ifdef __cplusplus +extern "C" { +#endif -void BlitColor (void* dstptr, uint32_t color, int width); -void BlitMosaicSolid (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int size); -void BlitMosaicBlend (uint8_t *srcpixel, TLN_Palette palette, void* dstptr, int width, int size, uint8_t* blend); + /* returns suitable blitter for specified conditions */ + ScanBlitPtr SelectBlitter(bool key, bool scaling, bool blend); + + /* solid color with opcional blend */ + void BlitColor(void* dstptr, uint32_t color, int width, uint8_t* blend); + + /* perfoms direct 32 -> 32 bpp blit with opcional blend */ + void Blit32_32(uint32_t *src, uint32_t* dst, int width, uint8_t* blend); + + /* performs mosaic blit */ + void BlitMosaic(uint32_t *src, uint32_t* dst, int width, int size, uint8_t* blend); + +#ifdef __cplusplus +} +#endif #endif \ No newline at end of file diff --git a/src/DIB.h b/src/DIB.h index cddb6cc0..ae67f425 100644 --- a/src/DIB.h +++ b/src/DIB.h @@ -20,9 +20,13 @@ typedef struct } RGBTRIPLE; -typedef struct +typedef union { - uint8_t b,g,r,a; + uint32_t value; + struct + { + uint8_t b, g, r, a; + }; } RGBQUAD; diff --git a/src/Debug.h b/src/Debug.h new file mode 100644 index 00000000..938feeb2 --- /dev/null +++ b/src/Debug.h @@ -0,0 +1,12 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +/* conditional debugmsg() trace */ +#ifdef _DEBUG +#include +#define debugmsg printf +#else +static void debugmsg(const char* format, ...) {} +#endif + +#endif diff --git a/src/Draw.c b/src/Draw.c index 06c6a6b5..8890561b 100644 --- a/src/Draw.c +++ b/src/Draw.c @@ -16,29 +16,142 @@ #include "Tileset.h" #include "Tilemap.h" #include "ObjectList.h" +#include "Sprite.h" /* private prototypes */ -static void DrawSpriteCollision (int nsprite, uint8_t *srcpixel, uint16_t *dstpixel, int width, int dx); -static void DrawSpriteCollisionScaling (int nsprite, uint8_t *srcpixel, uint16_t *dstpixel, int width, int dx, int srcx); - -/*! - * \brief Draws the next scanline of the frame started with TLN_BeginFrame() or TLN_BeginWindowFrame() - * \remarks Use this function in conjunction with TLN_BeginFrame() (custom render target) or - * TLN_BeginWindoFrame() and TLN_EndWindowFrame() (built-in window) for active rendering without callbacks. - * \returns true if there are still scanlines to draw or false when the frame is complete - */ -bool TLN_DrawNextScanline(void) +static void DrawSpriteCollision(int nsprite, uint8_t *srcpixel, uint16_t *dstpixel, int width, int dx); +static void DrawSpriteCollisionScaling(int nsprite, uint8_t *srcpixel, uint16_t *dstpixel, int width, int dx, int srcx); + +static bool check_sprite_coverage(Sprite* sprite, int nscan) +{ + /* check sprite coverage */ + if (nscan < sprite->dstrect.y1 || nscan >= sprite->dstrect.y2) + return false; + if (sprite->dstrect.x2 < 0 || sprite->srcrect.x2 < 0) + return false; + if ((sprite->flags & FLAG_MASKED) && nscan >= engine->sprite_mask_top && nscan <= engine->sprite_mask_bottom) + return false; + return true; +} + +/* draw background scanline taking into account mosaic and windowing effects */ +static bool draw_background_scanline(int nlayer, int line) +{ + /* draw */ + Layer* layer = &engine->layers[nlayer]; + LayerWindow* window = &layer->window; + uint32_t* mosaic = layer->mosaic.buffer; + uint32_t* scan = NULL; + const bool inside = line >= window->y1 && line <= window->y2; + const int framewidth = engine->framebuffer.width; + const int windowwidth = layer->window.x2 - layer->window.x1; + bool priority = false; + bool build_mosaic = false; + + /* determine target buffer */ + if (layer->mosaic.h != 0) + { + if (line % layer->mosaic.h == 0) + { + build_mosaic = true; + scan = engine->linebuffer; + } + else + scan = NULL; + } + else if (layer->mode >= MODE_TRANSFORM) + scan = engine->linebuffer; + else + scan = GetFramebufferLine(line); + + if (scan == engine->linebuffer) + memset(scan, 0, engine->framebuffer.pitch); + + /* regular region */ + if (scan != NULL) + { + if (!window->invert) + { + if (inside) + priority |= layer->draw(nlayer, scan, line, window->x1, window->x2); + } + else + { + if (inside) + { + priority |= layer->draw(nlayer, scan, line, 0, layer->window.x1); + priority |= layer->draw(nlayer, scan, line, layer->window.x2, framewidth); + } + else + priority |= layer->draw(nlayer, scan, line, 0, framewidth); + } + } + scan = GetFramebufferLine(line); + + /* build mosaic to linebuffer */ + if (build_mosaic) + { + memset(mosaic, 0, engine->framebuffer.pitch); + BlitMosaic(engine->linebuffer, mosaic, framewidth, layer->mosaic.w, NULL); + } + + /* blit mosaic */ + if (layer->mosaic.h != 0) + { + if (!window->invert) + { + if (inside) + Blit32_32(mosaic + window->x1, scan + window->x1, windowwidth, layer->blend); + } + else + { + if (inside) + { + Blit32_32(mosaic, scan, windowwidth, layer->blend); + Blit32_32(mosaic + window->x2, scan + window->x2, framewidth - window->x2, layer->blend); + } + else + Blit32_32(mosaic, scan, framewidth, layer->blend); + } + } + else if (layer->mode >= MODE_TRANSFORM) + Blit32_32(engine->linebuffer, scan, engine->framebuffer.width, layer->blend); + + /* clipped region */ + if (window->color != 0) + { + if (!window->invert) + { + if (inside) + { + BlitColor(scan, window->color, window->x1, window->blend); + BlitColor(scan + window->x2, window->color, framewidth - window->x2, window->blend); + } + else + BlitColor(scan, window->color, framewidth, window->blend); + } + else if (inside) + BlitColor(scan + window->x1, window->color, windowwidth, window->blend); + } + + return priority; +} + +/* Draws the next scanline of the frame started with TLN_BeginFrame() or TLN_BeginWindowFrame() */ +bool DrawScanline(void) { int line = engine->line; - uint8_t* scan = GetFramebufferLine(line); + uint32_t* scan = GetFramebufferLine(line); int size = engine->framebuffer.width; int c; - bool background_priority = false; - bool sprite_priority = false; + int index; + bool background_priority = false; /* at least one tile in priority layer */ + bool sprite_priority = false; /* at least one sprite in priority layer */ + List* list; /* call raster effect callback */ - if (engine->raster) - engine->raster(line); + if (engine->cb_raster) + engine->cb_raster(line); /* background is bitmap */ if (engine->bgbitmap && engine->bgpalette) @@ -51,57 +164,78 @@ bool TLN_DrawNextScanline(void) /* background is solid color */ else if (engine->bgcolor) - BlitColor(scan, engine->bgcolor, size); + BlitColor(scan, engine->bgcolor, size, NULL); - background_priority = false; - memset(engine->priority, 0, engine->framebuffer.pitch); - memset(engine->collision, -1, engine->framebuffer.width * sizeof(uint16_t)); - - /* draw background layers */ - for (c = engine->numlayers - 1; c >= 0; c--) + /* draw regular background layers */ + if (engine->numlayers > 0) { - Layer* layer = &engine->layers[c]; - - /* link layer */ - if (layer->parent != NULL) + background_priority = false; + memset(engine->priority, 0, engine->framebuffer.pitch); + for (c = engine->numlayers - 1; c >= 0; c--) { - layer->hstart = layer->parent->hstart; - layer->vstart = layer->parent->vstart; - } + Layer* layer = &engine->layers[c]; + + /* update if dirty */ + if (engine->dirty || layer->dirty) + { + const int lx = (int)(engine->xworld * layer->world.xfactor) - layer->world.offsetx; + const int ly = (int)(engine->yworld * layer->world.yfactor) - layer->world.offsety; + TLN_SetLayerPosition(c, lx, ly); + layer->dirty = false; + } - if (layer->ok && !layer->priority && line >= layer->clip.y1 && line <= layer->clip.y2) - { - if (layer->draw(c, line) == true) - background_priority = true; + /* draw */ + if (layer->ok && !layer->priority) + background_priority |= draw_background_scanline(c, line); } } /* draw regular sprites */ - for (c = 0; c < engine->numsprites; c++) + if (engine->numsprites > 0) { - Sprite* sprite = &engine->sprites[c]; - if (sprite->ok) + memset(engine->collision, -1, engine->framebuffer.width * sizeof(uint16_t)); + list = &engine->list_sprites; + index = list->first; + while (index != -1) { - if (!(sprite->flags & FLAG_PRIORITY)) - engine->sprites[c].draw(c, line); - else - sprite_priority = true; + Sprite* sprite = &engine->sprites[index]; + + /* update if dirty */ + if (sprite->world_space && (sprite->dirty || engine->dirty)) + { + sprite->x = sprite->xworld - engine->xworld; + sprite->y = sprite->yworld - engine->yworld; + UpdateSprite(sprite); + sprite->dirty = false; + } + + if (check_sprite_coverage(sprite, line)) + { + if (!(sprite->flags & FLAG_PRIORITY)) + sprite->draw(index, scan, line, 0, 0); + else + sprite_priority = true; + } + index = sprite->list_node.next; } } /* draw background layers with priority */ - for (c = engine->numlayers - 1; c >= 0; c--) + if (engine->numlayers > 0) { - const Layer* layer = &engine->layers[c]; - if (layer->ok && layer->priority && line >= layer->clip.y1 && line <= layer->clip.y2) - layer->draw(c, line); + for (c = engine->numlayers - 1; c >= 0; c--) + { + Layer* layer = &engine->layers[c]; + if (layer->ok && layer->priority) + draw_background_scanline(c, line); + } } /* overlay background tiles with priority */ if (background_priority == true) { - uint32_t* src = (uint32_t*)engine->priority; - uint32_t* dst = (uint32_t*)scan; + uint32_t* src = engine->priority; + uint32_t* dst = scan; for (c = 0; c < engine->framebuffer.width; c++) { if (*src) @@ -114,73 +248,100 @@ bool TLN_DrawNextScanline(void) /* draw sprites with priority */ if (sprite_priority == true) { - for (c = 0; c < engine->numsprites; c++) + index = list->first; + while (index != -1) { - Sprite* sprite = &engine->sprites[c]; - if (sprite->ok && (sprite->flags & FLAG_PRIORITY)) - engine->sprites[c].draw(c, line); + Sprite* sprite = &engine->sprites[index]; + if (check_sprite_coverage(sprite, line) && (sprite->flags & FLAG_PRIORITY)) + sprite->draw(index, scan, line, 0, 0); + index = sprite->list_node.next; } } /* next scanline */ + engine->dirty = false; engine->line++; return engine->line < engine->framebuffer.height; } -/* draw scanline of tiled background */ -static bool DrawLayerScanline (int nlayer, int nscan) +typedef struct { - const Layer *layer = &engine->layers[nlayer]; - const TLN_Tileset tileset = layer->tileset; - const TLN_Tilemap tilemap = layer->tilemap; - int shift; - TLN_Tile tile; - uint8_t *srcpixel; - int x,x1; - int xpos, ypos; - int xtile, ytile; - int srcx, srcy; - int direction, width; - int column; - int line; - uint8_t *dstpixel; - uint8_t *dstpixel_pri; - uint8_t *dst; - bool color_key; - bool priority = false; + int width, height; + int srcx; + int srcy; + int dx; + int stride; +} +Tilescan; - /* mosaic effect */ - if (layer->mosaic.h != 0) +/* process flip flags */ +static inline void process_flip(uint16_t flags, Tilescan* scan) +{ + /* H/V flip */ + if (flags & FLAG_FLIPX) { - shift = 0; - dstpixel = layer->mosaic.buffer; - if (nscan % layer->mosaic.h == 0) - memset (dstpixel, 0, engine->framebuffer.width); - else - goto draw_end; + scan->dx = -scan->dx; + scan->srcx = scan->width - 1; + } + if (flags & FLAG_FLIPY) + scan->srcy = scan->height - scan->srcy - 1; +} + +/* process flip & rotation flags */ +static inline void process_flip_rotation(uint16_t flags, Tilescan* scan) +{ + if (flags & FLAG_ROTATE) + { + int tmp = scan->srcx; + scan->srcx = scan->srcy; + scan->srcy = tmp; + scan->dx *= scan->stride; + + /* H/V flip */ + if (flags & FLAG_FLIPX) + { + scan->dx = -scan->dx; + scan->srcy = scan->height - scan->srcy - 1; + } + if (flags & FLAG_FLIPY) + scan->srcx = scan->width - scan->srcx - 1; } else { - shift = 2; - dstpixel = GetFramebufferLine (nscan); + /* H/V flip */ + if (flags & FLAG_FLIPX) + { + scan->dx = -scan->dx; + scan->srcx = scan->width - scan->srcx - 1; + } + if (flags & FLAG_FLIPY) + scan->srcy = scan->height - scan->srcy - 1; } +} + +/* draw scanline of tiled background */ +static bool DrawTiledScanline(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) +{ + const Layer *layer = (const Layer*)&engine->layers[nlayer]; + bool priority = false; + Tilescan scan = { 0 }; /* target lines */ - x = layer->clip.x1; - dstpixel += (x << shift); - dstpixel_pri = engine->priority; + int x = tx1; + const TLN_Tilemap tilemap = layer->tilemap; + const TLN_Tileset tileset = tilemap->tilesets[0]; + int xpos = (layer->hstart + x) % layer->width; + int xtile = xpos >> tileset->hshift; - xpos = (layer->hstart + x) % layer->width; - xtile = xpos >> tileset->hshift; - srcx = xpos & tileset->hmask; + scan.width = scan.height = scan.stride = tileset->width; + scan.srcx = xpos & tileset->hmask; /* fill whole scanline */ - column = x % tileset->width; - while (x < layer->clip.x2) + int column = x % tileset->width; + while (x < tx2) { - int tilewidth; - /* column offset: update ypos */ + int ypos; if (layer->column) { ypos = (layer->vstart + nscan + layer->column[column]) % layer->height; @@ -188,137 +349,85 @@ static bool DrawLayerScanline (int nlayer, int nscan) ypos = layer->height + ypos; } else - ypos = (layer->vstart + nscan) % layer->height; + ypos = (layer->vstart + nscan) % layer->height; - ytile = ypos >> tileset->vshift; - srcy = ypos & tileset->vmask; + int ytile = ypos >> tileset->vshift; + scan.srcy = ypos & tileset->vmask; - tile = &tilemap->tiles[ytile*tilemap->cols + xtile]; + TLN_Tile tile = &tilemap->tiles[ytile*tilemap->cols + xtile]; /* get effective tile width */ - tilewidth = tileset->width - srcx; - x1 = x + tilewidth; - if (x1 > layer->clip.x2) - x1 = layer->clip.x2; - width = x1 - x; + int tilewidth = tileset->width - scan.srcx; + int x1 = x + tilewidth; + if (x1 > tx2) + x1 = tx2; + int width = x1 - x; /* paint if not empty tile */ - if (tile->index) + if (tile->index != 0) { - /* H/V flip */ - if (tile->flags & FLAG_FLIPX) - { - direction = -1; - srcx = tilewidth - 1; - } - else - direction = 1; - if (tile->flags & FLAG_FLIPY) - srcy = tileset->height - srcy - 1; + const TLN_Tileset tileset = tilemap->tilesets[tile->tileset]; + const uint16_t tile_index = tileset->tiles[tile->index] - 1; + + /* selects suitable palette */ + TLN_Palette palette = tileset->palette; + if (layer->palette != NULL) + palette = layer->palette; + else if (engine->palettes[tile->palette] != NULL) + palette = engine->palettes[tile->palette]; + + /* process rotate & flip flags */ + scan.dx = 1; + if ((tile->flags & (FLAG_FLIPX + FLAG_FLIPY + FLAG_ROTATE)) != 0) + process_flip_rotation(tile->flags, &scan); /* paint tile scanline */ - srcpixel = &GetTilesetPixel (tileset, tile->index, srcx, srcy); + uint8_t *srcpixel = &GetTilesetPixel(tileset, tile_index, scan.srcx, scan.srcy); + uint32_t *dst = dstpixel; if (tile->flags & FLAG_PRIORITY) { - dst = dstpixel_pri; + dst = engine->priority; priority = true; } - else - { - dst = dstpixel; - } - line = GetTilesetLine (tileset, tile->index, srcy); - color_key = *(tileset->color_key + line); - layer->blitters[color_key] (srcpixel, layer->palette, dst, width, direction, 0, layer->blend); + + int line = GetTilesetLine(tileset, tile_index, scan.srcy); + bool color_key = *(tileset->color_key + line); + layer->blitters[1](srcpixel, palette, dst + x, width, scan.dx, 0, layer->blend); } /* next tile */ x += width; - width <<= shift; - dstpixel += width; - dstpixel_pri += width; xtile = (xtile + 1) % tilemap->cols; - srcx = 0; - column++; - } - -draw_end: - if (layer->mosaic.h != 0) - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = layer->mosaic.buffer + offset; - uint8_t* dstptr = GetFramebufferLine (nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - if (layer->blend != NULL) - BlitMosaicBlend (srcptr, layer->palette, dstptr, width, layer->mosaic.w, layer->blend); - else - BlitMosaicSolid (srcptr, layer->palette, dstptr, width, layer->mosaic.w); + scan.srcx = 0; + column += 1; } - return priority; } /* draw scanline of tiled background with scaling */ -static bool DrawLayerScanlineScaling (int nlayer, int nscan) +static bool DrawTiledScanlineScaling(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) { - const Layer *layer = &engine->layers[nlayer]; - const TLN_Tileset tileset = layer->tileset; - const TLN_Tilemap tilemap = layer->tilemap; - int shift; - TLN_Tile tile; - uint8_t *srcpixel; - int x,x1; - int xpos, ypos; - int xtile, ytile; - int srcx, srcy; - int direction, width; - int column; - int line; - uint8_t *dstpixel; - uint8_t *dstpixel_pri; - uint8_t *dst; - fix_t fix_tilewidth; - fix_t fix_x; - fix_t dx; - bool color_key; + const Layer *layer = (const Layer*)&engine->layers[nlayer]; bool priority = false; - - /* mosaic effect */ - if (layer->mosaic.h != 0) - { - shift = 0; - dstpixel = layer->mosaic.buffer; - if (nscan % layer->mosaic.h == 0) - memset (dstpixel, 0, engine->framebuffer.width); - else - goto draw_end; - } - else - { - shift = 2; - dstpixel = GetFramebufferLine (nscan); - } + Tilescan scan = { 0 }; /* target lines */ - x = layer->clip.x1; - dstpixel += (x << shift); - dstpixel_pri = engine->priority; + int x = tx1; + const TLN_Tilemap tilemap = layer->tilemap; + const TLN_Tileset tileset = tilemap->tilesets[0]; + int xpos = (layer->hstart + fix2int(x*layer->dx)) % layer->width; + int xtile = xpos >> tileset->hshift; - xpos = (layer->hstart + fix2int(x*layer->dx)) % layer->width; - xtile = xpos >> tileset->hshift; - srcx = xpos & tileset->hmask; + scan.width = scan.height = scan.stride = tileset->width; + scan.srcx = xpos & tileset->hmask; /* fill whole scanline */ - fix_x = int2fix (x); - column = x % tileset->width; - while (x < layer->clip.x2) + fix_t fix_x = int2fix(x); + int column = x % tileset->width; + while (x < tx2) { - int tilewidth; - int tilescalewidth; - /* column offset: update ypos */ - ypos = nscan; + int ypos = nscan; if (layer->column) ypos += layer->column[column]; @@ -328,368 +437,238 @@ static bool DrawLayerScanlineScaling (int nlayer, int nscan) else ypos = ypos % layer->height; - ytile = ypos >> tileset->vshift; - srcy = ypos & tileset->vmask; + int ytile = ypos >> tileset->vshift; + scan.srcy = ypos & tileset->vmask; - tile = &tilemap->tiles[ytile*tilemap->cols + xtile]; + TLN_Tile tile = &tilemap->tiles[ytile*tilemap->cols + xtile]; /* get effective tile width */ - tilewidth = tileset->width - srcx; - dx = int2fix(tilewidth); - fix_tilewidth = tilewidth * layer->xfactor; + int tilewidth = tileset->width - scan.srcx; + fix_t dx = int2fix(tilewidth); + fix_t fix_tilewidth = tilewidth * layer->xfactor; fix_x += fix_tilewidth; - x1 = fix2int (fix_x); - tilescalewidth = x1 - x; + int x1 = fix2int(fix_x); + int tilescalewidth = x1 - x; if (tilescalewidth) dx /= tilescalewidth; else dx = 0; /* right clip */ - if (x1 > layer->clip.x2) - x1 = layer->clip.x2; - width = x1 - x; - + if (x1 > tx2) + x1 = tx2; + int width = x1 - x; + /* paint if tile is not empty */ - if (tile->index) + if (tile->index != 0) { - /* volteado H/V */ - if (tile->flags & FLAG_FLIPX) - { - direction = -dx; - srcx = tilewidth - 1; - } - else - direction = dx; - if (tile->flags & FLAG_FLIPY) - srcy = tileset->height - srcy - 1; + const TLN_Tileset tileset = tilemap->tilesets[tile->tileset]; + const uint16_t tile_index = tileset->tiles[tile->index] - 1; + + /* selects suitable palette */ + TLN_Palette palette = tileset->palette; + if (layer->palette != NULL) + palette = layer->palette; + else if (engine->palettes[tile->palette] != NULL) + palette = engine->palettes[tile->palette]; + + /* process flip flags */ + scan.dx = dx; + if ((tile->flags & (FLAG_FLIPX + FLAG_FLIPY)) != 0) + process_flip(tile->flags, &scan); + //process_flip_rotation(tile->flags, &scan); - /* pinta tile scanline */ - srcpixel = &GetTilesetPixel (tileset, tile->index, srcx, srcy); + /* paint tile scanline */ + uint8_t* srcpixel = &GetTilesetPixel(tileset, tile_index, scan.srcx, scan.srcy); + uint32_t *dst = dstpixel; if (tile->flags & FLAG_PRIORITY) { - dst = dstpixel_pri; + dst = engine->priority; priority = true; } - else - { - dst = dstpixel; - } - line = GetTilesetLine (tileset, tile->index, srcy); - color_key = *(tileset->color_key + line); - layer->blitters[color_key] (srcpixel, layer->palette, dst, width, direction, 0, layer->blend); + + int line = GetTilesetLine(tileset, tile_index, scan.srcy); + bool color_key = *(tileset->color_key + line); + layer->blitters[color_key](srcpixel, palette, dst + x, width, scan.dx, 0, layer->blend); } /* next tile */ - width <<= shift; - dstpixel += width; - dstpixel_pri += width; x = x1; xtile = (xtile + 1) % tilemap->cols; - srcx = 0; - column++; - } - -draw_end: - if (layer->mosaic.h != 0) - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = layer->mosaic.buffer + offset; - uint8_t* dstptr = GetFramebufferLine (nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - if (layer->blend != NULL) - BlitMosaicBlend (srcptr, layer->palette, dstptr, width, layer->mosaic.w, layer->blend); - else - BlitMosaicSolid (srcptr, layer->palette, dstptr, width, layer->mosaic.w); + scan.srcx = 0; + column += 1; } - return priority; } /* draw scanline of tiled background with affine transform */ -static bool DrawLayerScanlineAffine (int nlayer, int nscan) +static bool DrawTiledScanlineAffine(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) { - Layer *layer = &engine->layers[nlayer]; - const TLN_Tileset tileset = layer->tileset; - const TLN_Tilemap tilemap = layer->tilemap; - const TLN_Palette palette = layer->palette; - int shift; - TLN_Tile tile; - int x, width; - int x1,y1, x2,y2; - fix_t dx, dy; - int xpos, ypos; - int xtile, ytile; - int srcx, srcy; - uint8_t *dstpixel; - Point2D p1,p2; - - /* mosaic effect */ - if (layer->mosaic.h != 0) - { - shift = 0; - dstpixel = layer->mosaic.buffer; - if (nscan % layer->mosaic.h == 0) - memset (dstpixel, 0, engine->framebuffer.width); - else - goto draw_end; - } - else - { - shift = 2; - dstpixel = engine->tmpindex; - memset (dstpixel, 0, engine->framebuffer.width); - } + const Layer *layer = (const Layer*)&engine->layers[nlayer]; + bool priority = false; + Tilescan scan = { 0 }; - /* target lines */ - x = layer->clip.x1; - width = layer->clip.x2; + const TLN_Tilemap tilemap = layer->tilemap; + const TLN_Tileset tileset = tilemap->tilesets[0]; + int xpos = layer->hstart; + int ypos = layer->vstart + nscan; - xpos = layer->hstart; - ypos = layer->vstart + nscan; + Point2D p1, p2; + Point2DSet(&p1, (math2d_t)xpos + tx1, (math2d_t)ypos); + Point2DSet(&p2, (math2d_t)xpos + tx2, (math2d_t)ypos); + Point2DMultiply(&p1, (Matrix3*)&layer->transform); + Point2DMultiply(&p2, (Matrix3*)&layer->transform); - Point2DSet (&p1, (math2d_t)xpos,(math2d_t)ypos); - Point2DSet (&p2, (math2d_t)xpos + width, (math2d_t)ypos); - Point2DMultiply (&p1, &layer->transform); - Point2DMultiply (&p2, &layer->transform); + int x1 = float2fix(p1.x); + int y1 = float2fix(p1.y); + int x2 = float2fix(p2.x); + int y2 = float2fix(p2.y); - x1 = float2fix(p1.x); - y1 = float2fix(p1.y); - x2 = float2fix(p2.x); - y2 = float2fix(p2.y); + const int twidth = tx2 - tx1; + const int dx = (x2 - x1) / twidth; + const int dy = (y2 - y1) / twidth; - dx = (x2 - x1) / width; - dy = (y2 - y1) / width; + scan.width = scan.height = scan.stride = tileset->width; + dstpixel += tx1; - while (x < width) + while (tx1 < tx2) { xpos = abs((fix2int(x1) + layer->width)) % layer->width; ypos = abs((fix2int(y1) + layer->height)) % layer->height; - xtile = xpos >> tileset->hshift; - ytile = ypos >> tileset->vshift; + int xtile = xpos >> tileset->hshift; + int ytile = ypos >> tileset->vshift; - srcx = xpos & tileset->hmask; - srcy = ypos & tileset->vmask; - - tile = &tilemap->tiles[ytile*tilemap->cols + xtile]; + scan.srcx = xpos & tileset->hmask; + scan.srcy = ypos & tileset->vmask; + TLN_Tile tile = &tilemap->tiles[ytile*tilemap->cols + xtile]; /* paint if not empty tile */ - if (tile->index) + if (tile->index != 0) { - /* H/V flip */ - if (tile->flags & FLAG_FLIPX) - srcx = tileset->width - srcx - 1; - if (tile->flags & FLAG_FLIPY) - srcy = tileset->height - srcy - 1; - - /* pinta scanline tile */ - *dstpixel = GetTilesetPixel (tileset, tile->index, srcx, srcy); + const TLN_Tileset tileset = tilemap->tilesets[tile->tileset]; + const uint16_t tile_index = tileset->tiles[tile->index] - 1; + + /* process flip & rotation flags */ + if ((tile->flags & (FLAG_FLIPX + FLAG_FLIPY + FLAG_ROTATE)) != 0) + process_flip_rotation(tile->flags, &scan); + + /* paint RGB pixel value */ + const TLN_Palette palette = layer->palette != NULL ? layer->palette : tileset->palette; + *dstpixel = palette->data[GetTilesetPixel(tileset, tile_index, scan.srcx, scan.srcy)]; } /* next pixel */ - x++; + tx1 += 1; x1 += dx; y1 += dy; - dstpixel++; - } - -draw_end: - if (layer->mosaic.h != 0) - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = layer->mosaic.buffer + offset; - uint8_t* dstptr = GetFramebufferLine (nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - if (layer->blend != NULL) - BlitMosaicBlend (srcptr, layer->palette, dstptr, width, layer->mosaic.w, layer->blend); - else - BlitMosaicSolid (srcptr, layer->palette, dstptr, width, layer->mosaic.w); - } - else - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = engine->tmpindex + offset; - uint8_t* dstptr = GetFramebufferLine (nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - layer->blitters[1] (srcptr, layer->palette, dstptr, width, 1, 0, layer->blend); + dstpixel += 1; } return false; } /* draw scanline of tiled background with per-pixel mapping */ -static bool DrawLayerScanlinePixelMapping (int nlayer, int nscan) +static bool DrawTiledScanlinePixelMapping(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) { - Layer *layer = &engine->layers[nlayer]; - const TLN_Tileset tileset = layer->tileset; + const Layer *layer = (const Layer*)&engine->layers[nlayer]; + bool priority = false; + Tilescan scan = { 0 }; + + /* target lines */ + int x = tx1; + dstpixel += x; + const TLN_Tilemap tilemap = layer->tilemap; - const TLN_Palette palette = layer->palette; + const TLN_Tileset tileset = tilemap->tilesets[0]; const int hstart = layer->hstart + layer->width; const int vstart = layer->vstart + layer->height; - int shift; - TLN_Tile tile; - int x, width; - int xpos, ypos; - int xtile, ytile; - int srcx, srcy; - uint8_t *dstpixel; - TLN_PixelMap* pixel_map; - - /* mosaic effect */ - if (layer->mosaic.h != 0) - { - shift = 0; - dstpixel = layer->mosaic.buffer; - if (nscan % layer->mosaic.h == 0) - memset (dstpixel, 0, engine->framebuffer.width); - else - goto draw_end; - } - else - { - shift = 2; - dstpixel = engine->tmpindex; - memset (dstpixel, 0, engine->framebuffer.width); - } + TLN_PixelMap* pixel_map = &layer->pixel_map[nscan*engine->framebuffer.width + x]; - /* target lines */ - x = layer->clip.x1; - width = layer->clip.x2 - layer->clip.x1; + scan.width = scan.height = scan.stride = tileset->width; - pixel_map = &layer->pixel_map[nscan*engine->framebuffer.width + x]; - while (x < width) + while (x < tx2) { - xpos = abs(hstart + pixel_map->dx) % layer->width; - ypos = abs(vstart + pixel_map->dy) % layer->height; - - xtile = xpos >> tileset->hshift; - ytile = ypos >> tileset->vshift; + int xpos = abs(hstart + pixel_map->dx) % layer->width; + int ypos = abs(vstart + pixel_map->dy) % layer->height; - srcx = xpos & tileset->hmask; - srcy = ypos & tileset->vmask; + int xtile = xpos >> tileset->hshift; + int ytile = ypos >> tileset->vshift; - tile = &tilemap->tiles[ytile*tilemap->cols + xtile]; + scan.srcx = xpos & tileset->hmask; + scan.srcy = ypos & tileset->vmask; + TLN_Tile tile = &tilemap->tiles[ytile*tilemap->cols + xtile]; /* paint if not empty tile */ - if (tile->index) + if (tile->index != 0) { - /* H/V flip */ - if (tile->flags & FLAG_FLIPX) - srcx = tileset->width - srcx - 1; - if (tile->flags & FLAG_FLIPY) - srcy = tileset->height - srcy - 1; + const TLN_Tileset tileset = tilemap->tilesets[tile->tileset]; + const uint16_t tile_index = tileset->tiles[tile->index] - 1; - /* paint tile scanline */ - *dstpixel = GetTilesetPixel (tileset, tile->index, srcx, srcy); + /* process flip & rotation flags */ + if ((tile->flags & (FLAG_FLIPX + FLAG_FLIPY + FLAG_ROTATE)) != 0) + process_flip_rotation(tile->flags, &scan); + + /* paint RGB pixel value */ + const TLN_Palette palette = layer->palette != NULL ? layer->palette : tileset->palette; + *dstpixel = palette->data[GetTilesetPixel(tileset, tile_index, scan.srcx, scan.srcy)]; } /* next pixel */ - x++; - dstpixel++; - pixel_map++; + x += 1; + dstpixel += 1; + pixel_map += 1; } - -draw_end: - if (layer->mosaic.h != 0) - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = layer->mosaic.buffer + offset; - uint8_t* dstptr = GetFramebufferLine (nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - if (layer->blend != NULL) - BlitMosaicBlend (srcptr, layer->palette, dstptr, width, layer->mosaic.w, layer->blend); - else - BlitMosaicSolid (srcptr, layer->palette, dstptr, width, layer->mosaic.w); - } - else - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = engine->tmpindex + offset; - uint8_t* dstptr = GetFramebufferLine (nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - layer->blitters[1] (srcptr, layer->palette, dstptr, width, 1, 0, layer->blend); - } - return true; + return false; } /* draw sprite scanline */ -static bool DrawSpriteScanline (int nsprite, int nscan) +static bool DrawSpriteScanline(int nsprite, uint32_t* dstscan, int nscan, int tx1, int tx2) { - int w; - Sprite *sprite; - uint8_t *srcpixel; - uint8_t *dstscan; - uint32_t *dstpixel; - int srcx, srcy; - int direction; + Sprite* sprite = (Sprite*)&engine->sprites[nsprite]; - sprite = &engine->sprites[nsprite]; + Tilescan scan = { 0 }; + scan.srcx = sprite->srcrect.x1; + scan.srcy = sprite->srcrect.y1 + (nscan - sprite->dstrect.y1); + scan.width = sprite->info->w; + scan.height = sprite->info->h; + scan.stride = sprite->pitch; - /* check sprite coverage */ - if (nscandstrect.y1 || nscan>=sprite->dstrect.y2) - return false; - if (sprite->dstrect.x2 < 0 || sprite->srcrect.x2 < 0) - return false; + /* disable rotation for non-squared sprites */ + uint16_t flags = sprite->flags; + if ((flags & FLAG_ROTATE) && sprite->info->w != sprite->info->h) + flags &= ~FLAG_ROTATE; - dstscan = GetFramebufferLine(nscan); - - srcx = sprite->srcrect.x1; - srcy = sprite->srcrect.y1 + (nscan - sprite->dstrect.y1); - w = sprite->dstrect.x2 - sprite->dstrect.x1; + const int w = sprite->dstrect.x2 - sprite->dstrect.x1; - /* H/V flip */ - if (sprite->flags & FLAG_FLIPX) - { - direction = -1; - srcx = sprite->info->w - srcx - 1; - } - else - direction = 1; - if (sprite->flags & FLAG_FLIPY) - srcy = sprite->info->h - srcy - 1; + /* process rotate & flip flags */ + scan.dx = 1; + if ((flags & (FLAG_FLIPX + FLAG_FLIPY + FLAG_ROTATE)) != 0) + process_flip_rotation(flags, &scan); - srcpixel = sprite->pixels + (srcy*sprite->pitch) + srcx; - dstpixel = (uint32_t*)(dstscan + (sprite->dstrect.x1 << 2)); - sprite->blitter (srcpixel, sprite->palette, dstpixel, w, direction, 0, sprite->blend); + /* blit scanline */ + uint8_t* srcpixel = sprite->pixels + (scan.srcy*sprite->pitch) + scan.srcx; + uint32_t *dstpixel = dstscan + sprite->dstrect.x1; + sprite->blitter(srcpixel, sprite->palette, dstpixel, w, scan.dx, 0, sprite->blend); if (sprite->do_collision) { uint16_t* dstpixel = engine->collision + sprite->dstrect.x1; - DrawSpriteCollision (nsprite, srcpixel, dstpixel, w, direction); + DrawSpriteCollision(nsprite, srcpixel, dstpixel, w, scan.dx); } return true; } /* draw sprite scanline with scaling */ -static bool DrawScalingSpriteScanline (int nsprite, int nscan) +static bool DrawScalingSpriteScanline(int nsprite, uint32_t* dstscan, int nscan, int tx1, int tx2) { - Sprite *sprite; - uint8_t *srcpixel; - uint8_t *dstscan; - uint32_t *dstpixel; - int srcx, srcy; - int dstw,dstx,dx; - struct Palette* palette; + Sprite* sprite = (Sprite*)&engine->sprites[nsprite]; - sprite = &engine->sprites[nsprite]; - - /* check sprite coverage */ - if (nscandstrect.y1 || nscan>=sprite->dstrect.y2) - return false; - if (sprite->dstrect.x2 < 0 || sprite->srcrect.x2 < 0) - return false; - - dstscan = GetFramebufferLine(nscan); - srcx = sprite->srcrect.x1; - srcy = sprite->srcrect.y1 + (nscan - sprite->dstrect.y1)*sprite->dy; - dstw = sprite->dstrect.x2 - sprite->dstrect.x1; + int srcx = sprite->srcrect.x1; + int srcy = sprite->srcrect.y1 + (nscan - sprite->dstrect.y1)*sprite->dy; + int dstw = sprite->dstrect.x2 - sprite->dstrect.x1; /* H/V flip */ + int dstx, dx; if (sprite->flags & FLAG_FLIPX) { srcx = int2fix(sprite->info->w) - srcx; @@ -704,78 +683,21 @@ static bool DrawScalingSpriteScanline (int nsprite, int nscan) if (sprite->flags & FLAG_FLIPY) srcy = int2fix(sprite->info->h) - srcy; - palette = sprite->palette; - srcpixel = sprite->pixels + (fix2int(srcy)*sprite->pitch); - dstpixel = (uint32_t*)(dstscan + (sprite->dstrect.x1 << 2)); - sprite->blitter (srcpixel, sprite->palette, dstpixel, dstw, dx, srcx, sprite->blend); - - if (sprite->do_collision) - { - uint16_t* dstpixel = engine->collision + sprite->dstrect.x1; - DrawSpriteCollisionScaling (nsprite, srcpixel, dstpixel, dstw, dx, srcx); - } - return true; -} - -/* Experimental WIP: blit pre-rotated sprite */ -static bool DrawSpriteScanlineRotation(int nsprite, int nscan) -{ - int w; - Sprite *sprite; - uint8_t *srcpixel; - uint8_t *dstscan; - uint32_t *dstpixel; - int srcx, srcy; - int direction; - - sprite = &engine->sprites[nsprite]; - - /* comprueba que el sprite estแ en este scanline y es visible */ - /* - if (nscandstrect.y1 || nscan >= sprite->dstrect.y2) - return false; - if (sprite->dstrect.x2 < 0 || sprite->srcrect.x2 < 0) - return false; - */ - - if (nscan < sprite->y || nscan > sprite->y + sprite->rotation_bitmap->height) - return false; - dstscan = GetFramebufferLine(nscan); - -/* - srcx = sprite->srcrect.x1; - srcy = sprite->srcrect.y1 + (nscan - sprite->dstrect.y1); - w = sprite->dstrect.x2 - sprite->dstrect.x1; - */ - srcx = sprite->srcrect.x1; - srcy = sprite->srcrect.y1 + (nscan - sprite->y); - w = sprite->dstrect.x2 - sprite->dstrect.x1; - - /* volteado H/V */ - if (sprite->flags & FLAG_FLIPX) - { - direction = -1; - srcx = sprite->rotation_bitmap->width - srcx - 1; - } - else - direction = 1; - if (sprite->flags & FLAG_FLIPY) - srcy = sprite->rotation_bitmap->height - srcy - 1; - - srcpixel = sprite->rotation_bitmap->data + (srcy*sprite->rotation_bitmap->pitch) + srcx; - dstpixel = (uint32_t*)(dstscan + (sprite->dstrect.x1 << 2)); - sprite->blitter(srcpixel, sprite->palette, dstpixel, w, direction, 0, sprite->blend); + /* blit scanline */ + uint8_t* srcpixel = sprite->pixels + (fix2int(srcy)*sprite->pitch); + uint32_t* dstpixel = dstscan + sprite->dstrect.x1; + sprite->blitter(srcpixel, sprite->palette, dstpixel, dstw, dx, srcx, sprite->blend); if (sprite->do_collision) { uint16_t* dstpixel = engine->collision + sprite->dstrect.x1; - DrawSpriteCollision(nsprite, srcpixel, dstpixel, w, direction); + DrawSpriteCollisionScaling(nsprite, srcpixel, dstpixel, dstw, dx, srcx); } return true; } /* updates per-pixel sprite collision buffer */ -static void DrawSpriteCollision (int nsprite, uint8_t *srcpixel, uint16_t *dstpixel, int width, int dx) +static void DrawSpriteCollision(int nsprite, uint8_t *srcpixel, uint16_t *dstpixel, int width, int dx) { while (width) { @@ -789,17 +711,17 @@ static void DrawSpriteCollision (int nsprite, uint8_t *srcpixel, uint16_t *dstpi *dstpixel = (uint16_t)nsprite; } srcpixel += dx; - dstpixel++; - width--; + dstpixel += 1; + width -= 1; } } /* updates per-pixel sprite collision buffer for scaled sprite */ -static void DrawSpriteCollisionScaling (int nsprite, uint8_t *srcpixel, uint16_t *dstpixel, int width, int dx, int srcx) +static void DrawSpriteCollisionScaling(int nsprite, uint8_t *srcpixel, uint16_t *dstpixel, int width, int dx, int srcx) { while (width) { - uint32_t src = *(srcpixel + srcx/(1 << FIXED_BITS)); + uint32_t src = *(srcpixel + srcx / (1 << FIXED_BITS)); if (src) { if (*dstpixel != 0xFFFF) @@ -808,391 +730,234 @@ static void DrawSpriteCollisionScaling (int nsprite, uint8_t *srcpixel, uint16_t engine->sprites[*dstpixel].collision = true; } *dstpixel = (uint16_t)nsprite; - } + } /* next pixel */ srcx += dx; - dstpixel++; - width--; + dstpixel += 1; + width -= 1; } } /* draws regular bitmap scanline for bitmap-based layer */ -static bool DrawBitmapScanline(int nlayer, int nscan) +static bool DrawBitmapScanline(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) { - const Layer *layer = &engine->layers[nlayer]; - TLN_Bitmap bitmap = layer->bitmap; - TLN_Palette palette = layer->palette; - uint8_t *srcpixel; - int shift; - int x, x1; - int xpos, ypos; - int direction, width; - uint8_t *dstpixel; - bool color_key; - - /* mosaic effect */ - if (layer->mosaic.h != 0) - { - shift = 0; - dstpixel = layer->mosaic.buffer; - if (nscan % layer->mosaic.h == 0) - memset(dstpixel, 0, engine->framebuffer.width); - else - goto draw_end; - } - else - { - shift = 2; - dstpixel = GetFramebufferLine(nscan); - } + const Layer *layer = (const Layer*)&engine->layers[nlayer]; /* target lines */ - x = layer->clip.x1; - dstpixel += (x << shift); - ypos = (layer->vstart + nscan) % layer->height; - xpos = (layer->hstart + x) % layer->width; - direction = 1; - color_key = true; + int x = tx1; + dstpixel += x; + int ypos = (layer->vstart + nscan) % layer->height; + int xpos = (layer->hstart + x) % layer->width; /* draws bitmap scanline */ - while (x < layer->clip.x2) + TLN_Bitmap bitmap = layer->bitmap; + TLN_Palette palette = layer->palette != NULL ? layer->palette : bitmap->palette; + while (x < tx2) { /* get effective width */ - width = layer->width - xpos; - x1 = x + width; - if (x1 > layer->clip.x2) - x1 = layer->clip.x2; + int width = layer->width - xpos; + int x1 = x + width; + if (x1 > tx2) + x1 = tx2; width = x1 - x; - srcpixel = (uint8_t*)get_bitmap_ptr(bitmap, xpos, ypos); - layer->blitters[color_key](srcpixel, palette, dstpixel, width, direction, 0, layer->blend); + uint8_t* srcpixel = (uint8_t*)get_bitmap_ptr(bitmap, xpos, ypos); + layer->blitters[1](srcpixel, palette, dstpixel, width, 1, 0, layer->blend); x += width; - width <<= shift; dstpixel += width; xpos = 0; } - -draw_end: - if (layer->mosaic.h != 0) - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = layer->mosaic.buffer + offset; - uint8_t* dstptr = GetFramebufferLine(nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - if (layer->blend != NULL) - BlitMosaicBlend(srcptr, layer->palette, dstptr, width, layer->mosaic.w, layer->blend); - else - BlitMosaicSolid(srcptr, layer->palette, dstptr, width, layer->mosaic.w); - } - return false; } /* draws regular bitmap scanline for bitmap-based layer with scaling */ -static bool DrawBitmapScanlineScaling(int nlayer, int nscan) +static bool DrawBitmapScanlineScaling(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) { - const Layer *layer = &engine->layers[nlayer]; - int shift; - uint8_t *srcpixel; - int x, x1; - int xpos, ypos; - int direction, width; - uint8_t *dstpixel; - fix_t fix_tilewidth; - fix_t fix_x; - fix_t dx; - bool color_key; - - /* mosaic effect */ - if (layer->mosaic.h != 0) - { - shift = 0; - dstpixel = layer->mosaic.buffer; - if (nscan % layer->mosaic.h == 0) - memset(dstpixel, 0, engine->framebuffer.width); - else - goto draw_end; - } - else - { - shift = 2; - dstpixel = GetFramebufferLine(nscan); - } + const Layer *layer = (const Layer*)&engine->layers[nlayer]; /* target line */ - x = layer->clip.x1; - dstpixel += (x << shift); - xpos = (layer->hstart + fix2int(x*layer->dx)) % layer->width; + int x = tx1; + dstpixel += x; + int xpos = (layer->hstart + fix2int(x*layer->dx)) % layer->width; /* fill whole scanline */ - fix_x = int2fix(x); - while (x < layer->clip.x2) + const TLN_Bitmap bitmap = layer->bitmap; + const TLN_Palette palette = layer->palette != NULL ? layer->palette : bitmap->palette; + fix_t fix_x = int2fix(x); + while (x < tx2) { - int tilescalewidth; - - ypos = layer->vstart + fix2int(nscan*layer->dy); + int ypos = layer->vstart + fix2int(nscan*layer->dy); if (ypos < 0) ypos = layer->height + ypos; else ypos = ypos % layer->height; /* get effective width */ - width = layer->width - xpos; - dx = int2fix(width); - fix_tilewidth = width * layer->xfactor; + int width = layer->width - xpos; + fix_t dx = int2fix(width); + fix_t fix_tilewidth = width * layer->xfactor; fix_x += fix_tilewidth; - x1 = fix2int(fix_x); - tilescalewidth = x1 - x; + int x1 = fix2int(fix_x); + int tilescalewidth = x1 - x; if (tilescalewidth) dx /= tilescalewidth; else dx = 0; /* right clipping */ - if (x1 > layer->clip.x2) - x1 = layer->clip.x2; + if (x1 > tx2) + x1 = tx2; width = x1 - x; /* draw bitmap scanline */ - direction = dx; - srcpixel = (uint8_t*)get_bitmap_ptr(layer->bitmap, xpos, ypos); - color_key = true; - layer->blitters[color_key](srcpixel, layer->palette, dstpixel, width, direction, 0, layer->blend); + uint8_t* srcpixel = (uint8_t*)get_bitmap_ptr(bitmap, xpos, ypos); + layer->blitters[1](srcpixel, palette, dstpixel, width, dx, 0, layer->blend); /* next */ - width <<= shift; dstpixel += width; x = x1; xpos = 0; } - -draw_end: - if (layer->mosaic.h != 0) - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = layer->mosaic.buffer + offset; - uint8_t* dstptr = GetFramebufferLine(nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - if (layer->blend != NULL) - BlitMosaicBlend(srcptr, layer->palette, dstptr, width, layer->mosaic.w, layer->blend); - else - BlitMosaicSolid(srcptr, layer->palette, dstptr, width, layer->mosaic.w); - } - return false; } /* draws regular bitmap scanline for bitmap-based layer with affine transform */ -static bool DrawBitmapScanlineAffine(int nlayer, int nscan) +static bool DrawBitmapScanlineAffine(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) { - Layer *layer = &engine->layers[nlayer]; - const TLN_Palette palette = layer->palette; - const TLN_Bitmap bitmap = layer->bitmap; - int shift; - int x, width; - int x1, y1, x2, y2; - fix_t dx, dy; - int xpos, ypos; - uint8_t *dstpixel; - Point2D p1, p2; - - /* mosaic effect */ - if (layer->mosaic.h != 0) - { - shift = 0; - dstpixel = layer->mosaic.buffer; - if (nscan % layer->mosaic.h == 0) - memset(dstpixel, 0, engine->framebuffer.width); - else - goto draw_end; - } - else - { - shift = 2; - dstpixel = engine->tmpindex; - memset(dstpixel, 0, engine->framebuffer.width); - } - - /* target lines */ - x = layer->clip.x1; - width = layer->clip.x2; + const Layer *layer = (const Layer*)&engine->layers[nlayer]; + bool priority = false; - xpos = layer->hstart; - ypos = layer->vstart + nscan; + int xpos = layer->hstart; + int ypos = layer->vstart + nscan; - Point2DSet(&p1, (math2d_t)xpos, (math2d_t)ypos); - Point2DSet(&p2, (math2d_t)xpos + width, (math2d_t)ypos); - Point2DMultiply(&p1, &layer->transform); - Point2DMultiply(&p2, &layer->transform); + Point2D p1, p2; + Point2DSet(&p1, (math2d_t)xpos + tx1, (math2d_t)ypos); + Point2DSet(&p2, (math2d_t)xpos + tx2, (math2d_t)ypos); + Point2DMultiply(&p1, (Matrix3*)&layer->transform); + Point2DMultiply(&p2, (Matrix3*)&layer->transform); - x1 = float2fix(p1.x); - y1 = float2fix(p1.y); - x2 = float2fix(p2.x); - y2 = float2fix(p2.y); + int x1 = float2fix(p1.x); + int y1 = float2fix(p1.y); + int x2 = float2fix(p2.x); + int y2 = float2fix(p2.y); - dx = (x2 - x1) / width; - dy = (y2 - y1) / width; + const int twidth = tx2 - tx1; + const int dx = (x2 - x1) / twidth; + const int dy = (y2 - y1) / twidth; - while (x < width) + const TLN_Bitmap bitmap = layer->bitmap; + const TLN_Palette palette = layer->palette != NULL ? layer->palette : bitmap->palette; + while (tx1 < tx2) { xpos = abs((fix2int(x1) + layer->width)) % layer->width; ypos = abs((fix2int(y1) + layer->height)) % layer->height; - *dstpixel = *get_bitmap_ptr(bitmap, xpos, ypos); + *dstpixel = palette->data[*get_bitmap_ptr(bitmap, xpos, ypos)]; /* next pixel */ - x++; + tx1 += 1; x1 += dx; y1 += dy; - dstpixel++; + dstpixel += 1; } - -draw_end: - if (layer->mosaic.h != 0) - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = layer->mosaic.buffer + offset; - uint8_t* dstptr = GetFramebufferLine(nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - if (layer->blend != NULL) - BlitMosaicBlend(srcptr, layer->palette, dstptr, width, layer->mosaic.w, layer->blend); - else - BlitMosaicSolid(srcptr, layer->palette, dstptr, width, layer->mosaic.w); - } - else - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = engine->tmpindex + offset; - uint8_t* dstptr = GetFramebufferLine(nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - layer->blitters[1](srcptr, layer->palette, dstptr, width, 1, 0, layer->blend); - } - return false; + return priority; } /* draws regular bitmap scanline for bitmap-based layer with per-pixel mapping */ -static bool DrawBitmapScanlinePixelMapping(int nlayer, int nscan) +static bool DrawBitmapScanlinePixelMapping(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) { - Layer *layer = &engine->layers[nlayer]; - const TLN_Bitmap bitmap = layer->bitmap; - const TLN_Palette palette = layer->palette; - const int hstart = layer->hstart + layer->width; - const int vstart = layer->vstart + layer->height; - int shift; - int x, width; - int xpos, ypos; - uint8_t *dstpixel; - TLN_PixelMap* pixel_map; - - /* mosaic effect */ - if (layer->mosaic.h != 0) - { - shift = 0; - dstpixel = layer->mosaic.buffer; - if (nscan % layer->mosaic.h == 0) - memset(dstpixel, 0, engine->framebuffer.width); - else - goto draw_end; - } - else - { - shift = 2; - dstpixel = engine->tmpindex; - memset(dstpixel, 0, engine->framebuffer.width); - } + const Layer *layer = (const Layer*)&engine->layers[nlayer]; + bool priority = false; /* target lines */ - x = layer->clip.x1; - width = layer->clip.x2 - layer->clip.x1; + int x = tx1; + dstpixel += x; - pixel_map = &layer->pixel_map[nscan*engine->framebuffer.width + x]; - while (x < width) + const int hstart = layer->hstart + layer->width; + const int vstart = layer->vstart + layer->height; + const TLN_Bitmap bitmap = layer->bitmap; + const TLN_Palette palette = layer->palette != NULL ? layer->palette : bitmap->palette; + const TLN_PixelMap* pixel_map = &layer->pixel_map[nscan*engine->framebuffer.width + x]; + while (x < tx2) { - xpos = abs(hstart + pixel_map->dx) % layer->width; - ypos = abs(vstart + pixel_map->dy) % layer->height; - *dstpixel = *get_bitmap_ptr(bitmap, xpos, ypos); + int xpos = abs(hstart + pixel_map->dx) % layer->width; + int ypos = abs(vstart + pixel_map->dy) % layer->height; + *dstpixel = layer->palette->data[*get_bitmap_ptr(bitmap, xpos, ypos)]; /* next pixel */ - x++; - dstpixel++; - pixel_map++; - } - -draw_end: - if (layer->mosaic.h != 0) - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = layer->mosaic.buffer + offset; - uint8_t* dstptr = GetFramebufferLine(nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - if (layer->blend != NULL) - BlitMosaicBlend(srcptr, layer->palette, dstptr, width, layer->mosaic.w, layer->blend); - else - BlitMosaicSolid(srcptr, layer->palette, dstptr, width, layer->mosaic.w); - } - else - { - int offset = (layer->clip.x1 << shift); - uint8_t* srcptr = engine->tmpindex + offset; - uint8_t* dstptr = GetFramebufferLine(nscan) + offset; - int width = layer->clip.x2 - layer->clip.x1; - - layer->blitters[1](srcptr, layer->palette, dstptr, width, 1, 0, layer->blend); + x += 1; + dstpixel += 1; + pixel_map += 1; } - return false; + return priority; } /* draws regular object layer scanline */ -static bool DrawLayerObjectScanline(int nlayer, int nscan) +static bool DrawObjectScanline(int nlayer, uint32_t* dstpixel, int nscan, int tx1, int tx2) { - const Layer* layer = &engine->layers[nlayer]; + const Layer *layer = (const Layer*)&engine->layers[nlayer]; struct _Object* object = layer->objects->list; - int x1 = layer->hstart + layer->clip.x1; - int x2 = layer->hstart + layer->clip.x2; + struct _Object tmpobject = { 0 }; + + int x1 = layer->hstart + tx1; + int x2 = layer->hstart + tx2; int y = layer->vstart + nscan; - uint8_t* dstscan = GetFramebufferLine(nscan); - uint8_t* pixels = layer->spriteset->bitmap->data; - uint32_t pitch = layer->spriteset->bitmap->pitch; + uint32_t* dstscan = GetFramebufferLine(nscan); + bool priority = false; while (object != NULL) { - if (IsObjectInLine(object, x1, x2, y)) + /* swap width & height for rotated objects */ + memcpy(&tmpobject, object, sizeof(struct _Object)); + if (tmpobject.flags & FLAG_ROTATE) { - int w; - uint8_t *srcpixel; - uint32_t *dstpixel; - int srcx, srcy; - int dstx1, dstx2; - - srcx = 0; - srcy = y - object->data.y; - dstx1 = object->data.x - x1; - dstx2 = dstx1 + object->data.width; - if (dstx1 < layer->clip.x1) + tmpobject.width = object->height; + tmpobject.height = object->width; + } + + if (IsObjectInLine(&tmpobject, x1, x2, y) && tmpobject.visible && tmpobject.bitmap != NULL) + { + Tilescan scan = { 0 }; + scan.srcx = 0; + scan.srcy = y - tmpobject.y; + + int dstx1 = tmpobject.x - x1; + int dstx2 = dstx1 + tmpobject.width; + if (dstx1 < tx1) { - int w = layer->clip.x1 - dstx1; - srcx = w; + int w = tx1 - dstx1; + scan.srcx = w; dstx1 = 0; } - if (dstx2 > layer->clip.x2) + if (dstx2 > tx2) + dstx2 = tx2; + int w = dstx2 - dstx1; + + TLN_Bitmap bitmap = tmpobject.bitmap; + scan.width = bitmap->width; + scan.height = bitmap->height; + scan.stride = bitmap->pitch; + + /* process rotate & flip flags */ + scan.dx = 1; + if ((tmpobject.flags & (FLAG_FLIPX + FLAG_FLIPY + FLAG_ROTATE)) != 0) + process_flip_rotation(tmpobject.flags, &scan); + + /* paint tile scanline */ + uint8_t* srcpixel = get_bitmap_ptr(bitmap, scan.srcx, scan.srcy); + uint32_t *target = dstscan; + if (tmpobject.flags & FLAG_PRIORITY) { - dstx2 = layer->clip.x2; + target = engine->priority; + priority = true; } - w = dstx2 - dstx1; - - srcpixel = pixels + object->sprite->offset + (srcy*pitch) + srcx; - dstpixel = (uint32_t*)(dstscan + (dstx1 << 2)); - layer->blitters[1] (srcpixel, layer->palette, dstpixel, w, 1, 0, layer->blend); + uint32_t* dstpixel = target + dstx1; + layer->blitters[1](srcpixel, bitmap->palette, dstpixel, w, scan.dx, 0, layer->blend); } object = object->next; } - return true; + return priority; } /* draw modes */ @@ -1206,29 +971,29 @@ enum }; /* table of function pointers to draw procedures */ -static const ScanDrawPtr drawers[MAX_DRAW_TYPE][MAX_DRAW_MODE] = +static const ScanDrawPtr draw_delegates[MAX_DRAW_TYPE][MAX_DRAW_MODE] = { - { DrawSpriteScanline, DrawScalingSpriteScanline, NULL, NULL}, - { DrawLayerScanline, DrawLayerScanlineScaling, DrawLayerScanlineAffine, DrawLayerScanlinePixelMapping }, - { DrawBitmapScanline, DrawBitmapScanlineScaling, DrawBitmapScanlineAffine, DrawBitmapScanlinePixelMapping }, - { DrawLayerObjectScanline, NULL, NULL, NULL }, + { DrawSpriteScanline, DrawScalingSpriteScanline, NULL, NULL}, + { DrawTiledScanline, DrawTiledScanlineScaling, DrawTiledScanlineAffine, DrawTiledScanlinePixelMapping }, + { DrawBitmapScanline, DrawBitmapScanlineScaling, DrawBitmapScanlineAffine, DrawBitmapScanlinePixelMapping }, + { DrawObjectScanline, NULL, NULL, NULL }, }; /* returns suitable draw procedure based on layer configuration */ ScanDrawPtr GetLayerDraw(Layer* layer) { if (layer->tilemap != NULL) - return drawers[DRAW_TILED_LAYER][layer->mode]; + return draw_delegates[DRAW_TILED_LAYER][layer->mode]; else if (layer->bitmap != NULL) - return drawers[DRAW_BITMAP_LAYER][layer->mode]; - else if (layer->spriteset != NULL) - return drawers[DRAW_OBJECT_LAYER][layer->mode]; + return draw_delegates[DRAW_BITMAP_LAYER][layer->mode]; + else if (layer->objects != NULL) + return draw_delegates[DRAW_OBJECT_LAYER][layer->mode]; else return NULL; } /* returns suitable draw procedure based on sprite configuration */ -ScanDrawPtr GetSpriteDraw (draw_t mode) +ScanDrawPtr GetSpriteDraw(draw_t mode) { - return drawers[DRAW_SPRITE][mode]; + return draw_delegates[DRAW_SPRITE][mode]; } diff --git a/src/Draw.h b/src/Draw.h index 4e85399c..397d2b32 100644 --- a/src/Draw.h +++ b/src/Draw.h @@ -22,10 +22,12 @@ typedef enum } draw_t; -typedef bool (*ScanDrawPtr)(int,int); +typedef bool (*ScanDrawPtr)(int,uint32_t*,int,int,int); typedef struct Layer Layer; ScanDrawPtr GetLayerDraw (Layer* layer); ScanDrawPtr GetSpriteDraw (draw_t mode); +extern bool DrawScanline(void); + #endif diff --git a/src/Engine.h b/src/Engine.h index 1a9f13a9..1bae82f3 100644 --- a/src/Engine.h +++ b/src/Engine.h @@ -11,38 +11,52 @@ #ifndef _ENGINE_H #define _ENGINE_H +#define NUM_PALETTES 8 +#define INTERNAL_FPS 60 + #include "Tilengine.h" #include "Sprite.h" #include "Layer.h" #include "Animation.h" #include "Bitmap.h" #include "Blitters.h" +#include "List.h" /* motor */ typedef struct Engine { - uint32_t header; /* object signature to identify as engine context */ - uint8_t* priority; /* scanline que recibe los tiles con prioridad */ - uint16_t* collision; /* scanline con IDs de colision de sprites */ - uint8_t* tmpindex; /* indices temporales para capas transformadas con transparencia */ - int numsprites; /* nบ de sprites */ - Sprite* sprites; /* puntero a los sprites */ - int numlayers; /* nบ de capas */ - Layer* layers; /* puntero a las capas */ - int numanimations; - Animation* animations; - bool dopriority; - TLN_Error error; /* ultimo error */ - TLN_LogLevel log_level; /* logging level */ + uint32_t header; /* object signature to identify as engine context */ + uint32_t* priority; /* buffer receiving tiles with priority */ + uint16_t* collision; /* buffer with sprite coverage IDs for per-pixel collision */ + uint32_t* linebuffer; /* buffer for intermediate scanline output */ + int numsprites; /* number of sprites */ + Sprite* sprites; /* pointer to sprite buffer */ + int numlayers; /* number of layers */ + Layer* layers; /* pointer to layer buffer */ + int numanimations; /* number of animations */ + Animation* animations; /* pointer to animation buffer */ + bool dopriority; /* there is some data in "priority" buffer that need blitting */ + TLN_Error error; /* last error code */ + TLN_LogLevel log_level; /* logging level */ + + uint32_t bgcolor; /* background color */ + TLN_Bitmap bgbitmap; /* background bitmap */ + TLN_Palette bgpalette; /* background bitmap palette */ + TLN_Palette palettes[NUM_PALETTES]; /* optional global palettes */ + ScanBlitPtr blit_fast; /* blitter for background bitmap */ + uint8_t* blend_table; /* current blending table */ + TLN_VideoCallback cb_raster;/* raster callback */ + TLN_VideoCallback cb_frame; /* frame callback */ + int frame; /* current frame number */ + int line; /* current scanline */ + int target_fps; - uint32_t bgcolor; /* color de fondo */ - TLN_Bitmap bgbitmap; /* bitmap de fondo */ - TLN_Palette bgpalette; /* paleta de fondo */ - ScanBlitPtr blit_fast; /* blitter para bitmap de fondo */ - uint8_t* mod_table; /* tabla de modulacion */ - void (*raster)(int); - void (*frame)(int); - int line; /* lํnea actual */ + List list_sprites; /* linked list active of sprites */ + List list_animations; /* linked list active of animations */ + int sprite_mask_top; /* top scanline for sprite masking */ + int sprite_mask_bottom; /* bottom scanline for sprite masking */ + int xworld, yworld; /* world coordinates with TLN_SetWorldPosition() */ + bool dirty; /* world position updated since last draw */ struct { @@ -60,6 +74,6 @@ extern Engine* engine; extern void tln_trace(TLN_LogLevel log_level, const char* format, ...); #define GetFramebufferLine(line) \ - (engine->framebuffer.data + (line*engine->framebuffer.pitch)) + (uint32_t*)(engine->framebuffer.data + (line*engine->framebuffer.pitch)) #endif diff --git a/src/Hash.h b/src/Hash.h deleted file mode 100644 index 22cf5ff2..00000000 --- a/src/Hash.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _HASH_H -#define _HASH_H - -#include - -typedef unsigned int hash_t; - -hash_t hash (unsigned int crc, const void *buf, size_t size); - -#endif diff --git a/src/Layer.c b/src/Layer.c index a87f92fa..c8d8133f 100644 --- a/src/Layer.c +++ b/src/Layer.c @@ -17,133 +17,97 @@ #include "Tilemap.h" #include "Tables.h" #include "ObjectList.h" +#include "Bitmap.h" -static void SelectBlitter (Layer* layer); +static void SetBlitter (Layer* layer); -/*! - * \brief - * Configures a background layer with the specified tileset and tilemap - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param tileset - * Optional reference to the tileset to assign. If the tilemap has a reference to its own tileset, passing NULL will assign the default tileset. - * - * \param tilemap - * Reference to the tilemap to assign - * - * \remarks - * This function doesn't modify the current position nor the blend mode, - * but assigns the palette of the specified tileset - * - * \see - * TLN_DisableLayer() - */ -bool TLN_SetLayer (int nlayer, TLN_Tileset tileset, TLN_Tilemap tilemap) +bool TLN_SetLayer(int nlayer, TLN_Tileset tileset, TLN_Tilemap tilemap) { Layer *layer; if (nlayer >= engine->numlayers) { - TLN_SetLastError (TLN_ERR_IDX_LAYER); + TLN_SetLastError(TLN_ERR_IDX_LAYER); return false; } layer = &engine->layers[nlayer]; layer->ok = false; - if (!CheckBaseObject (tilemap, OT_TILEMAP)) + if (!CheckBaseObject(tilemap, OT_TILEMAP)) return false; - - /* seleccionar tileset del tilemap */ + + /* select tilemsp's own tileset */ if (tileset == NULL) - tileset = tilemap->tileset; - - if (!CheckBaseObject (tileset, OT_TILESET)) + tileset = tilemap->tilesets[0]; + + if (!CheckBaseObject(tileset, OT_TILESET)) return false; - - if (tilemap->maxindex <= tileset->numtiles) - { - layer->tileset = tileset; - layer->tilemap = tilemap; - layer->width = tilemap->cols*tileset->width; - layer->height = tilemap->rows*tileset->height; - if (tileset->palette) - TLN_SetLayerPalette (nlayer, tileset->palette); - } + + layer->tilemap = tilemap; + layer->width = tilemap->cols*tileset->width; + layer->height = tilemap->rows*tileset->height; layer->bitmap = NULL; - layer->spriteset = NULL; - layer->ok = true; - layer->draw = GetLayerDraw (layer); + layer->objects = NULL; + layer->type = LAYER_TILE; - /* aplica atributo de prioridad del tileset al tilemap */ - if (tileset->attributes != NULL) + /* common operations per tileset */ + int ts; + for (ts = 0; ts < MAX_TILESETS; ts += 1) { - const int num_tiles = tilemap->rows * tilemap->cols; - int c; - Tile* tile = tilemap->tiles; - for (c=0; ctilesets[ts]; + if (tileset == NULL) + break; + + /* apply priority attribute */ + if (tileset->attributes != NULL) { - if (tile->index != 0) + const int num_tiles = tilemap->rows * tilemap->cols; + int c; + Tile* tile = tilemap->tiles; + for (c = 0; c < num_tiles; c++, tile++) { - if (tileset->attributes[tile->index - 1].priority == true) - tile->flags |= FLAG_PRIORITY; - else - tile->flags &= ~FLAG_PRIORITY; + if (tile->index != 0 && tile->index < tileset->numtiles) + { + if (tileset->attributes[tile->index - 1].priority == true) + tile->flags |= FLAG_PRIORITY; + else + tile->flags &= ~FLAG_PRIORITY; + } } } - } - - /* inicia animaciones */ - if (tileset->sp != NULL) - { - int index, c; - TLN_Sequence sequence; - /* desactiva animaciones de patr๓n de la capa actual */ - for (c=0; cnumanimations; c++) + /* start animations */ + if (tileset->sp != NULL) { - Animation* animation = &engine->animations[c]; - if (animation->idx == nlayer && animation->type == TYPE_TILESET) - TLN_DisableAnimation (c); - } + int c; + TLN_Sequence sequence; - /* inicia las del nuevo tileset */ - sequence = tileset->sp->sequences; - while (sequence != NULL) - { - index = TLN_GetAvailableAnimation (); - if (index != -1) + c = 0; + sequence = tileset->sp->sequences; + while (sequence != NULL) { - TLN_SetTilesetAnimation (index, nlayer, sequence); + SetTilesetAnimation(tileset, c, sequence); sequence = sequence->next; + c += 1; } - else - sequence = NULL; } } - SelectBlitter (layer); + if (tilemap->visible) + { + layer->ok = true; + layer->draw = GetLayerDraw(layer); + SetBlitter(layer); + } + TLN_SetLastError (TLN_ERR_OK); return true; } -/*! -* \brief -* Configures a background layer with the specified full bitmap -* -* \param nlayer -* Layer index [0, num_layers - 1] -* -* \param bitmap -* Reference to the bitmap to assign -* -* \remarks -* This function doesn't modify the current position nor the blend mode, -* but assigns the palette of the specified bitmap -* -* \see -* TLN_DisableLayer() -*/ +bool TLN_SetLayerTilemap(int nlayer, TLN_Tilemap tilemap) +{ + return TLN_SetLayer(nlayer, NULL, tilemap); +} + bool TLN_SetLayerBitmap(int nlayer, TLN_Bitmap bitmap) { Layer *layer; @@ -158,21 +122,19 @@ bool TLN_SetLayerBitmap(int nlayer, TLN_Bitmap bitmap) if (!CheckBaseObject(bitmap, OT_BITMAP)) return false; - layer->tileset = NULL; layer->tilemap = NULL; layer->bitmap = bitmap; - layer->spriteset = NULL; + layer->objects = NULL; layer->width = bitmap->width; layer->height = bitmap->height; - if (bitmap->palette) - TLN_SetLayerPalette(nlayer, bitmap->palette); /* require palette */ - if (layer->palette) + if (bitmap->palette != NULL) { + layer->type = LAYER_BITMAP; layer->ok = true; layer->draw = GetLayerDraw(layer); - SelectBlitter(layer); + SetBlitter(layer); TLN_SetLastError(TLN_ERR_OK); return true; } @@ -184,52 +146,66 @@ bool TLN_SetLayerBitmap(int nlayer, TLN_Bitmap bitmap) } } -/*! - * \brief Configures a background layer with a object list - * - * \param nlayer Layer index [0, num_layers - 1] - * \param objects Reference to the TLN_ObjectList to attach - * \param spriteset Reference to the TLN_Spriteset with the graphics - * \param width Layer width - * \param height Layer height - */ -bool TLN_SetLayerObjects(int nlayer, TLN_ObjectList objects, TLN_Spriteset spriteset, int width, int height) +bool TLN_SetLayerObjects(int nlayer, TLN_ObjectList objects, TLN_Tileset tileset) { - Layer *layer; + Layer *layer = NULL; + TLN_Object* item = NULL; + if (nlayer >= engine->numlayers) { TLN_SetLastError(TLN_ERR_IDX_LAYER); return false; } - layer = &engine->layers[nlayer]; layer->ok = false; - if (!CheckBaseObject(spriteset, OT_SPRITESET)) + + if (!CheckBaseObject(objects, OT_OBJECTLIST)) + { + TLN_SetLastError(TLN_ERR_REF_LIST); return false; + } + + if (tileset == NULL) + tileset = objects->tileset; + if (!CheckBaseObject(tileset, OT_TILESET) || tileset->tstype != TILESET_IMAGES) + { + TLN_SetLastError(TLN_ERR_REF_TILESET); + return false; + } - layer->tileset = NULL; layer->tilemap = NULL; layer->bitmap = NULL; - layer->spriteset = spriteset; layer->objects = objects; - layer->width = width; - layer->height = height; - if (spriteset->palette) - TLN_SetLayerPalette(nlayer, spriteset->palette); - - layer->ok = true; - layer->draw = GetLayerDraw(layer); - SelectBlitter(layer); + layer->width = objects->width; + layer->height = objects->height; + layer->type = LAYER_OBJECT; + + /* link objects to actual bitmaps */ + item = objects->list; + while (item) + { + if (item->visible && item->has_gid) + { + item->bitmap = GetTilesetBitmap(tileset, item->gid); + if (item->bitmap) + { + item->width = item->bitmap->width; + item->height = item->bitmap->height; + } + } + item = item->next; + } + + if (objects->visible) + { + layer->ok = true; + layer->draw = GetLayerDraw(layer); + SetBlitter(layer); + } TLN_SetLastError(TLN_ERR_OK); return true; } -/*! - * \brief Sets full layer priority, appearing in front of sprites - * - * \param nlayer Layer index [0, num_layers - 1] - * \param enable Enable (true) or dsiable (false) full priority - */ bool TLN_SetLayerPriority(int nlayer, bool enable) { Layer *layer; @@ -244,58 +220,16 @@ bool TLN_SetLayerPriority(int nlayer, bool enable) return true; } -/*! - * \brief Sets parent layer index to scroll in sync - * - * \param nlayer Layer index [0, num_layers - 1] - * \param parent Index of layer to attach to - * \remarks A layer with a parent gets scroll position from its parent, so they scroll together - */ bool TLN_SetLayerParent(int nlayer, int parent) { - Layer *layer; - if (nlayer >= engine->numlayers || parent >= engine->numlayers) - { - TLN_SetLastError(TLN_ERR_IDX_LAYER); - return false; - } - - layer = &engine->layers[nlayer]; - layer->parent = &engine->layers[parent]; - TLN_SetLastError(TLN_ERR_OK); return true; } -/*! - * \brief Disables layer parent - * - * \param nlayer Layer index [0, num_layers - 1] - * \see TLN_SetLayerParent() - */ bool TLN_DisableLayerParent(int nlayer) { - Layer *layer; - if (nlayer >= engine->numlayers) - { - TLN_SetLastError(TLN_ERR_IDX_LAYER); - return false; - } - - layer = &engine->layers[nlayer]; - layer->parent = NULL; - TLN_SetLastError(TLN_ERR_OK); return true; } -/*! - * \brief - * Returns the layer width in pixels - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \see TLN_SetLayer(), TLN_GetLayerHeight() - */ int TLN_GetLayerWidth (int nlayer) { if (nlayer >= engine->numlayers) @@ -308,15 +242,6 @@ int TLN_GetLayerWidth (int nlayer) return engine->layers[nlayer].width; } -/*! - * \brief - * Returns the layer height in pixels - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \see TLN_SetLayer(), TLN_GetLayerWidth() - */ int TLN_GetLayerHeight (int nlayer) { if (nlayer >= engine->numlayers) @@ -329,22 +254,6 @@ int TLN_GetLayerHeight (int nlayer) return engine->layers[nlayer].height; } -/*! - * \brief - * Sets the blending mode (transparency effect) - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param mode - * Member of the TLN_Blend enumeration - * - * \param factor - * Deprecated as of 1.12, left for backwards compatibility but doesn't have effect. - * - * \see - * Blending - */ bool TLN_SetLayerBlendMode (int nlayer, TLN_Blend mode, uint8_t factor) { Layer *layer; @@ -356,31 +265,11 @@ bool TLN_SetLayerBlendMode (int nlayer, TLN_Blend mode, uint8_t factor) layer = &engine->layers[nlayer]; layer->blend = SelectBlendTable (mode); - SelectBlitter (layer); + SetBlitter (layer); TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Sets the color palette to the layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param palette - * Reference to the palette to assign to the layer - * - * When a layer is assigned with a tileset with the function TLN_SetLayer(), it - * automatically sets the palette of the assigned tileset to the layer. - * Use this function to override it and set another palette - * - * \remarks - * Call this function inside a raster callback to change the palette in the middle - * of the frame to get raster effect colors, like and "underwater" palette below the - * water line in a partially submerged background, or a gradient palette in an area at - * the top of the screen to simulate a "depth fog effect" in a pseudo 3d background - */ bool TLN_SetLayerPalette (int nlayer, TLN_Palette palette) { Layer *layer; @@ -398,61 +287,92 @@ bool TLN_SetLayerPalette (int nlayer, TLN_Palette palette) } layer->palette = palette; - TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Gets the attached palette of a layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \returns - * Reference of the palette assigned to the layer - * - * \see - * TLN_SetLayerPalette() - */ TLN_Palette TLN_GetLayerPalette (int nlayer) { - if (nlayer >= engine->numlayers) + if (nlayer < engine->numlayers) { - TLN_SetLastError (TLN_ERR_IDX_LAYER); + Layer* layer = &engine->layers[nlayer]; + TLN_SetLastError(TLN_ERR_OK); + + if (layer->palette != NULL) + return layer->palette; + else if (layer->bitmap != NULL && layer->bitmap->palette != NULL) + return layer->bitmap->palette; + else if (layer->tilemap != NULL && layer->tilemap->tilesets[0] != NULL && layer->tilemap->tilesets[0]->palette != NULL) + return layer->tilemap->tilesets[0]->palette; + + TLN_SetLastError(TLN_ERR_REF_PALETTE); return NULL; } - TLN_SetLastError (TLN_ERR_OK); - return engine->layers[nlayer].palette; + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return NULL; +} + +TLN_LayerType TLN_GetLayerType(int nlayer) +{ + if (nlayer < engine->numlayers) + { + TLN_SetLastError(TLN_ERR_OK); + return engine->layers[nlayer].type; + } + + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return LAYER_NONE; +} + +TLN_Tileset TLN_GetLayerTileset(int nlayer) +{ + if (nlayer < engine->numlayers && engine->layers[nlayer].tilemap != NULL) + { + TLN_SetLastError(TLN_ERR_OK); + return engine->layers[nlayer].tilemap->tilesets[0]; + } + + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return NULL; +} + +TLN_Tilemap TLN_GetLayerTilemap(int nlayer) +{ + if (nlayer < engine->numlayers) + { + TLN_SetLastError(TLN_ERR_OK); + return engine->layers[nlayer].tilemap; + } + + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return NULL; +} + +TLN_Bitmap TLN_GetLayerBitmap(int nlayer) +{ + if (nlayer < engine->numlayers) + { + TLN_SetLastError(TLN_ERR_OK); + return engine->layers[nlayer].bitmap; + } + + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return NULL; +} + +TLN_ObjectList TLN_GetLayerObjects(int nlayer) +{ + if (nlayer < engine->numlayers) + { + TLN_SetLastError(TLN_ERR_OK); + return engine->layers[nlayer].objects; + } + + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return NULL; } -/*! - * \brief - * Sets the position of the tileset that corresponds to the upper left corner - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param hstart - * Horizontal offset in the tileset on the left side - * - * \param vstart - * Vertical offset in the tileset on the top side - * - * The tileset usually spans an area much bigger than the viewport. Use this - * function to move the viewport insde the tileset. Change this value progressively - * for each frame to get a scrolling effect - * - * \remarks - * Call this function inside a raster callback to get a raster scrolling effect. - * Use this to create horizontal strips of the same - * layer that move at different speeds to simulate depth. The extreme case of this effect, where - * the position is changed in each scanline, is called "line scroll" and was the technique used by - * games such as Street Fighter II to simualte a pseudo 3d floor, or many racing games to simulate - * a 3D road. - */ bool TLN_SetLayerPosition (int nlayer, int hstart, int vstart) { Layer *layer; @@ -469,45 +389,44 @@ bool TLN_SetLayerPosition (int nlayer, int hstart, int vstart) return false; } + /* wrapping */ layer->hstart = hstart % layer->width; layer->vstart = vstart % layer->height; - - /* warping por la izquierda */ if (layer->hstart < 0) layer->hstart += layer->width; if (layer->vstart < 0) layer->vstart += layer->height; TLN_SetLastError (TLN_ERR_OK); - layer->ok = true; + if ((layer->tilemap && layer->tilemap->visible) || (layer->objects && layer->objects->visible)) + layer->ok = true; return true; } -/*! - * \brief - * Gets info about the tile located in tilemap space - * - * \param nlayer - * Id of the layer to query [0, num_layers - 1] - * - * \param x - * x position - * - * \param y - * y position - * - * \param info - * Pointer to an application-allocated TLN_TileInfo struct that will get the data - * - * \returns - * true if success or false if error - * - * \remarks - * Use this function to implement collision detection between sprites and the main background layer. - * - * \see - * TLN_TileInfo - */ +int TLN_GetLayerX(int nlayer) +{ + if (nlayer >= engine->numlayers) + { + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return 0; + } + + TLN_SetLastError(TLN_ERR_OK); + return engine->layers[nlayer].hstart; +} + +int TLN_GetLayerY(int nlayer) +{ + if (nlayer >= engine->numlayers) + { + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return 0; + } + + TLN_SetLastError(TLN_ERR_OK); + return engine->layers[nlayer].vstart; +} + bool TLN_GetLayerTile (int nlayer, int x, int y, TLN_TileInfo* info) { Layer *layer; @@ -532,13 +451,15 @@ bool TLN_GetLayerTile (int nlayer, int x, int y, TLN_TileInfo* info) } layer = &engine->layers[nlayer]; - if (!CheckBaseObject (layer->tileset, OT_TILESET) || !CheckBaseObject (layer->tilemap, OT_TILEMAP)) + if (!CheckBaseObject(layer->tilemap, OT_TILEMAP) || !CheckBaseObject (layer->tilemap->tilesets[0], OT_TILESET)) return false; - tileset = layer->tileset; tilemap = layer->tilemap; + tileset = tilemap->tilesets[0]; - xpos = x % layer->width; + xpos = x % layer->width; + if (xpos < 0) + xpos += layer->width; xtile = xpos >> tileset->hshift; srcx = xpos & tileset->hmask; @@ -565,6 +486,7 @@ bool TLN_GetLayerTile (int nlayer, int x, int y, TLN_TileInfo* info) info->yoffset = srcy; if (tile->index != 0) { + tileset = tilemap->tilesets[tile->tileset]; info->index = tile->index - 1; info->flags = tile->flags; info->color = GetTilesetPixel (tileset, tile->index, srcx, srcy); @@ -577,26 +499,6 @@ bool TLN_GetLayerTile (int nlayer, int x, int y, TLN_TileInfo* info) return true; } -/*! - * \brief - * Enables column offset mode for this layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param offset - * Array of offsets to set. Set NULL to disable column offset mode - * - * Column offset is a value that is added or substracted (depeinding on the - * sign) to the vertical position for that layer (see TLN_SetLayerPosition) for - * each column in the tilemap assigned to that layer. - * - * \remarks - * This feature is tipically used to simulate vertical strips moving at different - * speeds, or combined with a line scroll effect, to fake rotations where the angle - * is small. The Sega Genesis games Puggsy and Chuck Rock II used this trick to simulate - * partially rotating backgrounds - */ bool TLN_SetLayerColumnOffset (int nlayer, int* offset) { if (nlayer >= engine->numlayers) @@ -610,6 +512,30 @@ bool TLN_SetLayerColumnOffset (int nlayer, int* offset) return true; } +bool TLN_EnableLayer(int nlayer) +{ + Layer* layer = NULL; + + if (nlayer >= engine->numlayers) + { + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return false; + } + + layer = &engine->layers[nlayer]; + + /* check proper config */ + if (layer->type == LAYER_TILE && layer->tilemap != NULL || layer->type == LAYER_BITMAP && layer->bitmap != NULL || layer->type == LAYER_OBJECT && layer->objects != NULL) + { + layer->ok = true; + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return true; + } + + TLN_SetLastError(TLN_ERR_NULL_POINTER); + return false; +} + /*! * \brief * Disables the specified layer so it is not drawn @@ -637,30 +563,6 @@ bool TLN_DisableLayer (int nlayer) return true; } -/*! - * \brief - * Sets affine transform matrix to enable rotating and scaling of this layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param affine - * Pointer to an TLN_Affine matrix, or NULL to disable it - * - * Enable the transformation matrix to give the layer the capabilities of the famous - * Super Nintendo / Famicom Mode 7. Beware that the rendering of a transformed layer - * uses more CPU than a regular layer. Unlike the original Mode 7, that could only transform - * the single layer available, Tilengine can transform all the layers at the same time. The only - * limitation is the available CPU power. - * - * \remarks - * Call this function inside a raster callback to set the transformation matrix in the middle of - * the frame. Setting it for each scanline is the trick used by many Super Nintendo games to fake - * a 3D perspective projection. - * - * \see - * TLN_SetLayerTransform() - */ bool TLN_SetLayerAffineTransform (int nlayer, TLN_Affine *affine) { Layer *layer; @@ -689,7 +591,7 @@ bool TLN_SetLayerAffineTransform (int nlayer, TLN_Affine *affine) layer->mode = MODE_TRANSFORM; layer->draw = GetLayerDraw (layer); - SelectBlitter (layer); + SetBlitter (layer); /*printf ("TLN_SetLayerAffineTransform (ptr=%08Xh, a=%.02f, d=%.02f,%.02f, s=%.02f,%.02f)\n", affine, affine->angle, affine->dx, affine->dy, affine->sx, affine->sy);*/ @@ -701,34 +603,6 @@ bool TLN_SetLayerAffineTransform (int nlayer, TLN_Affine *affine) return TLN_ResetLayerMode (nlayer); } -/*! - * \brief - * Sets affine transform matrix to enable rotating and scaling of this layer - * - * \param layer - * Layer index [0, num_layers - 1] - * - * \param angle - * Rotation angle in degrees - * - * \param dx - * Horizontal displacement - * - * \param dy - * Vertical displacement - * - * \param sx - * Horizontal scaling - * - * \param sy - * Vertical scaling - * - * \remarks - * This function is a simple wrapper to TLN_SetLayerAffineTransform() without using the TLN_Affine struct - * - * \see - * TLN_SetLayerAffineTransform() - */ bool TLN_SetLayerTransform (int layer, float angle, float dx, float dy, float sx, float sy) { TLN_Affine affine; @@ -742,29 +616,7 @@ bool TLN_SetLayerTransform (int layer, float angle, float dx, float dy, float sx return TLN_SetLayerAffineTransform (layer, &affine); } -/*! - * \brief - * Sets simple scaling - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param sx - * Horizontal scale factor - * - * \param sy - * Vertical scale factor - * - * By default the scaling factor of a given layer is 1.0f, 1.0f, which means - * no scaling. Use values below 1.0 to downscale (shrink) and above 1.0 to upscale (enlarge). - * Call TLN_ResetLayerMode() to disable scaling - * - * Write detailed description for TLN_SetLayerScaling here. - * - * \see TLN_ResetLayerMode() - * - */ -bool TLN_SetLayerScaling (int nlayer, float sx, float sy) +bool TLN_SetLayerScaling (int nlayer, float xfactor, float yfactor) { Layer *layer; if (nlayer >= engine->numlayers) @@ -774,28 +626,16 @@ bool TLN_SetLayerScaling (int nlayer, float sx, float sy) } layer = &engine->layers[nlayer]; - layer->xfactor = float2fix(sx); - layer->dx = float2fix((1.0f/sx)); - layer->dy = float2fix((1.0f/sy)); + layer->xfactor = float2fix(xfactor); + layer->dx = float2fix((1.0f / xfactor)); + layer->dy = float2fix((1.0f / yfactor)); layer->mode = MODE_SCALING; layer->draw = GetLayerDraw (layer); - SelectBlitter (layer); + SetBlitter (layer); TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Sets the table for pixel mapping render mode - * - * \param nlayer - * Layer index [0, num_layers - 1] - * \param table - * User-provided array of hres*vres sized TLN_PixelMap items - * - * \see - * TLN_SetLayerScaling(), TLN_SetLayerAffineTransform() - */ bool TLN_SetLayerPixelMapping (int nlayer, TLN_PixelMap* table) { Layer *layer; @@ -815,18 +655,6 @@ bool TLN_SetLayerPixelMapping (int nlayer, TLN_PixelMap* table) return true; } -/*! - * \brief - * Disables scaling or affine transform for the layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * Write detailed description for TLN_ResetLayerMode here. - * - * \see - * TLN_SetLayerScaling(), TLN_SetLayerAffineTransform() - */ bool TLN_ResetLayerMode (int nlayer) { Layer *layer; @@ -839,95 +667,98 @@ bool TLN_ResetLayerMode (int nlayer) layer = &engine->layers[nlayer]; layer->mode = MODE_NORMAL; layer->draw = GetLayerDraw (layer); - SelectBlitter (layer); + SetBlitter (layer); TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Enables clipping rectangle on selected layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param x1 - * left coordinate - * - * \param y1 - * top coordinate - * - * \param x2 - * right coordinate - * - * \param y2 - * bottom coordinate - * - * \see - * TLN_DisableLayerClip() - */ bool TLN_SetLayerClip (int nlayer, int x1, int y1, int x2, int y2) { - Layer *layer; + return TLN_SetLayerWindow(nlayer, x1, y1, x2, y2, false); +} + +bool TLN_DisableLayerClip (int nlayer) +{ if (nlayer >= engine->numlayers) { TLN_SetLastError (TLN_ERR_IDX_LAYER); return false; } - layer = &engine->layers[nlayer]; - layer->clip.x1 = x1 >= 0 && x1 <= engine->framebuffer.width? x1 : 0; - layer->clip.x2 = x2 >= 0 && x2 <= engine->framebuffer.width? x2 : engine->framebuffer.width; - layer->clip.y1 = y1 >= 0 && y1 <= engine->framebuffer.height? y1 : 0; - layer->clip.y2 = y2 >= 0 && y2 <= engine->framebuffer.height? y2 : engine->framebuffer.height; + LayerWindow* window = &engine->layers[nlayer].window; + window->x1 = 0; + window->x2 = engine->framebuffer.width; + window->y1 = 0; + window->y2 = engine->framebuffer.height; TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Disables clipping rectangle on selected layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \see - * TLN_SetLayerClip() - */ -bool TLN_DisableLayerClip (int nlayer) +bool TLN_SetLayerWindow(int nlayer, int x1, int y1, int x2, int y2, bool invert) { - Layer *layer; if (nlayer >= engine->numlayers) { - TLN_SetLastError (TLN_ERR_IDX_LAYER); + TLN_SetLastError(TLN_ERR_IDX_LAYER); return false; } - - layer = &engine->layers[nlayer]; - layer->clip.x1 = 0; - layer->clip.x2 = engine->framebuffer.width; - layer->clip.y1 = 0; - layer->clip.y2 = engine->framebuffer.height; - TLN_SetLastError (TLN_ERR_OK); + + LayerWindow* window = &engine->layers[nlayer].window; + window->x1 = x1 >= 0 && x1 <= engine->framebuffer.width ? x1 : 0; + window->x2 = x2 >= 0 && x2 <= engine->framebuffer.width ? x2 : engine->framebuffer.width; + window->y1 = y1 >= 0 && y1 <= engine->framebuffer.height ? y1 : 0; + window->y2 = y2 >= 0 && y2 <= engine->framebuffer.height ? y2 : engine->framebuffer.height; + window->invert = invert; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_SetLayerWindowColor(int nlayer, uint8_t r, uint8_t g, uint8_t b, TLN_Blend blend) +{ + if (nlayer >= engine->numlayers) + { + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return false; + } + + LayerWindow* window = &engine->layers[nlayer].window; + window->color = PackRGB32(r, g, b); + window->blend = SelectBlendTable(blend); + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_DisableLayerWindow(int nlayer) +{ + if (nlayer >= engine->numlayers) + { + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return false; + } + + LayerWindow* window = &engine->layers[nlayer].window; + window->x1 = 0; + window->x2 = engine->framebuffer.width; + window->y1 = 0; + window->y2 = engine->framebuffer.height; + window->invert = false; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_DisableLayerWindowColor(int nlayer) +{ + if (nlayer >= engine->numlayers) + { + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return false; + } + + LayerWindow* window = &engine->layers[nlayer].window; + window->color = 0; + window->blend = NULL; return true; } -/*! - * \brief - * Enables mosaic effect (pixelation) for selected layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \param width - * horizontal pixel size - * - * \param height - * vertical pixel size - * - * \see - * TLN_DisableLayerMosaic() - */ bool TLN_SetLayerMosaic (int nlayer, int width, int height) { Layer *layer; @@ -940,21 +771,11 @@ bool TLN_SetLayerMosaic (int nlayer, int width, int height) layer = &engine->layers[nlayer]; layer->mosaic.w = width; layer->mosaic.h = height; - SelectBlitter (layer); + SetBlitter (layer); TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Disables mosaic effect for selected layer - * - * \param nlayer - * Layer index [0, num_layers - 1] - * - * \see - * TLN_SetLayerMosaic() - */ bool TLN_DisableLayerMosaic (int nlayer) { Layer *layer; @@ -970,25 +791,16 @@ bool TLN_DisableLayerMosaic (int nlayer) return true; } -static void SelectBlitter (Layer* layer) +Layer* GetLayer(int index) { - bool scaling = layer->mode == MODE_SCALING; - bool blend; - int bpp; + return &engine->layers[index]; +} - /* without mosaic effect */ - if (layer->mosaic.h == 0) - { - blend = layer->blend != NULL; - bpp = 32; - } - /* with mosaic effect */ - else - { - blend = false; - bpp = 8; - } +static void SetBlitter (Layer* layer) +{ + bool scaling = layer->mode == MODE_SCALING; + bool blend = layer->blend != NULL && layer->mosaic.h == 0; - layer->blitters[0] = GetBlitter (bpp, false, scaling, blend); - layer->blitters[1] = GetBlitter (bpp, true, scaling, blend); + layer->blitters[0] = SelectBlitter (false, scaling, blend); + layer->blitters[1] = SelectBlitter(true, scaling, blend); } diff --git a/src/Layer.h b/src/Layer.h index 675ba3aa..ff9a4eb4 100644 --- a/src/Layer.h +++ b/src/Layer.h @@ -16,52 +16,65 @@ #include "Blitters.h" #include "Math2D.h" +typedef struct +{ + int x1, y1, x2, y2; /* clip region */ + bool invert; /* false=clip outside, true=clip inside */ + uint8_t* blend; /* optional solid color blend function */ + uint32_t color; /* color for optional blend function */ +} +LayerWindow; + /* capa */ typedef struct Layer { - /* configuraci๓n */ - TLN_Tileset tileset; /* puntero al tileset */ - TLN_Tilemap tilemap; /* puntero al array de memoria con el mapa de tiles */ - TLN_Palette palette; /* puntero a la paleta */ - TLN_Bitmap bitmap; /* puntero al bitmap (modo bitmap) */ - TLN_Spriteset spriteset; /* puntero al spriteset (modo object) */ - TLN_ObjectList objects; /* pointer to object list (objects mode) */ - int width; /* anchura total en pํxeles */ - int height; /* altura total en pํxeles */ + TLN_LayerType type; /* layer type */ + TLN_Tilemap tilemap; /* pointer to tilemap */ + TLN_Palette palette; /* pointer to current color alette */ + TLN_Bitmap bitmap; /* pointer to bitmap (bitmap layer mode) */ + TLN_ObjectList objects; /* pointer to object list (objects layer mode) */ + int width; /* layer width in pixels */ + int height; /* layer height in pixels */ bool ok; bool affine; ScanDrawPtr draw; ScanBlitPtr blitters[2]; Matrix3 transform; - int* column; /* offset de columna (opcional) */ + int* column; /* column offset (optional) */ fix_t xfactor; fix_t dx; fix_t dy; - uint8_t* blend; /* puntero a tabla de transparencia (NULL = no hay) */ - TLN_PixelMap* pixel_map; /* puntero a tabla de pixel map (NULL = no hay) */ + uint8_t* blend; /* pointer to blend table */ + TLN_PixelMap* pixel_map; /* pointer to pixel mapping table */ draw_t mode; - struct Layer* parent; /* optional link layer */ bool priority; /* whole layer in front of regular sprites */ - - /* */ - int hstart; /* offset de inicio horizontal */ - int vstart; /* offset de inicio vertical */ - /* clip */ + /* world mode related data */ struct { - int x1, y1, x2, y2; + int offsetx, offsety; + float xfactor, yfactor; } - clip; + world; + bool dirty; /* requires update before draw */ + + /* */ + int hstart; /* horizontal start offset */ + int vstart; /* vertical start offset*/ - /* mosaico */ + /* clip */ + LayerWindow window; + + /* mosaic */ struct { - int w,h; /* tama๑o del pixel */ - uint8_t* buffer; /* linea temporal */ + int w, h; /* virtual pixel size */ + uint32_t* buffer; /* line buffer */ } mosaic; } Layer; +Layer* GetLayer(int index); + #endif \ No newline at end of file diff --git a/src/List.c b/src/List.c new file mode 100644 index 00000000..44a5cf60 --- /dev/null +++ b/src/List.c @@ -0,0 +1,109 @@ +/* generic, array-based double linked list */ + +#include +#include +#include +#include "List.h" +#include "Debug.h" + +static ListNode* get_node(List* list, int node) +{ + if (node != -1) + { + uint8_t* addr = (uint8_t*)list->base; + return (ListNode*)(addr + node * (list->node_size)); + } + return NULL; +} + +void ListInit(List* list, ListNode* base, int node_size, int num_nodes) +{ + int c; + list->base = (void*)base; + list->node_size = node_size; + list->num_nodes = num_nodes; + list->first = -1; + list->last = -1; + + for (c = 0; c < num_nodes; c += 1) + { + ListNode* node = get_node(list, c); + node->prev = -1; + node->next = -1; + } +} + +void ListLinkNodes(List* list, int num1, int num2) +{ + ListNode* node; + + node = get_node(list, num1); + if (node) + node->next = num2; + + node = get_node(list, num2); + if (node) + node->prev = num1; +} + +void ListUnlinkNode(List* list, int num) +{ + ListNode* node = get_node(list, num); + ListNode* node_prev = get_node(list, node->prev); + ListNode* node_next = get_node(list, node->next); + + if (node_prev) + node_prev->next = node->next; + if (node_next) + node_next->prev = node->prev; + if (list->first == num) + list->first = node->next; + if (list->last == num) + list->last = node->prev; + node->prev = -1; + node->next = -1; + ListPrint(list); +} + +void ListAppendNode(List* list, int num) +{ + if (list->first == -1) + list->first = num; + ListLinkNodes(list, list->last, num); + list->last = num; + ListPrint(list); +} + +int ListGetPrev(List* list, int num) +{ + ListNode* node = get_node(list, num); + return node->prev; +} + +int ListGetNext(List* list, int num) +{ + ListNode* node = get_node(list, num); + return node->next; +} + +void ListPrint(List* list) +{ +#ifdef _DEBUG + int index; + int c = 0; + debugmsg("list: "); + index = list->first; + while (index != -1) + { + ListNode *node = get_node(list, index); + debugmsg("%d ", index); + index = node->next; + c += 1; + if (c > list->num_nodes) + { + exit(0); + } + } + debugmsg("\n"); +#endif +} diff --git a/src/List.h b/src/List.h new file mode 100644 index 00000000..4fbb62ea --- /dev/null +++ b/src/List.h @@ -0,0 +1,29 @@ +#ifndef _LIST_H +#define _LIST_H + +typedef struct +{ + int prev; /* index of prev node */ + int next; /* index of next node */ +} +ListNode; + +typedef struct +{ + void* base; /* ptr to first "ListNode" item */ + int node_size; /* size of nodes */ + int num_nodes; /* total number of nodes in array */ + int first; /* index to first node */ + int last; /* index to last node */ +} +List; + +void ListInit(List* list, ListNode* base, int node_size, int num_nodes); +void ListLinkNodes(List* list, int num1, int num2); +void ListUnlinkNode(List* list, int node); +void ListAppendNode(List* list, int node); +void ListPrint(List* list); +int ListGetPrev(List* list, int num); +int ListGetNext(List* list, int num); + +#endif diff --git a/src/LoadBitmap.c b/src/LoadBitmap.c index 7200f1b5..2a045973 100644 --- a/src/LoadBitmap.c +++ b/src/LoadBitmap.c @@ -14,41 +14,180 @@ #include "LoadFile.h" #include "png.h" #include "DIB.h" +#include "Bitmap.h" +#include "Palette.h" static TLN_Bitmap LoadPNG (const char* filename); static TLN_Bitmap LoadBMP (const char* filename); -/* fix old libpng referencing removed symbols from the CRT */ -#if _MSC_VER >= 1900 -#ifdef __cplusplus -extern "C" { -#endif +typedef struct +{ + uint32_t items[255]; + uint16_t count; +} +Set; + +static void set_init(Set* set) +{ + set->count = 0; +} - FILE* __cdecl __iob_func(void) +static int set_get_index(Set* set, uint32_t value) +{ + int c; + for (c = 0; c < set->count; c += 1) { - return NULL; + if (set->items[c] == value) + return c; } + return -1; +} -#ifdef __cplusplus +static bool set_add(Set* set, uint32_t value) +{ + int index; + if (set->count == 255) + return false; + + index = set_get_index(set, value); + if (index == -1) + { + set->items[set->count] = value; + set->count += 1; + } + return true; } -#endif - -#pragma comment(lib, "legacy_stdio_definitions.lib") -#endif - -/*! - * \brief - * Load image file (8-bit BMP or PNG) - * - * \param filename - * File name with the image - * - * \returns - * Handler to the loaded image or NULL if error - * - * \see - * TLN_DeleteBitmap() - */ + +static TLN_Palette BuildPaletteFromSet(Set* colors) +{ + int c; + RGBQUAD srccolor; + + TLN_Palette palette = TLN_CreatePalette(colors->count + 1); + uint32_t* dstcolor = (uint32_t*)palette->data; + + *dstcolor = 0xFFFF00FF; /* pink */ + dstcolor += 1; + for (c = 0; c < colors->count; c += 1) + { + srccolor.value = colors->items[c]; + *dstcolor = PackRGB32(srccolor.b, srccolor.g, srccolor.r); + dstcolor += 1; + } + return palette; +} + +static TLN_Bitmap Convert24ToIndexed(TLN_Bitmap source) +{ + TLN_Bitmap bitmap = NULL; + Set colors; + int x, y; + uint8_t* srcscan; + uint8_t* dstscan; + + /* count unique colors up to 255 */ + set_init(&colors); + srcscan = source->data; + for (y = 0; y < source->height; y += 1) + { + RGBTRIPLE* color = (RGBTRIPLE*)srcscan; + for (x = 0; x < source->width; x += 1) + { + uint32_t value = PackRGB32(color->r, color->g, color->b); + if (!set_add(&colors, value)) + return NULL; + color += 1; + } + srcscan += source->pitch; + } + + /* create new bitmap at 8 bpp */ + bitmap = TLN_CreateBitmap(source->width, source->height, 8); + srcscan = source->data; + dstscan = bitmap->data; + + /* set colors with palette indexes */ + for (y = 0; y < source->height; y += 1) + { + RGBTRIPLE* srccolor = (RGBTRIPLE*)srcscan; + uint8_t* dstcolor = dstscan; + for (x = 0; x < source->width; x += 1) + { + uint32_t value = PackRGB32(srccolor->r, srccolor->g, srccolor->b); + *dstcolor = set_get_index(&colors, value) + 1; + srccolor += 1; + dstcolor += 1; + } + + srcscan += source->pitch; + dstscan += bitmap->pitch; + } + + /* create attached palette and set actual colors */ + bitmap->palette = BuildPaletteFromSet(&colors); + + return bitmap; +} + +static TLN_Bitmap Convert32ToIndexed(TLN_Bitmap source) +{ + TLN_Bitmap bitmap = NULL; + Set colors; + int x,y; + uint8_t* srcscan; + uint8_t* dstscan; + + /* count unique colors up to 255 */ + set_init(&colors); + srcscan = source->data; + for (y = 0; y < source->height; y += 1) + { + RGBQUAD* color = (RGBQUAD*)srcscan; + for (x = 0; x < source->width; x += 1) + { + if (color->a >= 128) + { + color->a = 255; + if (!set_add(&colors, color->value)) + return NULL; + } + else + color->a = 0; + color += 1; + } + srcscan += source->pitch; + } + + /* create new bitmap at 8 bpp */ + bitmap = TLN_CreateBitmap(source->width, source->height, 8); + srcscan = source->data; + dstscan = bitmap->data; + + /* set colors with palette indexes */ + for (y = 0; y < source->height; y += 1) + { + RGBQUAD* srccolor = (RGBQUAD*)srcscan; + uint8_t* dstcolor = dstscan; + for (x = 0; x < source->width; x += 1) + { + if (srccolor->a == 0) + *dstcolor = 0; + else + *dstcolor = set_get_index(&colors, srccolor->value) + 1; + srccolor += 1; + dstcolor += 1; + } + + srcscan += source->pitch; + dstscan += bitmap->pitch; + } + + /* create attached palette and set actual colors */ + bitmap->palette = BuildPaletteFromSet(&colors); + + return bitmap; +} + TLN_Bitmap TLN_LoadBitmap (const char* filename) { TLN_Bitmap bitmap; @@ -69,6 +208,27 @@ TLN_Bitmap TLN_LoadBitmap (const char* filename) { /* accept only 8 bpp */ int bpp = TLN_GetBitmapDepth (bitmap); + if (bpp == 24) + { + TLN_Bitmap indexed = Convert24ToIndexed(bitmap); + if (indexed != NULL) + { + TLN_DeleteBitmap(bitmap); + bitmap = indexed; + bpp = 8; + } + } + else if (bpp == 32) + { + TLN_Bitmap indexed = Convert32ToIndexed(bitmap); + if (indexed != NULL) + { + TLN_DeleteBitmap(bitmap); + bitmap = indexed; + bpp = 8; + } + } + if (bpp == 8) TLN_SetLastError (TLN_ERR_OK); else diff --git a/src/LoadFile.c b/src/LoadFile.c index b22f0a9c..e99378e2 100644 --- a/src/LoadFile.c +++ b/src/LoadFile.c @@ -28,13 +28,6 @@ struct } static assets[MAX_ASSETS] = { 0 }; -/*! - * \brief - * Sets base path for TLN_LoadXXX functions. - * - * \param path - * Base path. Files will load at path/filename. Can be NULL - */ void TLN_SetLoadPath (const char* path) { size_t trailing; @@ -52,29 +45,12 @@ void TLN_SetLoadPath (const char* path) localpath[trailing] = 0; } -/*! - * \brief Open the resource package with optional aes-128 key and binds it - * \param filename file with the resource package (.dat extension) - * \param key optional null-terminated ASCII string with aes decryption key - * \return true if package opened and made current, or false if error - * \remarks - * When the package is opened, it's globally bind to all TLN_LoadXXX functions. - * The assets inside the package are indexed with their original path/file as when - * they were plain files. As long as the structure used to build the package - * matches the original structure of the assets, the TLN_SetLoadPath() and the TLN_LoadXXX - * functions will work transparently, easing the migration with minimal changes. - * \sa TLN_CloseResourcePack - */ bool TLN_OpenResourcePack(const char* filename, const char* key) { respack = ResPack_Open(filename, key); return respack != NULL; } -/*! - * \brief Closes current resource package and unbinds it - * \sa TLN_OpenResourcePack - */ void TLN_CloseResourcePack(void) { if (respack != NULL) @@ -211,3 +187,64 @@ bool CheckFile (const char* filename) FileClose (pf); return true; } + +/* returns file extension in lowercase */ +void SplitFilename(const char* filename, FileInfo* fileinfo) +{ + if (filename == NULL || fileinfo == NULL) + return; + + int len = 0; + char* block1 = (char*)strrchr(filename, SLASH); + char* block2 = (char*)strrchr(filename, '.'); + if (block1 == NULL) + block1 = (char*)strrchr(filename, BACKSLASH); + + memset(fileinfo, 0, sizeof(FileInfo)); + + /* path */ + if (block1) + { + block1 += 1; + len = (int)(block1 - filename) - 1; + memcpy(fileinfo->path, filename, len); + fileinfo->path[len] = 0; + } + else + block1 = (char*)filename; + + /* name + ext */ + if (block2 && block2 > block1) + { + /* name */ + len = (int)(block2 - block1); + if (block1 == NULL) + block1 = (char*)filename; + memcpy(fileinfo->name, block1, len); + fileinfo->name[len] = 0; + + /* ext */ + block2 += 1; + strncpy(fileinfo->ext, block2, 16); + } + + /* name only */ + else + strncpy(fileinfo->name, block1, 200); +} + +/* builds complete file path */ +void BuildFilePath(char* full_path, int len, const char* path, const char* name, const char* ext) +{ + bool valid_path = path != NULL && path[0] != 0; + bool valid_ext = ext != NULL && ext[0] != 0; + + if (valid_path && valid_ext) + snprintf(full_path, len, "%s/%s.%s", path, name, ext); + else if (valid_path) + snprintf(full_path, len, "%s/%s", path, name); + else if (valid_ext) + snprintf(full_path, len, "%s.%s", name, ext); + else + snprintf(full_path, len, "%s", name); +} diff --git a/src/LoadFile.h b/src/LoadFile.h index b92f86ee..5eff6b5a 100644 --- a/src/LoadFile.h +++ b/src/LoadFile.h @@ -26,9 +26,27 @@ typedef SSIZE_T ssize_t; #endif #endif -void* LoadFile (const char* filename, ssize_t* out_size); -void FileClose(FILE* pf); -FILE* FileOpen (const char* filename); -bool CheckFile (const char* filename); +typedef struct +{ + char path[200]; + char name[200]; + char ext[16]; +} +FileInfo; + +#ifdef __cplusplus +extern "C" { +#endif + + void* LoadFile(const char* filename, ssize_t* out_size); + void FileClose(FILE* pf); + FILE* FileOpen(const char* filename); + bool CheckFile(const char* filename); + void SplitFilename(const char* filename, FileInfo* fileinfo); + void BuildFilePath(char* full_path, int len, const char* path, const char* name, const char* ext); + +#ifdef __cplusplus +} +#endif #endif diff --git a/src/LoadPalette.c b/src/LoadPalette.c index 6dbfc112..ba08ed9b 100644 --- a/src/LoadPalette.c +++ b/src/LoadPalette.c @@ -12,6 +12,7 @@ #include #include "Tilengine.h" #include "LoadFile.h" +#include "Palette.h" #define SWAP(w) ((w)&0xFF)<<8 | ((w)>>8) @@ -26,23 +27,6 @@ struct } trailing; -/*! - * \brief - * Loads a palette from a standard .act file - * - * \param filename - * ACT file containing the palette to load - * - * \returns - * A reference to the newly loaded palette, or NULL if error - * - * \remarks - * Palettes are also automatically created when loading tilesets and spritesets. - * Use the functions TLN_GetTilesetPalette() and TLN_GetSpritesetPalette() to retrieve them. - * - * \see - * TLN_GetTilesetPalette(), TLN_GetSpritesetPalette() - */ TLN_Palette TLN_LoadPalette (const char* filename) { FILE *pf; diff --git a/src/LoadSequencePack.c b/src/LoadSequencePack.c index b2ec5117..f5bfc7d1 100644 --- a/src/LoadSequencePack.c +++ b/src/LoadSequencePack.c @@ -135,24 +135,6 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, return handler; } -/*! - * \brief - * Loads a sqx file containing one or more sequences - * - * \param filename - * SQX filename with the sequences to load - * - * \returns - * Reference to the newly created TLN_SequencePack() or NULL if error - * - * \remarks - * A SQX file can contain many sequences. This function loads all of them - * inside a single TLN_SequencePack(). Individual sequences can be later - * queried with TLN_FindSequence() - * - * \see - * TLN_FindSequence() - */ TLN_SequencePack TLN_LoadSequencePack (const char* filename) { SimpleXmlParser parser; diff --git a/src/LoadSpriteset.c b/src/LoadSpriteset.c index 16dd0c54..2d2574dc 100644 --- a/src/LoadSpriteset.c +++ b/src/LoadSpriteset.c @@ -13,110 +13,153 @@ #include #include "Tilengine.h" #include "LoadFile.h" +#include "cJSON.h" -/*! - * \brief - * Loads a spriteset from a png/txt file pair - * - * \param name - * Base name of the files containing the spriteset - * - * \returns - * Reference to the newly loaded spriteset or NULL if error - * - * \remarks - * The spriteset comes in a pair of files called name.png and name.txt. The png file contains - * the spriteset, whereas the txt contains the coordinates of the rectangles that define individual sprites. - * These files can be created wit the spritesheet packer tool (http://spritesheetpacker.codeplex.com/) - * \remarks - * An associated palette is also created, it can be obtained calling TLN_GetSpritesetPalette() - */ -TLN_Spriteset TLN_LoadSpriteset (const char* name) +/* loads txt format: name = x y w h */ +/* loads csv format: name,x,y,w,h */ +static TLN_SpriteData* load_txt_csv(const char* filename, int* num_entries) { - FILE *pf; - char filename[64]; - char line[64]; - int entries = 0; - TLN_Bitmap bitmap; - TLN_Spriteset spriteset; - TLN_SpriteData *sprite_data; - int c; - - /* load png file */ - sprintf (filename, "%s.png", name); - bitmap = TLN_LoadBitmap (filename); - if (!bitmap) - return NULL; - - /* load txt file */ - sprintf (filename, "%s.txt", name); - pf = FileOpen (filename); + TLN_SpriteData* data = NULL; + TLN_SpriteData* entry; + char line[200]; + FILE* pf = FileOpen(filename); if (!pf) - { - TLN_DeleteBitmap (bitmap); - TLN_SetLastError (TLN_ERR_FILE_NOT_FOUND); return NULL; - } /* count lines */ - while (fgets (line, 64, pf)) - entries++; + *num_entries = 0; + while (fgets(line, sizeof(line), pf)) + *num_entries += 1; + fseek(pf, 0, SEEK_SET); - sprite_data = (TLN_SpriteData*)malloc (sizeof(TLN_SpriteData)*entries); - if (!sprite_data) + data = (TLN_SpriteData*)calloc(*num_entries, sizeof(TLN_SpriteData)); + entry = data; + while (fgets(line, sizeof(line), pf)) { - TLN_DeleteBitmap (bitmap); - TLN_SetLastError (TLN_ERR_OUT_OF_MEMORY); - FileClose (pf); - return NULL; + if (strchr(line, '=')) + sscanf(line, "%s = %d %d %d %d", entry->name, &entry->x, &entry->y, &entry->w, &entry->h); + else if (strchr(line, ',')) + sscanf(line, "%64[^,],%d,%d,%d,%d", entry->name, &entry->x, &entry->y, &entry->w, &entry->h); + entry += 1; } + FileClose(pf); + return data; +} - /* read entries */ - fseek (pf, 0, SEEK_SET); - for (c=0; cname, cJSON_GetStringValue(field), sizeof(entry->name)); + + frame = cJSON_GetObjectItem(item, "frame"); + if (frame) { - char* equals; - TLN_SpriteData* entry; + field = cJSON_GetObjectItem(frame, "x"); + if (field) + entry->x = field->valueint; + field = cJSON_GetObjectItem(frame, "y"); + if (field) + entry->y = field->valueint; + field = cJSON_GetObjectItem(frame, "w"); + if (field) + entry->w = field->valueint; + field = cJSON_GetObjectItem(frame, "h"); + if (field) + entry->h = field->valueint; + } +} - /* lee linea */ - fgets (line, 64, pf); - entry = &sprite_data[c]; +/* loads json array format: frames[{filename, frame: {x,y,w,h}}] */ +static TLN_SpriteData* load_json(const char* filename, int* num_entries) +{ + TLN_SpriteData* data = NULL; + TLN_SpriteData* entry; + ssize_t file_size = 0; + cJSON* root; + cJSON* frames; + cJSON* item; + char* buffer = (char*)LoadFile(filename, &file_size); + if (!buffer) + return NULL; - /* format SpriteSheetPacker: name = x y w h */ - equals = strchr (line, '='); - if (equals != NULL) - { - sscanf (line, "%s = %d %d %d %d", entry->name, &entry->x, &entry->y, &entry->w, &entry->h); - continue; - } + root = cJSON_Parse(buffer); + free(buffer); + if (!root) + return NULL; - /* format Leshy SpriteSheet Tool csv: name,x,y,w,h */ - equals = strchr (line, ','); - if (equals != NULL) + frames = cJSON_GetObjectItem(root, "frames"); + if (frames) + { + int array_size = cJSON_GetArraySize(frames); + data = (TLN_SpriteData*)calloc(array_size, sizeof(TLN_SpriteData)); + + entry = data; + cJSON_ArrayForEach(item, frames) { - char* del = line; - while (*del) - { - if (*del == ',') - *del = ' '; - del++; - } - sscanf (line, "%s %d %d %d %d", entry->name, &entry->x, &entry->y, &entry->w, &entry->h); - continue; + parse_json_frame(item, entry); + entry += 1; } + *num_entries = (int)array_size; + } + cJSON_free(root); + return data; +} + +TLN_Spriteset TLN_LoadSpriteset (const char* name) +{ + FileInfo fileinfo = { 0 }; + char filename[200] = { 0 }; + char line[64] = { 0 }; + int entries = 0; + TLN_Bitmap bitmap = NULL; + TLN_Spriteset spriteset = NULL; + TLN_SpriteData *sprite_data = NULL; + + SplitFilename(name, &fileinfo); + + /* load image file: assume png if no extension */ + if (fileinfo.ext[0] != 0) + sprintf(filename, "%s", name); + else + BuildFilePath(filename, sizeof(filename), fileinfo.path, fileinfo.name, "png"); + bitmap = TLN_LoadBitmap (filename); + if (!bitmap) + return NULL; + + /* try different atlas formats: json, csv, txt */ + BuildFilePath(filename, sizeof(filename), fileinfo.path, fileinfo.name, "json"); + sprite_data = load_json(filename, &entries); + if (sprite_data == NULL) + { + BuildFilePath(filename, sizeof(filename), fileinfo.path, fileinfo.name, "csv"); + sprite_data = load_txt_csv(filename, &entries); + } + if (sprite_data == NULL) + { + BuildFilePath(filename, sizeof(filename), fileinfo.path, fileinfo.name, "txt"); + sprite_data = load_txt_csv(filename, &entries); + } + + /* no compatible atlas found */ + if (sprite_data == NULL) + { + TLN_DeleteBitmap(bitmap); + TLN_SetLastError(TLN_ERR_FILE_NOT_FOUND); + return NULL; } - FileClose (pf); /* create */ spriteset = TLN_CreateSpriteset (bitmap, sprite_data, entries); - /* free resources */ - free (sprite_data); - if (spriteset) TLN_SetLastError (TLN_ERR_OK); else TLN_SetLastError (TLN_ERR_OUT_OF_MEMORY); + free(sprite_data); return spriteset; } diff --git a/src/LoadTMX.c b/src/LoadTMX.c new file mode 100644 index 00000000..2bcab4c2 --- /dev/null +++ b/src/LoadTMX.c @@ -0,0 +1,232 @@ +#include +#include +#include "Tilengine.h" +#include "LoadTMX.h" +#include "LoadFile.h" +#include "simplexml.h" +#include "Layer.h" + +static TMXInfo tmxinfo; + +static void init_current_layer(TLN_LayerType type) +{ + TMXLayer* layer = &tmxinfo.layers[tmxinfo.num_layers]; + memset(layer, 0, sizeof(TMXLayer)); + layer->type = type; + layer->visible = true; + layer->parallaxx = layer->parallaxy = 1.0f; +} + +/* XML parser callback */ +static void* handler(SimpleXmlParser parser, SimpleXmlEvent evt, + const char* szName, const char* szAttribute, const char* szValue) +{ + int intvalue = 0; + float floatvalue = 0; + + switch (evt) + { + case ADD_SUBTAG: + if (!strcasecmp(szName, "layer")) + init_current_layer(LAYER_TILE); + else if (!strcasecmp(szName, "objectgroup")) + init_current_layer(LAYER_OBJECT); + else if (!strcasecmp(szName, "imagelayer")) + init_current_layer(LAYER_BITMAP); + else if (!strcasecmp(szName, "tileset")) + { + TMXTileset* tileset = &tmxinfo.tilesets[tmxinfo.num_tilesets]; + memset(tileset, 0, sizeof(TMXTileset)); + } + break; + + case ADD_ATTRIBUTE: + intvalue = atoi(szValue); + floatvalue = (float)atof(szValue); + if (!strcasecmp(szName, "map")) + { + if (!strcasecmp(szAttribute, "width")) + tmxinfo.width = intvalue; + else if (!strcasecmp(szAttribute, "height")) + tmxinfo.height = intvalue; + else if (!strcasecmp(szAttribute, "tilewidth")) + tmxinfo.tilewidth = intvalue; + else if (!strcasecmp(szAttribute, "tileheight")) + tmxinfo.tileheight = intvalue; + else if (!strcasecmp(szAttribute, "backgroundcolor")) + { + sscanf(&szValue[1], "%x", &tmxinfo.bgcolor); + tmxinfo.bgcolor += 0xFF000000; + } + } + + else if (!strcasecmp(szName, "tileset")) + { + TMXTileset* tileset = &tmxinfo.tilesets[tmxinfo.num_tilesets]; + if (!strcasecmp(szAttribute, "firstgid")) + tileset->firstgid = intvalue; + else if (!strcasecmp(szAttribute, "source")) + strncpy(tileset->source, szValue, sizeof(tileset->source)); + } + + else if (!strcasecmp(szName, "layer") || !strcasecmp(szName, "objectgroup") || !strcasecmp(szName, "imagelayer")) + { + TMXLayer* layer = &tmxinfo.layers[tmxinfo.num_layers]; + if (!strcasecmp(szAttribute, "name")) + strncpy(layer->name, szValue, sizeof(layer->name)); + else if (!strcasecmp(szAttribute, "id")) + layer->id = intvalue; + else if (!strcasecmp(szAttribute, "visible")) + layer->visible = (bool)intvalue; + else if (!strcasecmp(szAttribute, "width")) + layer->width = intvalue; + else if (!strcasecmp(szAttribute, "height")) + layer->height = intvalue; + else if (!strcasecmp(szAttribute, "parallaxx")) + layer->parallaxx = floatvalue; + else if (!strcasecmp(szAttribute, "parallaxy")) + layer->parallaxy = floatvalue; + else if (!strcasecmp(szAttribute, "offsetx")) + layer->offsetx = floatvalue; + else if (!strcasecmp(szAttribute, "offsety")) + layer->offsety = floatvalue; + else if (!strcasecmp(szAttribute, "opacity")) + layer->opacity = floatvalue; + else if (!strcasecmp(szAttribute, "tintcolor")) + sscanf(&szValue[1], "%x", &layer->tintcolor); + } + + else if (!strcasecmp(szName, "image")) + { + TMXLayer* layer = &tmxinfo.layers[tmxinfo.num_layers]; + if (!strcasecmp(szAttribute, "source")) + strncpy(layer->image, szValue, sizeof(layer->name)); + else if (!strcasecmp(szAttribute, "width")) + layer->width = intvalue; + else if (!strcasecmp(szAttribute, "height")) + layer->height = intvalue; + } + break; + + case FINISH_ATTRIBUTES: + break; + + case ADD_CONTENT: + break; + + case FINISH_TAG: + if (!strcasecmp(szName, "tileset") && tmxinfo.num_tilesets < TMX_MAX_TILESET - 1) + tmxinfo.num_tilesets += 1; + else if (!strcasecmp(szName, "layer") && tmxinfo.num_layers < TMX_MAX_LAYER - 1) + tmxinfo.num_layers += 1; + else if (!strcasecmp(szName, "objectgroup") && tmxinfo.num_layers < TMX_MAX_LAYER - 1) + tmxinfo.num_layers += 1; + else if (!strcasecmp(szName, "imagelayer") && tmxinfo.num_layers < TMX_MAX_LAYER - 1) + tmxinfo.num_layers += 1; + else if (!strcasecmp(szName, "object")) + tmxinfo.layers[tmxinfo.num_layers].num_objects += 1; + break; + } + return handler; +} + +static int compare(void const* d1, void const* d2) +{ + TMXTileset* t1 = (TMXTileset*)d1; + TMXTileset* t2 = (TMXTileset*)d2; + return t1->firstgid > t2->firstgid; +} + +/* loads common info about a .tmx file */ +bool TMXLoad(const char* filename, TMXInfo* info) +{ + SimpleXmlParser parser; + ssize_t size; + uint8_t *data; + TLN_Tilemap tilemap = NULL; + bool retval = false; + + /* already cached: return as is */ + if (!strcasecmp(filename, tmxinfo.filename)) + { + memcpy(info, &tmxinfo, sizeof(TMXInfo)); + return true; + } + + /* load file */ + data = (uint8_t*)LoadFile(filename, &size); + if (!data) + { + if (size == 0) + TLN_SetLastError(TLN_ERR_FILE_NOT_FOUND); + else if (size == -1) + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); + return retval; + } + + /* parse */ + memset(&tmxinfo, 0, sizeof(TMXInfo)); + parser = simpleXmlCreateParser((char*)data, (long)size); + if (parser != NULL) + { + if (simpleXmlParse(parser, handler) != 0) + { + printf("parse error on line %li:\n%s\n", + simpleXmlGetLineNumber(parser), simpleXmlGetErrorDescription(parser)); + } + else + { + strncpy(tmxinfo.filename, filename, sizeof(tmxinfo.filename)); + TLN_SetLastError(TLN_ERR_OK); + retval = true; + } + } + else + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); + + /* sort tilesets by gid */ + qsort(&tmxinfo.tilesets, tmxinfo.num_tilesets, sizeof(TMXTileset), compare); + + simpleXmlDestroyParser(parser); + free(data); + if (retval) + memcpy(info, &tmxinfo, sizeof(TMXInfo)); + return retval; +} + +/* returns index of suitable tileset acoording to gid range, -1 if not valid tileset found */ +int TMXGetSuitableTileset(TMXInfo* info, int gid, TLN_Tileset* tilesets) +{ + int c; + for (c = 0; c < info->num_tilesets; c += 1) + { + const int first = info->tilesets[c].firstgid; + if (gid >= first && gid < first + tilesets[c]->numtiles) + return c; + } + return -1; +} + +/*returns first layer of requested type */ +TMXLayer* TMXGetFirstLayer(TMXInfo* info, TLN_LayerType type) +{ + int c; + for (c = 0; c < info->num_layers; c += 1) + { + if (info->layers[c].type == type) + return &info->layers[c]; + } + return NULL; +} + +/* returns specified layer */ +TMXLayer* TMXGetLayer(TMXInfo* info, const char* name) +{ + int c; + for (c = 0; c < info->num_layers; c += 1) + { + if (!strcasecmp(info->layers[c].name, name)) + return &info->layers[c]; + } + return NULL; +} \ No newline at end of file diff --git a/src/LoadTMX.h b/src/LoadTMX.h new file mode 100644 index 00000000..7092cd51 --- /dev/null +++ b/src/LoadTMX.h @@ -0,0 +1,59 @@ +#ifndef _LOAD_TMX_H +#define _LOAD_TMX_H + +#include +#include +#include "Tileset.h" + +#define TMX_MAX_LAYER 32 +#define TMX_MAX_TILESET 32 + +typedef struct +{ + TLN_LayerType type; + char name[64]; + char image[100]; /* bitmap name for bitmap layers */ + int width; /* layer width (tiles or pixels) */ + int height; /* layer height (tiles or pixels) */ + int num_objects; /* number of objects for object layers */ + int id; + bool visible; + bool locked; + float parallaxx; + float parallaxy; + float offsetx; + float offsety; + float opacity; + uint32_t tintcolor; +} +TMXLayer; + +typedef struct +{ + char source[64]; + int firstgid; +} +TMXTileset; + +/* general TMX info */ +typedef struct +{ + char filename[100]; /* cached tmx file */ + int width; /* map width (tiles) */ + int height; /* map height (tiles) */ + int tilewidth; /* */ + int tileheight; /* */ + int num_layers; /* number of layers */ + int num_tilesets; /* number of tilesets */ + uint32_t bgcolor; /* background color */ + TMXLayer layers[TMX_MAX_LAYER]; /* array of layers */ + TMXTileset tilesets[TMX_MAX_TILESET]; /* array of tilesets */ +} +TMXInfo; + +bool TMXLoad(const char* filename, TMXInfo* info); +int TMXGetSuitableTileset(TMXInfo* info, int gid, TLN_Tileset* tilesets); +TMXLayer* TMXGetFirstLayer(TMXInfo* info, TLN_LayerType type); +TMXLayer* TMXGetLayer(TMXInfo* info, const char* name); + +#endif diff --git a/src/LoadTilemap.c b/src/LoadTilemap.c index 7cd51b99..827926d4 100644 --- a/src/LoadTilemap.c +++ b/src/LoadTilemap.c @@ -17,20 +17,11 @@ #include "zlib.h" #include "LoadFile.h" #include "Base64.h" - -#define MAX_TILESETS 8 +#include "LoadTMX.h" +#include "Tilemap.h" static int csvdecode (const char* in, int numtiles, uint32_t* data); static int decompress (uint8_t* in, int in_size, uint8_t* out, int out_size); -static uint32_t ParseHTMLColor (const char* string); - -typedef enum -{ - LS_IDLE, /* waiting for start */ - LS_ACTIVE, /* loading underway */ - LS_DONE, /* loading done, ignore everything */ -} -LoadState; /* encoding */ typedef enum @@ -53,20 +44,12 @@ compression_t; /* load manager */ struct { - char layer_name[64]; /* name of layer to load */ - LoadState state; - int cols, rows; /* map size */ + TMXLayer* layer; /* target layer */ + bool state; encoding_t encoding; /* encoding */ compression_t compression; /* compression */ - uint32_t bgcolor; /* background color */ uint32_t* data; /* map data (rows*cols) */ - struct - { - int firstgid; - TLN_Tileset tileset; - } - tilesets[MAX_TILESETS]; - int num_tileset; + uint32_t numtiles; } static loader; @@ -78,53 +61,20 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, switch (evt) { case ADD_SUBTAG: - if (!strcasecmp(szName, "map")) - return handler; - - else if (!strcasecmp(szName, "tileset") && loader.num_tileset < MAX_TILESETS - 1) - return handler; - - else if (!strcasecmp(szName, "layer") && loader.state == LS_IDLE) - return handler; - - else if (!strcasecmp(szName, "data") && loader.state == LS_ACTIVE) - return handler; - break; case ADD_ATTRIBUTE: intvalue = atoi(szValue); - if (!strcasecmp(szName, "map")) - { - if (!strcasecmp(szAttribute, "backgroundcolor")) - loader.bgcolor = ParseHTMLColor (szValue); - } - - else if (!strcasecmp(szName, "tileset")) + if (!strcasecmp(szName, "layer") && (!strcasecmp(szAttribute, "name"))) { - if (!strcasecmp(szAttribute, "firstgid")) - loader.tilesets[loader.num_tileset].firstgid = intvalue; - else if (!strcasecmp(szAttribute, "source")) - loader.tilesets[loader.num_tileset].tileset = TLN_LoadTileset(szValue); + if (!strcasecmp(szValue, loader.layer->name)) + loader.state = true; + else + loader.state = false; } - else if (!strcasecmp(szName, "layer") && loader.state != LS_DONE) - { - if (!strcasecmp(szAttribute, "name")) - { - if (loader.layer_name[0] == 0 || !strcasecmp(szValue, loader.layer_name)) - loader.state = LS_ACTIVE; - } - if (loader.state != LS_ACTIVE) - break; - else if (!strcasecmp(szAttribute, "width")) - loader.cols = intvalue; - else if (!strcasecmp(szAttribute, "height")) - loader.rows = intvalue; - } - - else if (!strcasecmp(szName, "data")) + else if (!strcasecmp(szName, "data") && loader.state == true) { if (!strcasecmp(szAttribute, "encoding")) { @@ -133,14 +83,14 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, else if (!strcasecmp(szValue, "base64")) loader.encoding = ENCODING_BASE64; else - loader.state = LS_IDLE; + loader.state = false; } else if (!strcasecmp(szAttribute, "compression")) { if (!strcasecmp(szValue, "gzip")) /* loader.compression = COMPRESSION_GZIP; */ - loader.state = LS_IDLE; + loader.state = false; else if (!strcasecmp(szValue, "zlib")) loader.compression = COMPRESSION_ZLIB; } @@ -148,20 +98,19 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, break; case FINISH_ATTRIBUTES: - if (!strcasecmp(szName, "tileset")) - loader.num_tileset += 1; break; case ADD_CONTENT: - if (!strcasecmp(szName, "data") && loader.state == LS_ACTIVE) + if (!strcasecmp(szName, "data") && loader.state == true) { - int numtiles = loader.cols * loader.rows; - int size = numtiles * sizeof(uint32_t); + int size = loader.numtiles * sizeof(uint32_t); uint32_t* data = (uint32_t*)malloc (size); + if (data == NULL) + return handler; memset (data, 0, size); if (loader.encoding == ENCODING_CSV) - csvdecode (szValue, numtiles, data); + csvdecode (szValue, loader.numtiles, data); else if (loader.encoding == ENCODING_BASE64) { @@ -177,7 +126,6 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, } } loader.data = data; - loader.state = LS_DONE; } break; @@ -187,50 +135,53 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, return handler; } -/*! - * \brief - * Loads a tilemap layer from a Tiled .tmx file - * - * \param filename - * TMX file with the tilemap - * - * \param layername - * Optional name of the layer inside the tmx file to load. NULL to load the first layer - * - * \returns - * Reference to the newly loaded tilemap or NULL if error - * - * \remarks - * A tmx map file from Tiled can contain one or more layers, each with its own name. TLN_LoadTilemap() - * doesn't load a full tmx file, only the specified layer. The associated *external* tileset (TSX file) is - * also loaded and associated to the tilemap - */ +static TLN_Tileset load_tileset(TMXInfo* info, const char* filename, int index) +{ + FileInfo fi = { 0 }; + char tsxpath[200]; + + /* composite tsx filename with relative path of parent tmx */ + TMXTileset* tmxtileset = &info->tilesets[index]; + SplitFilename(filename, &fi); + if (fi.path[0] != 0) + snprintf(tsxpath, sizeof(tsxpath), "%s/%s", fi.path, tmxtileset->source); + else + strncpy(tsxpath, tmxtileset->source, sizeof(tsxpath)); + return TLN_LoadTileset(tsxpath); +} + TLN_Tilemap TLN_LoadTilemap (const char *filename, const char *layername) { SimpleXmlParser parser; ssize_t size; uint8_t *data; TLN_Tilemap tilemap = NULL; + TMXInfo tmxinfo = { 0 }; + uint32_t c; - /* load file */ - data = (uint8_t*)LoadFile (filename, &size); - if (!data) + /* load map info */ + if (!TMXLoad(filename, &tmxinfo)) { - if (size == 0) - TLN_SetLastError (TLN_ERR_FILE_NOT_FOUND); - else if (size == -1) - TLN_SetLastError (TLN_ERR_OUT_OF_MEMORY); + TLN_SetLastError(TLN_ERR_FILE_NOT_FOUND); return NULL; } - /* parse */ - memset (&loader, 0, sizeof(loader)); + /* get target layer */ + memset(&loader, 0, sizeof(loader)); if (layername) + loader.layer = TMXGetLayer(&tmxinfo, layername); + else + loader.layer = TMXGetFirstLayer(&tmxinfo, LAYER_TILE); + if (loader.layer == NULL) { - strncpy (loader.layer_name, layername, sizeof(loader.layer_name)); - loader.layer_name[sizeof(loader.layer_name) - 1] = '\0'; - } - parser = simpleXmlCreateParser ((char*)data, (long)size); + TLN_SetLastError(TLN_ERR_FILE_NOT_FOUND); + return NULL; + } + + /* parse */ + loader.numtiles = loader.layer->width*loader.layer->height; + data = (uint8_t*)LoadFile(filename, &size); + parser = simpleXmlCreateParser((char*)data, (long)size); if (parser != NULL) { if (simpleXmlParse(parser, handler) != 0) @@ -247,58 +198,38 @@ TLN_Tilemap TLN_LoadTilemap (const char *filename, const char *layername) simpleXmlDestroyParser(parser); free (data); - if (loader.state == LS_DONE) + /* load referenced tilesets */ + TLN_Tileset tilesets[TMX_MAX_TILESET] = { 0 }; + for (c = 0; c < tmxinfo.num_tilesets; c += 1) + tilesets[c] = load_tileset(&tmxinfo, filename, c); + + if (loader.data != NULL) { - TLN_Tileset tileset = NULL; - int numtiles = loader.cols*loader.rows; - int c; - uint32_t* data = loader.data; - uint32_t firstgid = 9999999; - uint32_t lastgid = 0; - - /* find range of used tiles */ - for (c = 0; c < numtiles; c++) + /* correct with firstgid */ + Tile* tile = (Tile*)loader.data; + for (c = 0; c < loader.numtiles; c += 1, tile += 1) { - Tile* tile = (Tile*)&data[c]; if (tile->index > 0) { - if (firstgid > tile->index) - firstgid = tile->index; - if (lastgid < tile->index) - lastgid = tile->index; - } - } - - /* find suitable tileset */ - for (c = 0; c < loader.num_tileset && tileset == NULL; c++) - { - uint32_t tilefirst = loader.tilesets[c].firstgid; - uint32_t tilelast = tilefirst + TLN_GetTilesetNumTiles(loader.tilesets[c].tileset); - if (firstgid >= tilefirst && lastgid <= tilelast) - { - tileset = loader.tilesets[c].tileset; - firstgid = loader.tilesets[c].firstgid; + int suitable = TMXGetSuitableTileset(&tmxinfo, tile->index, tilesets); + if (suitable != -1 && suitable < MAX_TILESETS) + { + tile->tileset = suitable; + tile->index = tile->index - tmxinfo.tilesets[suitable].firstgid + 1; + } + else + tile->index = 0; } } - /* correct with firstgid */ - for (c = 0; c < numtiles; c++) - { - Tile* tile = (Tile*)&data[c]; - if (tile->index > 0) - tile->index = tile->index - firstgid + 1; - } - /* create */ - tilemap = TLN_CreateTilemap(loader.rows, loader.cols, (TLN_Tile)data, loader.bgcolor, tileset); - - /* delete loaded but unused tilesets */ - for (c = 0; c < loader.num_tileset; c++) - { - if (loader.tilesets[c].tileset != tileset) - TLN_DeleteTileset(loader.tilesets[c].tileset); - } + tilemap = TLN_CreateTilemap(loader.layer->height, loader.layer->width, (Tile*)loader.data, tmxinfo.bgcolor, NULL); + tilemap->id = loader.layer->id; + tilemap->visible = loader.layer->visible; + tilemap->num_tilesets = tmxinfo.num_tilesets < MAX_TILESETS ? tmxinfo.num_tilesets : MAX_TILESETS; + memcpy(tilemap->tilesets, tilesets, sizeof(TLN_Tileset)*tilemap->num_tilesets); } + return tilemap; } @@ -369,34 +300,3 @@ static int decompress (uint8_t* in, int in_size, uint8_t* out, int out_size) (void)inflateEnd(&strm); return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; } - -static uint8_t ParseHexChar (char data) -{ - if (data >= '0' && data <= '9') - return data - '0'; - else if (data >= 'A' && data <= 'F') - return data - 'A' + 10; - else if (data >= 'a' && data <= 'f') - return data - 'a' + 10; - else - return 0; -} - -static uint8_t ParseHexByte (const char* string) -{ - return (ParseHexChar(string[0]) << 4) + ParseHexChar(string[1]); -} - -static uint32_t ParseHTMLColor (const char* string) -{ - int r,g,b; - - if (string[0] != '#') - return 0; - - r = ParseHexByte (&string[1]); - g = ParseHexByte (&string[3]); - b = ParseHexByte (&string[5]); - - return (uint32_t)(0xFF000000 | (r << 16) | (g << 8) | b); -} diff --git a/src/LoadTileset.c b/src/LoadTileset.c index b4290169..18c87451 100644 --- a/src/LoadTileset.c +++ b/src/LoadTileset.c @@ -14,6 +14,7 @@ #include "Tilengine.h" #include "simplexml.h" #include "LoadFile.h" +#include "Tileset.h" /* properties */ typedef enum @@ -24,20 +25,42 @@ typedef enum } Property; +/* image context */ +typedef enum +{ + CONTEXT_NONE, + CONTEXT_TILESET, + CONTEXT_TILE, +} +ImageContext; + /* load manager */ struct { char source[64]; + int tilecount; int tilewidth; int tileheight; int spacing; int margin; - int tile_id; - Property property; - TLN_TileAttributes* attributes; + ImageContext context; + TLN_TileAttributes* attributes; /* array of attributes */ TLN_SequencePack sp; TLN_SequenceFrame frames[100]; + TLN_TileImage* images; /* array of images */ + TLN_TileImage* image; /* current image */ int frame_count; + + /* tile-specific values */ + struct + { + int id; /* id of tile */ + int type; /* type of tile */ + Property property; /* property being read */ + bool priority; /* value of priority property */ + TLN_Bitmap bitmap; /* bitmap of image-based tile */ + } + tile; } static loader; @@ -53,6 +76,11 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, { loader.frame_count = 0; } + + else if (!strcasecmp(szName, "tileset")) + loader.context = CONTEXT_TILESET; + else if (!strcasecmp(szName, "tile")) + loader.context = CONTEXT_TILE; break; case ADD_ATTRIBUTE: @@ -68,12 +96,7 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, else if (!strcasecmp(szAttribute, "spacing")) loader.spacing = atoi(szValue); else if (!strcasecmp(szAttribute, "tilecount")) - { - const int tilecount = atoi(szValue); - const int size_attribs = tilecount * sizeof(TLN_TileAttributes); - loader.attributes = (TLN_TileAttributes*)malloc(size_attribs); - memset (loader.attributes, 0, size_attribs); - } + loader.tilecount = atoi(szValue); } /* */ @@ -81,16 +104,25 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, { if (!strcasecmp(szAttribute, "source")) { - strncpy (loader.source, szValue, sizeof(loader.source)); + strncpy(loader.source, szValue, sizeof(loader.source)); loader.source[sizeof(loader.source) - 1] = '\0'; + + /* image for each image-based tileset */ + if (loader.context == CONTEXT_TILE) + { + loader.tile.bitmap = TLN_LoadBitmap(loader.source); + loader.source[0] = 0; + } } } - /* */ + /* */ else if (!strcasecmp(szName, "tile")) { if (!strcasecmp(szAttribute, "id")) - loader.tile_id = atoi(szValue); + loader.tile.id = atoi(szValue); + else if (!strcasecmp(szAttribute, "type")) + loader.tile.type = atoi(szValue); } /* */ @@ -99,22 +131,22 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, if (!strcasecmp(szAttribute, "name")) { if (!strcasecmp(szValue, "type")) - loader.property = PROPERTY_TYPE; + loader.tile.property = PROPERTY_TYPE; else if (!strcasecmp(szValue, "priority")) - loader.property = PROPERTY_PRIORITY; + loader.tile.property = PROPERTY_PRIORITY; else - loader.property = PROPERTY_NONE; + loader.tile.property = PROPERTY_NONE; } - else if (!strcasecmp(szAttribute, "value")) + else if (!strcasecmp(szAttribute, "value") && loader.tilecount != 0) { - if (loader.property == PROPERTY_TYPE) - loader.attributes[loader.tile_id].type = atoi(szValue); - else if (loader.property == PROPERTY_PRIORITY) + if (loader.tile.property == PROPERTY_TYPE) + loader.attributes[loader.tile.id].type = atoi(szValue); + else if (loader.tile.property == PROPERTY_PRIORITY) { if (!strcasecmp(szValue, "true")) - loader.attributes[loader.tile_id].priority = true; + loader.attributes[loader.tile.id].priority = true; else - loader.attributes[loader.tile_id].priority = true; + loader.attributes[loader.tile.id].priority = false; } } } @@ -123,13 +155,19 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, else if (!strcasecmp(szName, "frame")) { if (!strcasecmp(szAttribute, "tileid")) - loader.frames[loader.frame_count].index = atoi(szValue); + loader.frames[loader.frame_count].index = atoi(szValue) + 1; else if (!strcasecmp(szAttribute, "duration")) loader.frames[loader.frame_count].delay = atoi(szValue)*60/1000; } break; case FINISH_ATTRIBUTES: + if (!strcasecmp(szName, "tileset") && loader.tilecount != 0) + { + loader.attributes = (TLN_TileAttributes*)calloc(loader.tilecount, sizeof(TLN_TileAttributes)); + loader.images = (TLN_TileImage*)calloc(loader.tilecount, sizeof(TLN_TileImage)); + loader.image = loader.images; + } break; case ADD_CONTENT: @@ -138,12 +176,28 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, case FINISH_TAG: if (!strcasecmp(szName, "frame")) loader.frame_count++; + else if (!strcasecmp(szName, "tile") && loader.tilecount != 0) + { + if (loader.context == CONTEXT_TILESET) + { + TLN_TileAttributes* attribute = &loader.attributes[loader.tile.id]; + attribute->priority = loader.tile.priority; + attribute->type = loader.tile.type; + } + else if (loader.context == CONTEXT_TILE) + { + loader.image->bitmap = loader.tile.bitmap; + loader.image->id = loader.tile.id; + loader.image->type = loader.tile.type; + loader.image += 1; + } + } else if (!strcasecmp(szName, "animation")) { char name[16]; TLN_Sequence sequence; - sprintf (name, "%d", loader.tile_id); - sequence = TLN_CreateSequence (name, loader.tile_id, loader.frame_count, loader.frames); + sprintf (name, "%d", loader.tile.id); + sequence = TLN_CreateSequence (name, loader.tile.id + 1, loader.frame_count, loader.frames); if (loader.sp == NULL) loader.sp = TLN_CreateSequencePack (); TLN_AddSequenceToPack (loader.sp, sequence); @@ -153,30 +207,48 @@ static void* handler (SimpleXmlParser parser, SimpleXmlEvent evt, return handler; } -/*! - * \brief - * Loads a tileset from a Tiled .tsx file - * - * \param filename - * TSX file to load - * - * \returns - * Reference to the newly loaded tileset or NULL if error - * - * \remarks - * An associated palette is also created, it can be obtained calling TLN_GetTilesetPalette() - */ +/* cache section: keeps already loaded tilesets so it doesnt spawn multiple instances of the same */ +#define CACHE_SIZE 16 +static int cache_entries = 0; +struct +{ + char name[200]; + TLN_Tileset tileset; +} +static cache[16]; + +static TLN_Tileset search_cache(const char* name) +{ + int c; + for (c = 0; c < cache_entries; c += 1) + { + if (!strcmp(cache[c].name, name)) + return cache[c].tileset; + } + return NULL; +} + +static void add_to_cache(const char* name, TLN_Tileset tileset) +{ + if (cache_entries < CACHE_SIZE - 1) + { + strncpy(cache[cache_entries].name, name, sizeof(cache[0].name)); + cache[cache_entries].tileset = tileset; + cache_entries += 1; + } +} + TLN_Tileset TLN_LoadTileset (const char* filename) { SimpleXmlParser parser; - ssize_t size; - uint8_t *data; - TLN_Tileset tileset; - TLN_Bitmap bitmap; - int htiles, vtiles; - int x,y,dx,dy; - int id; - int pitch; + ssize_t size = 0; + uint8_t *data = NULL; + TLN_Tileset tileset = NULL; + + /* find in cache */ + tileset = search_cache(filename); + if (tileset) + return tileset; /* load file */ data = (uint8_t*)LoadFile (filename, &size); @@ -210,43 +282,76 @@ TLN_Tileset TLN_LoadTileset (const char* filename) simpleXmlDestroyParser(parser); free(data); - /* check filename */ - if (!loader.source[0]) + /* tile based tileset (classic) */ + if (loader.source[0] != 0) { - TLN_SetLastError (TLN_ERR_WRONG_FORMAT); - return NULL; - } - - /* load picture */ - bitmap = TLN_LoadBitmap (loader.source); - if (!bitmap) - return NULL; + TLN_Bitmap bitmap; + int htiles, vtiles; + int x, y, dx, dy; + int id; + int pitch; + FileInfo fi = { 0 }; + char imagepath[200]; - /* create tileset */ - dx = loader.tilewidth + loader.spacing; - dy = loader.tileheight + loader.spacing; - htiles = (TLN_GetBitmapWidth(bitmap) - loader.margin*2 + loader.spacing) / dx; - vtiles = (TLN_GetBitmapHeight(bitmap) - loader.margin*2 + loader.spacing) / dy; - tileset = TLN_CreateTileset (htiles*vtiles, loader.tilewidth, loader.tileheight, TLN_ClonePalette(TLN_GetBitmapPalette(bitmap)), loader.sp, loader.attributes); - if (tileset == NULL) - return NULL; + /* composite bitmap filename with relative path of parent tsx */ + SplitFilename(filename, &fi); + if (fi.path[0] != 0) + snprintf(imagepath, sizeof(imagepath), "%s/%s", fi.path, loader.source); + else + strncpy(imagepath, loader.source, sizeof(imagepath)); - pitch = TLN_GetBitmapPitch (bitmap); + bitmap = TLN_LoadBitmap(imagepath); + if (!bitmap) + { + TLN_SetLastError(TLN_ERR_FILE_NOT_FOUND); + return NULL; + } + + /* create */ + dx = loader.tilewidth + loader.spacing; + dy = loader.tileheight + loader.spacing; + htiles = (TLN_GetBitmapWidth(bitmap) - loader.margin * 2 + loader.spacing) / dx; + vtiles = (TLN_GetBitmapHeight(bitmap) - loader.margin * 2 + loader.spacing) / dy; + int tilecount = loader.tilecount != 0 ? loader.tilecount : htiles * vtiles; + tileset = TLN_CreateTileset(tilecount, loader.tilewidth, loader.tileheight, TLN_ClonePalette(TLN_GetBitmapPalette(bitmap)), loader.sp, loader.attributes); + if (tileset == NULL) + { + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); + return NULL; + } + + /* load tile data */ + pitch = TLN_GetBitmapPitch(bitmap); + for (id = 0, y = 0; y < vtiles; y++) + { + for (x = 0; x < htiles; x++, id++) + { + uint8_t *srcptr = TLN_GetBitmapPtr(bitmap, loader.margin + x * dx, loader.margin + y * dy); + if (id < tilecount) + TLN_SetTilesetPixels(tileset, id, srcptr, pitch); + } + } + tileset->tiles_per_row = htiles; + TLN_DeleteBitmap(bitmap); + } - /* load tiles */ - for (id=1, y=0; y> FIXED_BITS) +#define fix2float(f) (float)(f)/(1 << FIXED_BITS) + #ifdef __cplusplus -extern "C"{ +extern "C" { #endif -void Matrix3SetIdentity (Matrix3*); -void Matrix3Set (Matrix3*, math2d_t*); -void Matrix3Add (Matrix3*, Matrix3*); -void Matrix3Multiply (Matrix3*, Matrix3*); -void Matrix3SetRotation (Matrix3 *matrix, math2d_t angle); -void Matrix3SetTranslation (Matrix3 *matrix, math2d_t x, math2d_t y); -void Matrix3SetScale (Matrix3 *matrix, math2d_t sx, math2d_t sy); + void Matrix3SetIdentity(Matrix3*); + void Matrix3Set(Matrix3*, math2d_t*); + void Matrix3Add(Matrix3*, Matrix3*); + void Matrix3Multiply(Matrix3*, Matrix3*); + void Matrix3SetRotation(Matrix3 *matrix, math2d_t angle); + void Matrix3SetTranslation(Matrix3 *matrix, math2d_t x, math2d_t y); + void Matrix3SetScale(Matrix3 *matrix, math2d_t sx, math2d_t sy); -void Point2DSet (Point2D*, math2d_t,math2d_t); -void Point2DAdd (Point2D*, Point2D*); -void Point2DMultiply (Point2D*, Matrix3*); + void Point2DSet(Point2D*, math2d_t, math2d_t); + void Point2DAdd(Point2D*, Point2D*); + void Point2DMultiply(Point2D*, Matrix3*); #if defined _MSC_VER && _MSC_VER < 1900 -int roundf(float fvalue); + int roundf(float fvalue); #endif #ifdef __cplusplus diff --git a/src/Object.c b/src/Object.c index 5eae352a..2ebef28d 100644 --- a/src/Object.c +++ b/src/Object.c @@ -26,6 +26,7 @@ static const char* object_types[] = "bitmap", "sequence", "sequence pack" + "object list", }; static const TLN_Error object_errors[] = @@ -38,6 +39,7 @@ static const TLN_Error object_errors[] = TLN_ERR_REF_BITMAP, TLN_ERR_REF_SEQUENCE, TLN_ERR_REF_SEQPACK, + TLN_ERR_REF_LIST, }; /* crea objecto */ diff --git a/src/ObjectList.c b/src/ObjectList.c index 5fa75ad7..b29ba1da 100644 --- a/src/ObjectList.c +++ b/src/ObjectList.c @@ -15,30 +15,30 @@ #include "Sprite.h" #include "simplexml.h" #include "LoadFile.h" +#include "LoadTMX.h" +/* properties */ typedef enum { - LS_IDLE, /* waiting for start */ - LS_ACTIVE, /* loading underway */ - LS_DONE, /* loading done, ignore everything */ + PROPERTY_NONE, + PROPERTY_TYPE, + PROPERTY_PRIORITY, } -LoadState; +Property; /* load manager */ struct { - char layer_name[64]; /* name of layer to load */ - LoadState state; - int firstgid; - int width; - int height; - int tilewidth; - int tileheight; + TMXLayer* layer; + bool state; TLN_ObjectList objects; TLN_Object object; + Property property; /* current property */ } static loader; +static bool CloneObjectToList(TLN_ObjectList list, TLN_Object* data); + /* XML parser callback */ static void* handler(SimpleXmlParser parser, SimpleXmlEvent evt, const char* szName, const char* szAttribute, const char* szValue) @@ -47,46 +47,36 @@ static void* handler(SimpleXmlParser parser, SimpleXmlEvent evt, switch (evt) { case ADD_SUBTAG: - if (!strcasecmp(szName, "map")) - return handler; - - else if (loader.state == LS_IDLE && !strcasecmp(szName, "objectgroup")) - return handler; - - /* create new object and add to list */ - else if (loader.state == LS_ACTIVE && !strcasecmp(szName, "object")) + if (!strcasecmp(szName, "object")) { - return handler; + memset(&loader.object, 0, sizeof(struct _Object)); + loader.object.visible = true; } - return NULL; + break; case ADD_ATTRIBUTE: intvalue = atoi(szValue); - if (!strcasecmp(szName, "map")) + if (!strcasecmp(szName, "objectgroup") && (!strcasecmp(szAttribute, "name"))) { - if (!strcasecmp(szAttribute, "width")) - loader.width = intvalue; - else if (!strcasecmp(szAttribute, "height")) - loader.height = intvalue; - else if (!strcasecmp(szAttribute, "tilewidth")) - loader.tilewidth = intvalue; - else if (!strcasecmp(szAttribute, "tileheight")) - loader.tileheight = intvalue; - } - - else if (!strcasecmp(szName, "objectgroup") && !strcasecmp(szAttribute, "name")) - { - if (loader.layer_name[0] == 0 || !strcasecmp(szValue, loader.layer_name)) - loader.state = LS_ACTIVE; + if (!strcasecmp(szValue, loader.layer->name)) + loader.state = true; + else + loader.state = false; } else if (!strcasecmp(szName, "object")) { if (!strcasecmp(szAttribute, "id")) - loader.object.gid = intvalue; + loader.object.id = intvalue; else if (!strcasecmp(szAttribute, "gid")) - loader.object.gid = intvalue - loader.firstgid; + { + Tile tile; + tile.value = strtoul(szValue, NULL, 0); + loader.object.has_gid = true; + loader.object.flags = tile.flags; + loader.object.gid = tile.index; + } else if (!strcasecmp(szAttribute, "x")) loader.object.x = intvalue; else if (!strcasecmp(szAttribute, "y")) @@ -95,27 +85,62 @@ static void* handler(SimpleXmlParser parser, SimpleXmlEvent evt, loader.object.width = intvalue; else if (!strcasecmp(szAttribute, "height")) loader.object.height = intvalue; + else if (!strcasecmp(szAttribute, "type")) + loader.object.type = intvalue; + else if (!strcasecmp(szAttribute, "visible")) + loader.object.visible = (bool)intvalue; + else if (!strcasecmp(szAttribute, "name")) + strncpy(loader.object.name, szValue, sizeof(loader.object.name)); + } + + /* */ + else if (!strcasecmp(szName, "property")) + { + if (!strcasecmp(szAttribute, "name")) + { + if (!strcasecmp(szValue, "priority")) + loader.property = PROPERTY_PRIORITY; + else + loader.property = PROPERTY_NONE; + } + else if (!strcasecmp(szAttribute, "value")) + { + if (loader.property == PROPERTY_PRIORITY) + { + if (!strcasecmp(szValue, "true")) + loader.object.flags += FLAG_PRIORITY; + } + } } break; case FINISH_ATTRIBUTES: - if (!strcasecmp(szName, "objectgroup")) + if (loader.state == true) { - /* create */ - loader.width *= loader.tilewidth; - loader.height *= loader.tileheight; - loader.objects = TLN_CreateObjectList(); + if (!strcasecmp(szName, "objectgroup")) + { + loader.objects = TLN_CreateObjectList(); + loader.objects->id = loader.layer->id; + loader.objects->visible = loader.layer->visible; + } } - else if (!strcasecmp(szName, "object")) - TLN_AddObjectToList(loader.objects, &loader.object); break; case ADD_CONTENT: break; case FINISH_TAG: - if (!strcasecmp(szName, "objectgroup")) - loader.state = LS_DONE; + if (loader.state == true) + { + if (!strcasecmp(szName, "objectgroup")) + loader.state = false; + else if (!strcasecmp(szName, "object")) + { + if (loader.object.has_gid) + loader.object.y -= loader.object.height; + CloneObjectToList(loader.objects, &loader.object); + } + } break; } return handler; @@ -126,12 +151,6 @@ static bool intersetcs(rect_t* rect1, rect_t* rect2) return !(rect1->x2 < rect2->x1 || rect1->x1 > rect2->x2 || rect1->y2 < rect2->y1 || rect1->y1 > rect2->y2); } -/*! - * \brief Creates a TLN_ObjectList - * The list is created empty, it must be populated with TLN_AddSpriteToList() - * and assigned to a layer with TLN_SetLayerObjects() - * \return Reference to new object or NULL if error - */ TLN_ObjectList TLN_CreateObjectList(void) { TLN_ObjectList list = NULL; @@ -142,6 +161,7 @@ TLN_ObjectList TLN_CreateObjectList(void) if (!list) return NULL; + list->visible = true; TLN_SetLastError(TLN_ERR_OK); return list; } @@ -155,6 +175,7 @@ static void add_to_list(TLN_ObjectList list, struct _Object* object) list->last->next = object; list->last = object; list->num_items += 1; + object->next = NULL; } /*! @@ -163,7 +184,7 @@ static void add_to_list(TLN_ObjectList list, struct _Object* object) * \param data Pointer to a user-provided TLN_Object. This object is internally copied to the list, so it's safe to discard the user-provided one after addition. * \return true if success or false if error */ -bool TLN_AddObjectToList(TLN_ObjectList list, TLN_Object* data) +static bool CloneObjectToList(TLN_ObjectList list, TLN_Object* data) { struct _Object* object; @@ -171,79 +192,60 @@ bool TLN_AddObjectToList(TLN_ObjectList list, TLN_Object* data) return false; object = (struct _Object*)calloc(1, sizeof(struct _Object)); - memcpy(&object->data, data, sizeof(TLN_Object)); + if (object == NULL) + return false; + + memcpy(object, data, sizeof(struct _Object)); add_to_list(list, object); return true; } -/*! - * \brief Adds a named sprite from a TLN_Spriteset object to a given TLN_ObjectList - * - * \param list Reference to TLN_ObjectList - * \param spriteset Reference to the TLN_Spriteset that contains the sprite to add - * \param name Name of the sprite inside the spriteset to add to the list - * \param id User-provided unique identifier to identify the object later when querying the list - * \param x Layer-space horizontal coordinate of the top-left corner - * \param y Layer-space bertical coordinate of the top-left corner - * \return true if success or false if error - */ -bool TLN_AddSpriteToList(TLN_ObjectList list, TLN_Spriteset spriteset, const char* name, int id, int x, int y) +bool TLN_AddTileObjectToList(TLN_ObjectList list, uint16_t id, uint16_t gid, uint16_t flags, int x, int y) { struct _Object* object; - int index; - if (!CheckBaseObject(list, OT_OBJECTLIST) || !CheckBaseObject(spriteset, OT_SPRITESET)) + if (!CheckBaseObject(list, OT_OBJECTLIST)) return false; - index = TLN_FindSpritesetSprite(spriteset, name); - if (index == -1) + object = (struct _Object*)calloc(1, sizeof(struct _Object)); + if (object == NULL) return false; - object = (struct _Object*)calloc(1, sizeof(struct _Object)); - object->sprite = &spriteset->data[index]; - object->data.x = x; - object->data.y = y; - object->data.width = object->sprite->w; - object->data.height = object->sprite->h; - object->data.gid = index; - object->data.id = id; + object->gid = gid; + object->x = x; + object->y = y; add_to_list(list, object); return true; } -/*! - * \brief Loads an object list from a Tiled object layer - * - * \param filename Name of the .tmx file containing the list - * \param layername Name of the layer to load - * \param firstgid First graphic id (gid) of the tileset used by layer, must match Tiled value! - * \return Reference to the loaded object or NULL if error - */ -TLN_ObjectList TLN_LoadObjectList(const char* filename, const char* layername, int firstgid) +TLN_ObjectList TLN_LoadObjectList(const char* filename, const char* layername) { SimpleXmlParser parser; ssize_t size; uint8_t *data; + TMXInfo tmxinfo = { 0 }; - /* load file */ - data = (uint8_t*)LoadFile(filename, &size); - if (!data) + /* load map info */ + if (!TMXLoad(filename, &tmxinfo)) { - if (size == 0) - TLN_SetLastError(TLN_ERR_FILE_NOT_FOUND); - else if (size == -1) - TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); + TLN_SetLastError(TLN_ERR_FILE_NOT_FOUND); return NULL; } - - /* parse */ + + /* get target layer */ memset(&loader, 0, sizeof(loader)); - loader.firstgid = firstgid; if (layername) + loader.layer = TMXGetLayer(&tmxinfo, layername); + else + loader.layer = TMXGetFirstLayer(&tmxinfo, LAYER_OBJECT); + if (loader.layer == NULL) { - strncpy(loader.layer_name, layername, sizeof(loader.layer_name)); - loader.layer_name[sizeof(loader.layer_name) - 1] = '\0'; + TLN_SetLastError(TLN_ERR_FILE_NOT_FOUND); + return NULL; } + + /* parse */ + data = (uint8_t*)LoadFile(filename, &size); parser = simpleXmlCreateParser((char*)data, (long)size); if (parser != NULL) { @@ -261,15 +263,56 @@ TLN_ObjectList TLN_LoadObjectList(const char* filename, const char* layername, i simpleXmlDestroyParser(parser); free(data); + + if (loader.objects != NULL) + { + TMXTileset* tmxtileset; + struct _Object* item; + int gid = 0; + int c; + + /* find suitable tileset */ + item = loader.objects->list; + while (item != NULL && gid == 0) + { + if (item->gid > 0) + gid = item->gid; + item = item->next; + } + + /* load referenced tilesets */ + TLN_Tileset tilesets[TMX_MAX_TILESET] = { 0 }; + for (c = 0; c < tmxinfo.num_tilesets; c += 1) + tilesets[c] = TLN_LoadTileset(tmxinfo.tilesets[c].source); + + int suitable = TMXGetSuitableTileset(&tmxinfo, gid, tilesets); + tmxtileset = &tmxinfo.tilesets[suitable]; + + /* correct with firstgid */ + item = loader.objects->list; + while (item != NULL) + { + if (item->gid > 0) + item->gid = item->gid - tmxtileset->firstgid; + item = item->next; + } + + /* delete unused tilesets */ + for (c = 0; c < tmxinfo.num_tilesets; c += 1) + { + if (c != suitable) + TLN_DeleteTileset(tilesets[c]); + } + + TLN_Tileset tileset = tilesets[suitable]; + loader.objects->tileset = tileset; + loader.objects->width = tmxinfo.width*tmxinfo.tilewidth; + loader.objects->height = tmxinfo.height*tmxinfo.tileheight; + } return loader.objects; } -/*! - * \brief Creates a duplicate of a given TLN_ObjectList object - * \param src Reference to the source object to clone - * \return A reference to the newly cloned object list, or NULL if error - */ TLN_ObjectList TLN_CloneObjectList(TLN_ObjectList src) { TLN_ObjectList list; @@ -282,67 +325,76 @@ TLN_ObjectList TLN_CloneObjectList(TLN_ObjectList src) object = src->list; while (object != NULL) { - TLN_AddObjectToList(list, &object->data); + CloneObjectToList(list, object); object = object->next; } + list->iterator = NULL; return list; } -/*! - * \brief Returns a list of the objects contained inside the given rectangle - * - * \param list Reference to the TLN_ObjectList to query - * \param x Horizontal position of top-left corner - * \param y Vertical position of top-left corner - * \param width Rectangle width - * \param height Rectangle - * \param array_size Size of user-provided pointer array to receive objects - * \param objects User-provided array of TLN_Object pointers - * \return Number of objects found. It can be greater of array_size - */ -int TLN_GetObjectsInReigion(TLN_ObjectList list, int x, int y, int width, int height, int array_size, TLN_Object* objects[]) +int TLN_GetListNumObjects(TLN_ObjectList list) { - struct _Object* object; - int current = 0; - rect_t rect1; + if (CheckBaseObject(list, OT_OBJECTLIST)) + { + TLN_SetLastError(TLN_ERR_OK); + return list->num_items; + } + else + { + TLN_SetLastError(TLN_ERR_REF_LIST); + return 0; + } +} + +bool TLN_GetListObject(TLN_ObjectList list, TLN_ObjectInfo* info) +{ + struct _Object* item; if (!CheckBaseObject(list, OT_OBJECTLIST)) + { + TLN_SetLastError(TLN_ERR_REF_LIST); return false; + } - MakeRect(&rect1, x, y, width, height); - object = list->list; - while (object != NULL) + /* start iterator */ + if (info != NULL) { - rect_t rect2; - TLN_Object* data = &object->data; - MakeRect(&rect2, data->x, data->y, data->width, data->height); - if (intersetcs(&rect1, &rect2)) - { - if (current < array_size) - objects[current] = data; - current += 1; - } - object = object->next; + list->iterator = list->list; + list->info = info; } - return current; + + if (list->iterator == NULL) + return false; + + /* copy info */ + item = list->iterator; + info = list->info; + info->id = item->id; + info->gid = item->gid; + info->flags = item->flags; + info->x = item->x; + info->y = item->y; + info->width = item->width; + info->height = item->height; + info->type = item->type; + info->visible = item->visible; + if (item->name[0]) + strncpy(info->name, item->name, sizeof(info->name)); + + /* advance */ + list->iterator = item->next; + return true; } bool IsObjectInLine(struct _Object* object, int x1, int x2, int y) { rect_t rect; - TLN_Object* data = &object->data; - MakeRect(&rect, data->x, data->y, data->width, data->height); + MakeRect(&rect, object->x, object->y, object->width, object->height); if (y >= rect.y1 && y < rect.y2 && !(x1 > rect.x2 || x2 < rect.x1)) return true; else return false; } -/*! - * \brief Deletes object list - * - * \param list Reference to list to delete -* \return true if success or false if error - */ bool TLN_DeleteObjectList(TLN_ObjectList list) { struct _Object* object; diff --git a/src/ObjectList.h b/src/ObjectList.h index b3599eae..b82badd4 100644 --- a/src/ObjectList.h +++ b/src/ObjectList.h @@ -13,22 +13,40 @@ #include "Tilengine.h" #include "Object.h" -#include "Hash.h" +#include "crc32.h" #include "Spriteset.h" -struct _Object +typedef struct _Object { - TLN_Object data; - SpriteEntry* sprite; + uint16_t id; + uint16_t gid; + uint16_t flags; + uint8_t type; + char name[64]; + int x; + int y; + int width; + int height; + TLN_Bitmap bitmap; /* computed after calling TLN_SetLayerObjects() */ + bool has_gid; + bool visible; struct _Object* next; -}; +} +TLN_Object; struct ObjectList { DEFINE_OBJECT; - int num_items; + int num_items; /* items in list */ + int width; /* map width, pixels */ + int height; /* map height, pixels */ + int id; /* id property */ + bool visible; /* visible property */ + TLN_Tileset tileset; /* attached tileset, if any */ struct _Object* list; struct _Object* last; + struct _Object* iterator; + TLN_ObjectInfo* info; }; extern bool IsObjectInLine(struct _Object* object, int x1, int x2, int y); diff --git a/src/Palette.c b/src/Palette.c index 3d30ae71..6003f412 100644 --- a/src/Palette.c +++ b/src/Palette.c @@ -15,20 +15,10 @@ #include "Palette.h" #include "Tables.h" -/*! - * \brief - * Creates a new color table - * - * \param entries - * Number of color entries (typically 256) - * - * \returns - * Reference to the created palette or NULL if error - */ TLN_Palette TLN_CreatePalette (int entries) { TLN_Palette palette; - int size = sizeof(struct Palette) + (4*entries); + int size = sizeof(struct Palette) + 1024; // always alloc 256 colors, to avoid crash when tileset uses more colors than the palette palette = (TLN_Palette)CreateBaseObject(OT_PALETTE, size); if (palette) @@ -41,19 +31,6 @@ TLN_Palette TLN_CreatePalette (int entries) return NULL; } -/*! - * \brief - * Creates a duplicate of the specified palette - * - * \param src - * Reference to the palette to clone - * - * \returns - * A reference to the newly cloned palette, or NULL if error - * - * \see - * TLN_CreatePalette() - */ TLN_Palette TLN_ClonePalette (TLN_Palette src) { TLN_Palette palette; @@ -71,16 +48,6 @@ TLN_Palette TLN_ClonePalette (TLN_Palette src) return NULL; } -/*! - * \brief - * Deletes the specified palette and frees memory - * - * \param palette - * Reference to the palette to delete - * - * \remarks - * Don't delete a palette currently attached to a layer or sprite! - */ bool TLN_DeletePalette (TLN_Palette palette) { if (CheckBaseObject (palette, OT_PALETTE)) @@ -93,31 +60,20 @@ bool TLN_DeletePalette (TLN_Palette palette) return false; } -/*! - * \brief - * Sets the RGB color value of a palette entry - * - * \param palette - * Reference to the palette to modify - * - * \param index - * Index of the palette entry to modify (0-255) - * - * \param r - * Red component of the color (0-255) - * - * \param g - * Green component of the color (0-255) - * - * \param b - * Blue component of the color (0-255) - */ bool TLN_SetPaletteColor (TLN_Palette palette, int index, uint8_t r, uint8_t g, uint8_t b) { - if (CheckBaseObject (palette, OT_PALETTE)) + if (CheckBaseObject (palette, OT_PALETTE) && index < palette->entries) { - uint32_t* data = (uint32_t*)GetPaletteData (palette, index); - *data = PackRGB32(r,g,b); + Color* color = (Color*)GetPaletteData (palette, index); + if (index == 0) + color->value = 0; + else + { + color->r = r; + color->g = g; + color->b = b; + color->a = 255; + } TLN_SetLastError (TLN_ERR_OK); return true; } @@ -125,19 +81,6 @@ bool TLN_SetPaletteColor (TLN_Palette palette, int index, uint8_t r, uint8_t g, return false; } -/*! - * \brief - * Returns the color value of a palette entry - * - * \param palette - * Reference to the palette to get the color - * - * \param index - * Index of the palette entry to obtain (0-255) - * - * \returns - * 32-bit integer with the packed color in internal pixel format RGBA - */ uint8_t* TLN_GetPaletteData (TLN_Palette palette, int index) { if (!CheckBaseObject (palette, OT_PALETTE)) @@ -150,31 +93,15 @@ uint8_t* TLN_GetPaletteData (TLN_Palette palette, int index) else { TLN_SetLastError (TLN_ERR_OK); - return GetPaletteData (palette, index); + return (uint8_t*)GetPaletteData (palette, index); } } -/*! - * \brief - * Mixes two palettes to create a third one - * - * \param src1 - * Reference to the first source palette - * - * \param src2 - * Reference to the second source palette - * - * \param dst - * Reference to the target palette - * - * \param factor - * Integer with mixing factor. 0=100% src1, 255=100% src2, 128=50%/50% - */ bool TLN_MixPalettes (TLN_Palette src1, TLN_Palette src2, TLN_Palette dst, uint8_t factor) { int c; const uint8_t invfactor = 255 - factor; - const uint8_t* mod_table = engine->mod_table; + const uint8_t* blend_table = engine->blend_table; uint8_t* src1ptr; uint8_t* src2ptr; uint8_t* dstptr; @@ -186,7 +113,7 @@ bool TLN_MixPalettes (TLN_Palette src1, TLN_Palette src2, TLN_Palette dst, uint8 src1ptr = TLN_GetPaletteData (src1, 0); src2ptr = TLN_GetPaletteData (src2, 0); dstptr = TLN_GetPaletteData (dst, 0); - mod_table = SelectBlendTable (BLEND_MOD); + blend_table = SelectBlendTable (BLEND_MOD); if (src1->entries > src2->entries) count = src1->entries; @@ -195,9 +122,9 @@ bool TLN_MixPalettes (TLN_Palette src1, TLN_Palette src2, TLN_Palette dst, uint8 for (c=0; centries; +} diff --git a/src/Palette.h b/src/Palette.h index ab2ff16c..8a8f24f9 100644 --- a/src/Palette.h +++ b/src/Palette.h @@ -13,16 +13,28 @@ #include "Object.h" -/* entrada de paleta */ +/* color definition */ +typedef union +{ + struct + { + uint8_t b, g, r, a; + }; + uint32_t value; +} +Color; + +/* palette object */ struct Palette { DEFINE_OBJECT; - int entries; - uint8_t data[0]; + int entries; /* number of colors */ + uint32_t data[0]; /* variable size Color array */ }; +/* returns pointer to specified index color definition */ #define GetPaletteData(palette,index) \ - &palette->data[(index) << 2] + &palette->data[index] #define PackRGB32(r,g,b) \ (uint32_t)(0xFF000000 | (r << 16) | (g << 8) | b) diff --git a/src/ResPack.h b/src/ResPack.h index fe6c082a..a100a718 100644 --- a/src/ResPack.h +++ b/src/ResPack.h @@ -12,24 +12,39 @@ #define _RESPACK_H #include -#include #include +#include typedef struct _ResPack* ResPack; typedef struct _ResAsset* ResAsset; #ifdef __cplusplus -extern"C"{ +extern"C" { #endif -ResPack ResPack_Open(const char* filename, const char* key); -void ResPack_Close(ResPack rp); -void* ResPack_LoadAsset(ResPack rp, const char* filename, uint32_t* size); -ResAsset ResPack_OpenAsset(ResPack rp, const char* filename); -FILE* ResPack_GetAssetFile(ResAsset asset); -uint32_t ResPack_GetAssetSize(ResAsset asset); -void ResPack_CloseAsset(ResAsset asset); -int ResPack_Build(const char* filelist, const char* aes_key); + /* opens a resource pack, returns handler. passphrase optional (set for AES-128 cypher) */ + ResPack ResPack_Open(const char* filename, const char* passphrase); + + /* closes an opened resource pack */ + void ResPack_Close(ResPack rp); + + /* loads contents of asset to memory, returns buffer and actual size */ + void* ResPack_LoadAsset(ResPack rp, const char* filename, uint32_t* size); + + /* creates a temporal file and opens it, returns asset handler */ + ResAsset ResPack_OpenAsset(ResPack rp, const char* filename); + + /* returns file handler of an opened asset with ResPack_OpenAsset() */ + FILE* ResPack_GetAssetFile(ResAsset asset); + + /* returns actual size of an opened asset with ResPack_OpenAsset() */ + uint32_t ResPack_GetAssetSize(ResAsset asset); + + /* closes opened asset, deletes temporal file */ + void ResPack_CloseAsset(ResAsset asset); + + /* builds a resource pack from "filelist" to "filelist.dat", returns number of assets */ + int ResPack_Build(const char* filelist, const char* passphrase); #ifdef __cplusplus } diff --git a/src/ResourcePacker.c b/src/ResourcePacker.c index 08355261..bf3423da 100644 --- a/src/ResourcePacker.c +++ b/src/ResourcePacker.c @@ -8,21 +8,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * */ -#pragma warning(disable : 4200) - #include #include #include #include #include "aes.h" -#include "Hash.h" +#include "crc32.h" +#include "md5.h" #include "ResPack.h" #define KEY_SIZE 128 #define FILE_ID "ResPack" static uint8_t iv[AES_BLOCK_SIZE] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f }; -static uint32_t key[60] = { 0 }; /* asset descriptor register */ typedef struct @@ -75,13 +73,13 @@ static void normalize_path(char* path) } /* calculates hash of file path */ -static uint32_t path2hash(const char* filename) +static uint32_t path2_crc32(const char* filename) { char path[200]; strncpy(path, filename, sizeof(path)); path[sizeof(path) - 1] = 0; normalize_path(path); - return hash(0, path, strlen(path)); + return _crc32(0, path, strlen(path)); } /* finds given entry inside a resource pack */ @@ -96,7 +94,7 @@ static ResEntry* find_entry(ResPack rp, const char* filename) return NULL; /* find entry */ - id = path2hash(filename); + id = path2_crc32(filename); for (c = 0; c < rp->num_entries; c++) { if (rp->entries[c].id == id) @@ -109,37 +107,55 @@ static ResEntry* find_entry(ResPack rp, const char* filename) static void* load_asset(ResPack rp, ResEntry* entry) { uint32_t crc; - void* buffer = malloc(entry->data_size); + uint8_t* buffer = (uint8_t*)malloc(entry->data_size + 1); if (buffer == NULL) return NULL; fseek(rp->pf, entry->offset, SEEK_SET); if (rp->encrypted == true) { - uint8_t* cypher = (uint8_t*)malloc(entry->pack_size); - uint8_t* content = (uint8_t*)malloc(entry->pack_size); - fread(cypher, entry->pack_size, 1, rp->pf); - aes_decrypt_cbc(cypher, entry->pack_size, content, rp->key, KEY_SIZE, iv); - memcpy(buffer, content, entry->data_size); - free(content); - free(cypher); + void* cyphertext = malloc(entry->pack_size); + void* plaintext = malloc(entry->pack_size); + if (cyphertext != NULL && plaintext != NULL) + { + fread(cyphertext, entry->pack_size, 1, rp->pf); + aes_decrypt_cbc((uint8_t*)cyphertext, entry->pack_size, (uint8_t*)plaintext, rp->key, KEY_SIZE, iv); + memcpy(buffer, plaintext, entry->data_size); + } + if (plaintext != NULL) + free(plaintext); + if (cyphertext != NULL) + free(cyphertext); } else fread(buffer, entry->data_size, 1, rp->pf); /* validate integrity */ - crc = hash(0, buffer, entry->data_size); - if (crc != entry->crc) + crc = _crc32(0, buffer, entry->data_size); + if (crc == entry->crc) + buffer[entry->data_size] = 0; // NULL-terminated string + else { free(buffer); buffer = NULL; } + return (void*)buffer; +} - return buffer; +/* builds AES key schedule using md5 of variable length passphrase */ +static void build_key(const char* passphrase, uint32_t* key) +{ + MD5_CTX md5c; + uint8_t md5_result[16] = { 0 }; + + MD5_Init(&md5c); + MD5_Update(&md5c, passphrase, (uint32_t)strlen(passphrase)); + MD5_Final(md5_result, &md5c); + aes_key_setup(md5_result, key, KEY_SIZE); } /* opens a resource pack */ -ResPack ResPack_Open(const char* filename, const char* key) +ResPack ResPack_Open(const char* filename, const char* passphrase) { ResPack rp = NULL; ResHeader res_header; @@ -162,15 +178,19 @@ ResPack ResPack_Open(const char* filename, const char* key) /* create object */ size = sizeof(struct _ResPack) + sizeof(ResEntry)*res_header.num_regs; rp = (ResPack)calloc(size, 1); + if (rp == NULL) + { + fclose(pf); + return NULL; + } + rp->num_entries = res_header.num_regs; rp->pf = pf; /* prepare AES-128 key*/ - if (key != NULL) + if (passphrase != NULL) { - char padded_key[16] = { 0 }; - strncpy(padded_key, key, sizeof(padded_key)); - aes_key_setup((uint8_t*)padded_key, rp->key, KEY_SIZE); + build_key(passphrase, rp->key); rp->encrypted = true; } @@ -226,6 +246,9 @@ ResAsset ResPack_OpenAsset(ResPack rp, const char* filename) return NULL; asset = (ResAsset)malloc(sizeof(struct _ResAsset)); + if (asset == NULL) + return NULL; + sprintf(asset->filename, "_tmp%d", entry->id); asset->pf = fopen(asset->filename, "wb"); fwrite(content, entry->data_size, 1, asset->pf); @@ -265,12 +288,11 @@ void ResPack_CloseAsset(ResAsset asset) } } -/* loads file to memory, padded to 16 byte boundary for AES */ -static void* load_file(const char* filename, uint32_t padding, uint32_t* data_size, uint32_t* pack_size) +/* loads file to memory */ +static void* load_file(const char* filename, uint32_t* data_size) { FILE* pf; uint32_t size; - uint32_t pad_size; void* buffer; pf = fopen(filename, "rb"); @@ -279,18 +301,132 @@ static void* load_file(const char* filename, uint32_t padding, uint32_t* data_si fseek(pf, 0, SEEK_END); size = ftell(pf); - if (padding > 1) - pad_size = ((size + padding - 1) / padding) * padding; - else - pad_size = size; fseek(pf, 0, SEEK_SET); - buffer = malloc(pad_size); - memset(buffer, 0, pad_size); + buffer = malloc(size); + if (buffer == NULL) + { + fclose(pf); + return NULL; + } + fread(buffer, size, 1, pf); fclose(pf); *data_size = size; - *pack_size = pad_size; return buffer; } + +#ifdef RESPACK_LIB + +/* builds a resource pack, returns number of assets */ +int ResPack_Build(const char* filelist, const char* passphrase) +{ + FILE* pf_list; + FILE* pf_output; + ResHeader res_header = { FILE_ID, 0 }; + ResEntry* res_entries = NULL; + char* dot; + char filename[100]; + char line[200]; + uint32_t c; + uint32_t offset; + int count = 0; + uint32_t key[60] = { 0 }; + + /* open input list */ + strncpy(filename, filelist, sizeof(filename)); + pf_list = fopen(filename, "rt"); + if (pf_list == NULL) + { + printf("ResPack_Build error: file %s not found\n", filename); + return 0; + } + + /* count number of lines */ + while (fgets(line, sizeof(line), pf_list) != NULL) + res_header.num_regs += 1; + fseek(pf_list, 0, SEEK_SET); + + /* open output file */ + dot = strchr(filename, '.'); + if (dot != NULL) + *dot = 0; + strncat(filename, ".dat", sizeof(filename)); + pf_output = fopen(filename, "wb"); + + /* optionally starts AES-128 encryption */ + if (passphrase != NULL) + build_key(passphrase, key); + + /* generates output */ + res_entries = (ResEntry*)calloc(res_header.num_regs, sizeof(ResEntry)); + offset = sizeof(ResHeader) + (res_header.num_regs * sizeof(ResEntry)); + for (c = 0; c < res_header.num_regs; c++) + { + ResEntry* entry = &res_entries[c]; + void* content; + + /* load source content */ + fgets(line, sizeof(line), pf_list); + dot = strchr(line, '\n'); + if (dot != NULL) + *dot = 0; + content = load_file(line, &entry->data_size); + if (content == NULL) + { + printf("ResPack_Build warning: asset \"%s\" not found\n", line); + continue; + } + + /* update entry header */ + entry->pack_size = entry->data_size; + entry->offset = offset; + entry->id = path2_crc32(line); + entry->crc = _crc32(0, content, entry->data_size); + count += 1; + + /* optional encryption */ + if (passphrase != NULL) + { + /* calc full block size */ + uint32_t pack_size = (entry->data_size + AES_BLOCK_SIZE - 1) & ~(AES_BLOCK_SIZE - 1); + if (pack_size == entry->data_size) + pack_size += AES_BLOCK_SIZE; + entry->pack_size = pack_size; + + /* allocate & fill with PKCS#7 padding value*/ + uint8_t* plaintext = (uint8_t*)malloc(pack_size); + uint32_t pkcs7_value = pack_size - entry->data_size; + memcpy(plaintext, content, entry->data_size); + memset(&plaintext[entry->data_size], pkcs7_value, pkcs7_value); + free(content); + + /* encrypt & discard plaintext */ + void* cyphertext = malloc(pack_size); + aes_encrypt_cbc(plaintext, entry->pack_size, (uint8_t*)cyphertext, key, KEY_SIZE, iv); + content = cyphertext; + free(plaintext); + } + + //printf("%s id=%08X, size=%d, pack_size=%d, _crc32=%08X\n", line, entry->id, entry->data_size, entry->pack_size, entry->crc); + + /* write to file */ + fseek(pf_output, offset, SEEK_SET); + fwrite(content, entry->pack_size, 1, pf_output); + free(content); + offset += entry->pack_size; + } + + /* write headers */ + fseek(pf_output, 0, SEEK_SET); + fwrite(&res_header, sizeof(ResHeader), 1, pf_output); + fwrite(res_entries, sizeof(ResEntry), res_header.num_regs, pf_output); + fclose(pf_output); + + fclose(pf_list); + free(res_entries); + + return count; +} +#endif diff --git a/src/Sequence.c b/src/Sequence.c index 259d1146..33bc7c5f 100644 --- a/src/Sequence.c +++ b/src/Sequence.c @@ -18,33 +18,8 @@ #include "Sequence.h" #include "Tilengine.h" #include "Object.h" -#include "Hash.h" +#include "crc32.h" -/*! - * \brief - * Creates a new sequence for the animation engine - * - * \param name - * String with an unique name to query later - * - * \param target - * For tileset animations, the tile index to animate - * - * \param count - * Number of frames - * - * \param frames - * Array of TLN_Frame items with indexes and delays - * - * \returns - * Reference to the new sequence or NULL if error - * - * \remarks - * Use this function to create tileset or sprite animations - * - * \see - * TLN_SetTilemapAnimation(), TLN_SetSpriteAnimation() - */ TLN_Sequence TLN_CreateSequence (const char* name, int target, int count, TLN_SequenceFrame* frames) { int size; @@ -58,7 +33,7 @@ TLN_Sequence TLN_CreateSequence (const char* name, int target, int count, TLN_Se if (name) { - sequence->hash = hash(0, name, strlen(name)); + sequence->hash = _crc32(0, name, strlen(name)); strncpy (sequence->name, name, sizeof(sequence->name)); sequence->name[sizeof(sequence->name) - 1] = '\0'; } @@ -72,28 +47,6 @@ TLN_Sequence TLN_CreateSequence (const char* name, int target, int count, TLN_Se return sequence; } -/*! - * \brief - * Creates a color cycle sequence for palette animation - * - * \param name - * String with an unique name to query later - * - * \param count - * Number of color strips - * - * \param strips - * Array of color strips to assign - * - * \returns - * Reference to the created cycle or NULL if error - * - * \remarks - * Use this function to create advanced palette animation effects - * - * \see - * TLN_ColorStrip(), TLN_SetPaletteAnimation() - */ TLN_Sequence TLN_CreateCycle (const char* name, int count, TLN_ColorStrip* strips) { int size, c; @@ -108,7 +61,7 @@ TLN_Sequence TLN_CreateCycle (const char* name, int count, TLN_ColorStrip* strip if (name) { - sequence->hash = hash(0, name, strlen(name)); + sequence->hash = _crc32(0, name, strlen(name)); strncpy (sequence->name, name, sizeof(sequence->name)); sequence->name[sizeof(sequence->name) - 1] = '\0'; } @@ -130,22 +83,15 @@ TLN_Sequence TLN_CreateCycle (const char* name, int count, TLN_ColorStrip* strip return sequence; } -/*! - * \see Creates a name based sprite sequence - * - * \param name Optional name used to retrieve it when adding to a TLN_SequencePack, can be NULL - * \param spriteset Reference to the spriteset with frames to animate - * \param basename Base of the sprite name for the numbered sequence - * \param count Number of frames in the animation - * \param delay Number of ticks to delay between frame - * \return Reference to the created TLN_Sequence object or NULL if error - */ -TLN_Sequence TLN_CreateSpriteSequence(const char* name, TLN_Spriteset spriteset, char* basename, int count, int delay) +TLN_Sequence TLN_CreateSpriteSequence(const char* name, TLN_Spriteset spriteset, const char* basename, int delay) { int size; TLN_Sequence sequence; TLN_SequenceFrame* frame; - int c, old; + int c; + int count = 0; + int index; + char framename[64]; if (!CheckBaseObject(spriteset, OT_SPRITESET)) { @@ -153,6 +99,22 @@ TLN_Sequence TLN_CreateSpriteSequence(const char* name, TLN_Spriteset spriteset, return NULL; } + /* find number of frames */ + do + { + snprintf(framename, sizeof(framename), "%s%d", basename, count + 1); + index = TLN_FindSpritesetSprite(spriteset, framename); + if (index != -1) + count += 1; + } while (index != -1); + + /* noi matching frames found: exit */ + if (count == 0) + { + TLN_SetLastError(TLN_ERR_REF_SPRITESET); + return NULL; + } + size = count * sizeof(TLN_SequenceFrame); sequence = (TLN_Sequence)CreateBaseObject(OT_SEQUENCE, sizeof(struct Sequence) + size); if (!sequence) @@ -160,7 +122,7 @@ TLN_Sequence TLN_CreateSpriteSequence(const char* name, TLN_Spriteset spriteset, if (name) { - sequence->hash = hash(0, name, strlen(name)); + sequence->hash = _crc32(0, name, strlen(name)); strncpy(sequence->name, name, sizeof(sequence->name)); sequence->name[sizeof(sequence->name) - 1] = '\0'; } @@ -168,21 +130,10 @@ TLN_Sequence TLN_CreateSpriteSequence(const char* name, TLN_Spriteset spriteset, /* build frames from sprite name */ frame = (TLN_SequenceFrame*)&sequence->data; - old = 0; for (c = 0; c < count; c++) { - int index; - char num[5]; - char framename[64]; - - snprintf(num, sizeof(num), "%d", c + 1); - snprintf(framename, sizeof(framename), "%s%s", basename, num); - index = TLN_FindSpritesetSprite(spriteset, framename); - if (index != -1) - frame->index = index; - else - frame->index = old; - old = frame->index; + snprintf(framename, sizeof(framename), "%s%d", basename, c + 1); + frame->index = TLN_FindSpritesetSprite(spriteset, framename); frame->delay = delay; frame += 1; } @@ -191,19 +142,6 @@ TLN_Sequence TLN_CreateSpriteSequence(const char* name, TLN_Spriteset spriteset, return sequence; } -/*! - * \brief - * Creates a duplicate of the specified sequence - * - * \param src - * Sequence to clone - * - * \returns - * A reference to the newly cloned sequence, or NULL if error - * - * \see - * TLN_FindSequence() - */ TLN_Sequence TLN_CloneSequence (TLN_Sequence src) { TLN_Sequence sequence; @@ -221,19 +159,6 @@ TLN_Sequence TLN_CloneSequence (TLN_Sequence src) return NULL; } -/*! - * \brief - * Returns runtime info about a given sequence - * - * \param sequence - * Sequence to query - * - * \param info - * Pointer to a user-provided TLN_SequenceInfo structure to hold the returned data - * - * \see - * TLN_FindSequence() - */ bool TLN_GetSequenceInfo (TLN_Sequence sequence, TLN_SequenceInfo* info) { if (CheckBaseObject (sequence, OT_SEQUENCE) && info != NULL) @@ -247,16 +172,6 @@ bool TLN_GetSequenceInfo (TLN_Sequence sequence, TLN_SequenceInfo* info) return false; } -/*! - * \brief - * Deletes the sequence and frees resources - * - * \param sequence - * Reference to the sequence to be deleted - * - * \remarks - * Don't delete an active sequence! - */ bool TLN_DeleteSequence (TLN_Sequence sequence) { if (CheckBaseObject (sequence, OT_SEQUENCE)) diff --git a/src/Sequence.h b/src/Sequence.h index 223b66c6..5caae4f0 100644 --- a/src/Sequence.h +++ b/src/Sequence.h @@ -12,7 +12,7 @@ #define _SEQUENCE_H #include "Object.h" -#include "Hash.h" +#include "crc32.h" /* ciclo de color */ struct Strip @@ -30,7 +30,7 @@ struct Strip struct Sequence { DEFINE_OBJECT; - hash_t hash; + uint32_t hash; int count; int target; char name[32]; diff --git a/src/SequencePack.c b/src/SequencePack.c index 040678d4..aa961bb8 100644 --- a/src/SequencePack.c +++ b/src/SequencePack.c @@ -12,18 +12,8 @@ #include #include "SequencePack.h" #include "Object.h" -#include "Hash.h" - -/*! - * \brief - * Creates a new collection of sequences - * - * \returns - * Reference to the created pack or NULL if error - * - * \see - * TLN_AddSequenceToPack(), TLN_CreateSequence() - */ +#include "crc32.h" + TLN_SequencePack TLN_CreateSequencePack (void) { TLN_SequencePack sp; @@ -35,22 +25,6 @@ TLN_SequencePack TLN_CreateSequencePack (void) return sp; } -/*! - * \brief - * Adds a sequence to a sequence pack - * - * \param sp - * Reference to the sequence pack - * - * \param sequence - * Reference to the sequence to attach - * - * \remarks - * Write remarks for TLN_AddSequenceToPack here. - * - * \see - * TLN_CreateSequencePack(), TLN_CreateSequence() - */ bool TLN_AddSequenceToPack (TLN_SequencePack sp, TLN_Sequence sequence) { if (!CheckBaseObject (sp, OT_SEQPACK) || !CheckBaseObject (sequence, OT_SEQUENCE)) @@ -69,13 +43,6 @@ bool TLN_AddSequenceToPack (TLN_SequencePack sp, TLN_Sequence sequence) return true; } -/*! - * \brief - * Returns the number of sequences inside a sequence pack - * - * \param sp - * Reference to the sequence pack to query - */ int TLN_GetSequencePackCount (TLN_SequencePack sp) { if (!CheckBaseObject (sp, OT_SEQPACK)) @@ -85,19 +52,6 @@ int TLN_GetSequencePackCount (TLN_SequencePack sp) return sp->num_sequences; } -/*! - * \brief - * Returns the nth sequence inside a sequence pack - * - * \param sp - * Reference to the sequence pack containing the sequence to find - * - * \param index - * Sequence number to return [0, num_sequences - 1] - * - * \see - * TLN_LoadSequencePack(), TLN_GetSequencePackCount() - */ TLN_Sequence TLN_GetSequence (TLN_SequencePack sp, int index) { TLN_Sequence sequence; @@ -120,26 +74,10 @@ TLN_Sequence TLN_GetSequence (TLN_SequencePack sp, int index) return sequence; } -/*! - * \brief - * Finds a sequence inside a sequence pack - * - * \param sp - * Reference to the sequence pack containing the sequence to find - * - * \param name - * Name of the sequence to find - * - * \returns - * Reference to the sequence with the specified name, or NULL if not found - * - * \see - * TLN_LoadSequencePack() - */ TLN_Sequence TLN_FindSequence (TLN_SequencePack sp, const char* name) { TLN_Sequence sequence; - hash_t find; + uint32_t find; if (!CheckBaseObject (sp, OT_SEQPACK)) return NULL; @@ -151,7 +89,7 @@ TLN_Sequence TLN_FindSequence (TLN_SequencePack sp, const char* name) } /* recorre lista */ - find = hash(0, name, strlen(name)); + find = _crc32(0, name, strlen(name)); sequence = sp->sequences; while (sequence != NULL) { @@ -168,20 +106,6 @@ TLN_Sequence TLN_FindSequence (TLN_SequencePack sp, const char* name) return NULL; } -/*! - * \brief - * Deletes the specified sequence pack and frees memory - * - * \param sp - * Reference to the sequence pack to delete - * - * \remarks - * Don't delete a sequence pack that has sequences currently attached to animations! - * The attached sequences are also deleted, so they haven't to be deleted externally. - * - * \see - * TLN_LoadSequencePack() - */ bool TLN_DeleteSequencePack (TLN_SequencePack sp) { if (!CheckBaseObject (sp, OT_SEQPACK)) diff --git a/src/Sprite.c b/src/Sprite.c index 73a1acc0..1ce93045 100644 --- a/src/Sprite.c +++ b/src/Sprite.c @@ -17,59 +17,25 @@ #include "Palette.h" #include "Spriteset.h" #include "Tables.h" -#include "Hash.h" +#include "Debug.h" #ifdef _MSC_VER #define inline __inline #endif -static void SelectBlitter (Sprite* sprite); -static void UpdateSprite (Sprite* sprite); - -/*! - * \brief - * Configures a sprite, setting setting spriteset and flags at once - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param spriteset - * Reference of the spriteset containing the graphics to set - * - * \param flags - * Can be 0 or a combination of FLAG_FLIPX and FLAG_FLIPY - * - * \remarks - * This function also assigns the palette of the spriteset - * \see - * TLN_SetSpritePicture() - */ -bool TLN_ConfigSprite (int nsprite, TLN_Spriteset spriteset, TLN_TileFlags flags) +static void SelectSpriteBlitter (Sprite* sprite); + +bool TLN_ConfigSprite (int nsprite, TLN_Spriteset spriteset, uint32_t flags) { return TLN_SetSpriteSet (nsprite, spriteset) && TLN_SetSpriteFlags (nsprite, flags); } -/*! - * \brief - * Assigns the spriteset and its palette to a given sprite - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param spriteset - * Reference of the spriteset containing the graphics to set - * - * \remarks - * This function also assigns the palette of the spriteset - * - * \see - * TLN_SetSpritePicture() - */ bool TLN_SetSpriteSet (int nsprite, TLN_Spriteset spriteset) { Sprite *sprite; + bool enabled; if (nsprite >= engine->numsprites) { TLN_SetLastError (TLN_ERR_IDX_SPRITE); @@ -81,25 +47,25 @@ bool TLN_SetSpriteSet (int nsprite, TLN_Spriteset spriteset) sprite = &engine->sprites[nsprite]; sprite->spriteset = spriteset; sprite->pitch = sprite->spriteset->bitmap->pitch; + enabled = sprite->ok; if (spriteset->palette) sprite->palette = spriteset->palette; sprite->ok = sprite->spriteset && sprite->palette; - sprite->num = nsprite; + if (sprite->ok) + { + sprite->num = nsprite; + sprite->ptx = sprite->pty = 0.0f; + sprite->ok = TLN_SetSpritePicture(nsprite, 0); + } + + /* sprite enabled: add to the end */ + if (enabled == false && sprite->ok == true) + ListAppendNode(&engine->list_sprites, nsprite); - return TLN_SetSpritePicture (nsprite, 0); + return sprite->ok; } -/*! - * \brief - * Sets flags for a given sprite - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param flags - * Can be 0 or a combination of FLAG_FLIPX and FLAG_FLIPY - */ -bool TLN_SetSpriteFlags (int nsprite, TLN_TileFlags flags) +bool TLN_SetSpriteFlags (int nsprite, uint32_t flags) { if (nsprite >= engine->numsprites) { @@ -112,26 +78,23 @@ bool TLN_SetSpriteFlags (int nsprite, TLN_TileFlags flags) return true; } -/*! - * \brief - * Sets the sprite position inside the viewport - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param x - * Horizontal position (0 = left margin) - * - * \param y - * Vertical position (0 = top margin) - * - * \remarks - * Call this function inside a raster callback to so some vertical distortion effects - * (fake scaling) or sprite multiplexing (reusing a single sprite at different screen heights). - * This technique was used by some 8 bit games, with very few hardware sprites, to draw much more - * sprites in the screen, as long as they don't overlap vertically - * - */ +bool TLN_EnableSpriteFlag(int nsprite, uint32_t flag, bool enable) +{ + if (nsprite >= engine->numsprites) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + + if (enable) + engine->sprites[nsprite].flags |= flag; + else + engine->sprites[nsprite].flags &= ~flag; + + TLN_SetLastError(TLN_ERR_OK); + return true; +} + bool TLN_SetSpritePosition (int nsprite, int x, int y) { Sprite *sprite; @@ -150,25 +113,6 @@ bool TLN_SetSpritePosition (int nsprite, int x, int y) return true; } -/*! - * \brief - * Sets the actual graphic to the sprite - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param entry - * Index of the actual picture inside the srpteset to assign (0 <= entry < num_spriteset_graphics) - * - * To show a sprite, at least two function calls are needed: first call TLN_ConfigSprite to assign - * the spriteset containing the graphics, and then call TLN_SetSpritePicture to assign the actual graphic. - * Usually all the frames for a given character are contained inside the same spriteset, so it is only - * needed to configure the sprite once at the beginning for a particular character, and then just reassign - * the graphic - * - * \see - * TLN_ConfigSprite(), TLN_SetSpriteSet(), TLN_SetSpritePictureByName() - */ bool TLN_SetSpritePicture (int nsprite, int entry) { Sprite *sprite; @@ -186,28 +130,12 @@ bool TLN_SetSpritePicture (int nsprite, int entry) sprite->info = &sprite->spriteset->data[entry]; sprite->pixels = sprite->spriteset->bitmap->data + sprite->info->offset; UpdateSprite (sprite); + debugmsg("SetSpritePicture %d -> %d\n", nsprite, entry); TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Assigns a palette to a sprite - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param palette - * Reference of the palete to assign - * - * Write detailed description for TLN_SetSpritePalette here. - * - * \remarks - * When a sprite is configured with a spriteset with the function TLN_ConfigSprite(), it - * automatically sets the palette of the assigned spriteset to the sprite. - * Use this function to override it and set another palette - */ bool TLN_SetSpritePalette (int nsprite, TLN_Palette palette) { Sprite *sprite; @@ -227,19 +155,6 @@ bool TLN_SetSpritePalette (int nsprite, TLN_Palette palette) return true; } -/*! - * \brief - * Gets the palette assigned to a given sprite - * - * \param nsprite - * Id of the sprite to query (0 <= id < max_sprites) - * - * \returns - * Reference to the sprite palette - * - * \see - * TLN_SetSpritePalette(), TLN_SetSpriteSet() - */ TLN_Palette TLN_GetSpritePalette (int nsprite) { if (nsprite >= engine->numsprites) @@ -252,22 +167,30 @@ TLN_Palette TLN_GetSpritePalette (int nsprite) return engine->sprites[nsprite].palette; } -/*! - * \brief - * Sets the blending mode (transparency effect) - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param mode - * Member of the TLN_Blend enumeration - * - * \param factor - * Deprecated as of 1.12, left for backwards compatibility but doesn't have effect. - * - * \see - * Blending - */ +int TLN_GetSpriteX(int nsprite) +{ + if (nsprite >= engine->numsprites) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return 0; + } + + TLN_SetLastError(TLN_ERR_OK); + return engine->sprites[nsprite].x; +} + +int TLN_GetSpriteY(int nsprite) +{ + if (nsprite >= engine->numsprites) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return 0; + } + + TLN_SetLastError(TLN_ERR_OK); + return engine->sprites[nsprite].y; +} + bool TLN_SetSpriteBlendMode (int nsprite, TLN_Blend mode, uint8_t factor) { Sprite *sprite; @@ -279,35 +202,12 @@ bool TLN_SetSpriteBlendMode (int nsprite, TLN_Blend mode, uint8_t factor) sprite = &engine->sprites[nsprite]; sprite->blend = SelectBlendTable (mode); - SelectBlitter (sprite); + SelectSpriteBlitter (sprite); TLN_SetLastError (TLN_ERR_OK); return true; } -/*! - * \brief - * Sets the scaling factor of the sprite - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param sx - * Horizontal scale factor - * - * \param sy - * Vertical scale factor - * - * By default the scaling factor of a given sprite is 1.0f, 1.0f, which means - * no scaling. Use values below 1.0 to downscale (shrink) and above 1.0 to upscale (enlarge). - * Call TLN_ResetScaling() to disable scaling - * - * \remarks - * The rendering of a sprite with scaling enabled requires somewhat more CPU power than a regular sprite. - * - * \see - * TLN_ResetSpriteScaling() - */ bool TLN_SetSpriteScaling (int nsprite, float sx, float sy) { Sprite *sprite; @@ -323,20 +223,10 @@ bool TLN_SetSpriteScaling (int nsprite, float sx, float sy) sprite->mode = MODE_SCALING; sprite->draw = GetSpriteDraw (sprite->mode); UpdateSprite (sprite); - SelectBlitter (sprite); + SelectSpriteBlitter (sprite); return true; } -/*! - * \brief - * Disables scaling for a given sprite - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \see - * TLN_SetSpriteScaling() - */ bool TLN_ResetSpriteScaling (int nsprite) { Sprite *sprite; @@ -353,10 +243,12 @@ bool TLN_ResetSpriteScaling (int nsprite) UpdateSprite (sprite); TLN_SetLastError (TLN_ERR_OK); - SelectBlitter (sprite); + SelectSpriteBlitter (sprite); return true; } +#if 0 + typedef struct { fix_t x, y; @@ -434,7 +326,7 @@ bool TLN_SetSpriteRotation(int nsprite, float angle) corners[c].y = (math2d_t)roundf(corners[c].y); } - /* obtiene rectแngulo contenedor en pantalla */ + /* obtiene rectรกngulo contenedor en pantalla */ rect = &sprite->dstrect; rect->x1 = rect->x2 = (int)corners[0].x; rect->y1 = rect->y2 = (int)corners[0].y; @@ -447,7 +339,7 @@ bool TLN_SetSpriteRotation(int nsprite, float angle) if (rect->y2 < point->y) rect->y2 = (int)point->y; } - /* ajusta array de puntos a origen (0,0) para obtener tama๑o */ + /* ajusta array de puntos a origen (0,0) para obtener tamaรฑo */ for (c = 0; c < 4; c++) { corners[c].x -= rect->x1; @@ -486,7 +378,7 @@ bool TLN_SetSpriteRotation(int nsprite, float angle) for (y = 0; y < rotated->height; y++) { for (x = 0; x < rotated->width; x++) - printf("%2d ", *get_bitmap_ptr(rotated, x, y)); + debugmsg("%2d ", *get_bitmap_ptr(rotated, x, y)); } */ return true; @@ -511,16 +403,8 @@ bool TLN_ResetSpriteRotation(int nsprite) return true; } -/*! - * \brief - * Returns the index of the assigned picture from the spriteset - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \see - * TLN_SetSpritePicture() - */ +#endif + int TLN_GetSpritePicture (int nsprite) { if (nsprite >= engine->numsprites) @@ -533,13 +417,6 @@ int TLN_GetSpritePicture (int nsprite) return engine->sprites[nsprite].index; } -/*! - * \brief - * Finds an available (unused) sprite - * - * \returns - * Index of the first unused sprite (starting from 0) or -1 if none found - */ int TLN_GetAvailableSprite(void) { int c; @@ -553,26 +430,6 @@ int TLN_GetAvailableSprite(void) return -1; } -/*! - * \brief - * Enable sprite collision checking at pixel level - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param enable - * Set true to enable o false to disable (default value) - * - * \remarks - * Only sprites that have collision enabled are checked between them, - * so to detect a collision between two sprites, both of them must - * have collision detection enabled. Processing collision detection - * sprites take more a bit more CPU time compared to non-colliding sprites, so - * by default it is disabled on all sprites. - * - * \see - * TLN_GetSpriteCollision() - */ bool TLN_EnableSpriteCollision(int nsprite, bool enable) { if (nsprite >= engine->numsprites) @@ -585,22 +442,6 @@ bool TLN_EnableSpriteCollision(int nsprite, bool enable) return true; } -/*! - * \brief - * Gets the collision status of a given sprite - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \returns - * Tue if this sprite is involved in a collision with another sprite - * - * \remarks - * Collision detection must be enabled for the sprite to get checked - * - * \see - * TLN_EnableSpriteCollision() - */ bool TLN_GetSpriteCollision(int nsprite) { if (nsprite >= engine->numsprites) @@ -612,43 +453,33 @@ bool TLN_GetSpriteCollision(int nsprite) return engine->sprites[nsprite].collision; } -/*! - * \brief - * Disables the sprite so it is not drawn - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \remarks - * A sprite is also automatically disabled when assigned with an invalid spriteste or palette. Disabled - * sprites are returned by the function TLN_GetAvailableSprite as available - */ bool TLN_DisableSprite(int nsprite) { + Sprite* sprite; + bool enabled; if (nsprite >= engine->numsprites) { TLN_SetLastError(TLN_ERR_IDX_SPRITE); return false; } - engine->sprites[nsprite].ok = false; + sprite = &engine->sprites[nsprite]; + enabled = sprite->ok; + sprite->ok = false; + sprite->collision = false; + sprite->do_collision = false; + + /* disabled: remove from linked list */ + if (enabled == true) + { + debugmsg("%s(%d)\t", __FUNCTION__, nsprite); + ListUnlinkNode(&engine->list_sprites, nsprite); + } + TLN_SetLastError(TLN_ERR_OK); return true; } -/*! - * \brief - * Returns runtime info about a given sprite - * - * \param nsprite - * Id of the sprite [0, num_sprites - 1] - * - * \param state - * Pointer to a user-allocated TLN_SpriteState structure to fill with requested data - * - * \remarks - * Info may not be accurate if SpriteState.enabled member is returned as false - */ TLNAPI bool TLN_GetSpriteState(int nsprite, TLN_SpriteState* state) { Sprite* sprite; @@ -688,8 +519,115 @@ TLNAPI bool TLN_GetSpriteState(int nsprite, TLN_SpriteState* state) return true; } -/* actualiza datos internos */ -static void UpdateSprite (Sprite* sprite) +bool TLN_SetFirstSprite(int nsprite) +{ + Sprite* sprite; + List* list; + ListNode* node; + int cut1, cut2; + if (nsprite >= engine->numsprites || !engine->sprites[nsprite].ok || nsprite == engine->list_sprites.first) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + list = &engine->list_sprites; + sprite = &engine->sprites[nsprite]; + node = &sprite->list_node; + + /* cut points inside the list to rejoin */ + cut1 = node->prev; + cut2 = node->next; + + /* rejoin segments */ + node->prev = -1; + node->next = -1; + ListLinkNodes(list, nsprite, list->first); + ListLinkNodes(list, cut1, cut2); + list->first = nsprite; + + debugmsg("%s(%d)\t", __FUNCTION__, nsprite); + ListPrint(list); + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_SetNextSprite(int nsprite, int next) +{ + List* list; + int cut1, cut2, cut3; + if (nsprite >= engine->numsprites || !engine->sprites[nsprite].ok || nsprite == next) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + + if (next >= engine->numsprites || !engine->sprites[next].ok) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + list = &engine->list_sprites; + + /* cut points inside the list to rejoin */ + cut1 = ListGetNext(list, nsprite); + cut2 = ListGetPrev(list, next); + cut3 = ListGetNext(list, next); + + /* rejoin segments */ + ListLinkNodes(list, nsprite, next); + ListLinkNodes(list, next, cut1); + ListLinkNodes(list, cut2, cut3); + if (list->first == next) + list->first = cut3; + if (list->last == nsprite) + list->last = next; + + debugmsg("%s(%d,%d)\t", __FUNCTION__, nsprite, next); + ListPrint(list); + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +bool TLN_EnableSpriteMasking(int nsprite, bool enable) +{ + return TLN_EnableSpriteFlag(nsprite, FLAG_MASKED, enable); +} + +/* normalize clamp in range 0.0f - 1.0f */ +static void nclamp(float* v) +{ + if (*v < 0.0f) + *v = 0.0f; + if (*v > 1.0f) + *v = 1.0f; +} + +bool TLN_SetSpritePivot(int nsprite, float px, float py) +{ + Sprite* sprite; + if (nsprite >= engine->numsprites) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + + sprite = &engine->sprites[nsprite]; + nclamp(&px); + nclamp(&py); + sprite->ptx = px; + sprite->pty = py; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +void TLN_SetSpritesMaskRegion(int top_line, int bottom_line) +{ + engine->sprite_mask_top = top_line; + engine->sprite_mask_bottom = bottom_line; +} + +/* updates clipping rect cache */ +void UpdateSprite (Sprite* sprite) { int w,h; @@ -699,19 +637,22 @@ static void UpdateSprite (Sprite* sprite) if (sprite->sx > 1.0) w = 0; - /* rectangulo origen (sprite) */ + /* sprite source rectangle */ MakeRect(&sprite->srcrect, 0, 0, sprite->info->w, sprite->info->h); - /* clipping normal */ + /* standard clipping */ if (sprite->mode == MODE_NORMAL) { w = sprite->info->w; h = sprite->info->h; - /* rectangulo destino (pantalla) */ - MakeRect(&sprite->dstrect, sprite->x, sprite->y, w, h); + int x = sprite->x - (int)(w * sprite->ptx); + int y = sprite->y - (int)(h * sprite->pty); - /* clipping vertical */ + /* screen target rectangle */ + MakeRect(&sprite->dstrect, x, y, w, h); + + /* vertical clipping */ if (sprite->dstrect.y1 < 0) { sprite->srcrect.y1 -= sprite->dstrect.y1; @@ -723,7 +664,7 @@ static void UpdateSprite (Sprite* sprite) sprite->dstrect.y2 = engine->framebuffer.height; } - /* clipping horizontal */ + /* horizontal clipping */ if (sprite->dstrect.x1 < 0) { sprite->srcrect.x1 -= sprite->dstrect.x1; @@ -739,27 +680,25 @@ static void UpdateSprite (Sprite* sprite) /* clipping scaling */ else if (sprite->mode == MODE_SCALING) { - int srcw, srch, dstw, dsth; - w = (int)(sprite->info->w * sprite->sx); h = (int)(sprite->info->h * sprite->sy); - /* rectangulo destino (pantalla) */ - sprite->dstrect.x1 = sprite->x + ((sprite->info->w - w) >> 1); - sprite->dstrect.y1 = sprite->y + ((sprite->info->h - h) >> 1); + /* screen target rectangle */ + sprite->dstrect.x1 = sprite->x - (int)(w * sprite->ptx); + sprite->dstrect.y1 = sprite->y - (int)(h * sprite->pty); sprite->dstrect.x2 = sprite->dstrect.x1 + w; sprite->dstrect.y2 = sprite->dstrect.y1 + h; - /* coordenadas origen son fix */ + /* source coords are 16.16 fixed point */ sprite->srcrect.x1 = int2fix (sprite->srcrect.x1); sprite->srcrect.y1 = int2fix (sprite->srcrect.y1); sprite->srcrect.x2 = int2fix (sprite->srcrect.x2); sprite->srcrect.y2 = int2fix (sprite->srcrect.y2); - srcw = sprite->srcrect.x2 - sprite->srcrect.x1; - srch = sprite->srcrect.y2 - sprite->srcrect.y1; - dstw = sprite->dstrect.x2 - sprite->dstrect.x1; - dsth = sprite->dstrect.y2 - sprite->dstrect.y1; + int srcw = sprite->srcrect.x2 - sprite->srcrect.x1; + int srch = sprite->srcrect.y2 - sprite->srcrect.y1; + int dstw = sprite->dstrect.x2 - sprite->dstrect.x1; + int dsth = sprite->dstrect.y2 - sprite->dstrect.y1; sprite->dx = srcw/dstw; sprite->dy = srch/dsth; @@ -792,19 +731,19 @@ static void UpdateSprite (Sprite* sprite) } /* - printf ("Sprite %02d scale=%.02f,%.02f src=[%d,%d,%d,%d] dst=[%d,%d,%d,%d]\n", + debugmsg ("Sprite %02d scale=%.02f,%.02f src=[%d,%d,%d,%d] dst=[%d,%d,%d,%d]\n", sprite->num, sprite->sx, sprite->sy, fix2int(sprite->srcrect.x1), fix2int(sprite->srcrect.y1), fix2int(sprite->srcrect.x2), fix2int(sprite->srcrect.y2), sprite->dstrect.x1, sprite->dstrect.y1, sprite->dstrect.x2, sprite->dstrect.y2); */ } -static void SelectBlitter (Sprite* sprite) +static void SelectSpriteBlitter (Sprite* sprite) { const bool scaling = sprite->mode == MODE_SCALING; const bool blend = sprite->blend != NULL; - sprite->blitter = GetBlitter (32, true, scaling, blend); + sprite->blitter = SelectBlitter (true, scaling, blend); } void MakeRect(rect_t* rect, int x, int y, int w, int h) diff --git a/src/Sprite.h b/src/Sprite.h index edacd281..1fd4a17f 100644 --- a/src/Sprite.h +++ b/src/Sprite.h @@ -15,6 +15,8 @@ #include "Draw.h" #include "Blitters.h" #include "Spriteset.h" +#include "List.h" +#include "Animation.h" /* rectangulo */ typedef struct @@ -34,22 +36,30 @@ typedef struct Sprite uint8_t* pixels; int pitch; int num; - int index; - int x,y; + int index; /* spriteset picture index */ + int x,y; /* screen space location (TLN_SetSpritePosition) */ int dx,dy; + int xworld, yworld; /* world space location (TLN_SetSpriteWorldPosition) */ float sx,sy; + float ptx, pty; /* normalized pivot position inside sprite (default = 0,0) */ rect_t srcrect; rect_t dstrect; draw_t mode; uint8_t* blend; - TLN_TileFlags flags; + uint32_t flags; ScanDrawPtr draw; ScanBlitPtr blitter; bool ok; bool do_collision; bool collision; + bool world_space; /* valid position is world space, false = screen space */ + bool dirty; /* requires call to UpdatePosition() before drawing */ TLN_Bitmap rotation_bitmap; + ListNode list_node; + Animation animation; } Sprite; +extern void UpdateSprite(Sprite* sprite); + #endif \ No newline at end of file diff --git a/src/Spriteset.c b/src/Spriteset.c index 4f28e704..4c6a4c92 100644 --- a/src/Spriteset.c +++ b/src/Spriteset.c @@ -14,7 +14,7 @@ #include "Spriteset.h" #include "Palette.h" #include "Bitmap.h" -#include "Hash.h" +#include "crc32.h" static void set_sprite_entry (TLN_Spriteset spriteset, int entry, TLN_SpriteData* data) { @@ -23,30 +23,11 @@ static void set_sprite_entry (TLN_Spriteset spriteset, int entry, TLN_SpriteData dst_data->h = data->h; dst_data->offset = data->y*spriteset->bitmap->pitch + data->x; if (data->name[0] != 0) - dst_data->hash = hash(0, data->name, strlen(data->name)); + dst_data->hash = _crc32(0, data->name, strlen(data->name)); else dst_data->hash = 0; } -/*! - * \brief - * Creates a new spriteset - * - * \param bitmap - * Bitmap containing the sprite graphics - * - * \param data - * Array of TLN_SpriteData structures with sprite descriptions - * - * \param num_entries - * Number of entries in data[] array - * - * \returns - * Reference to the created spriteset, or NULL if error - * - * \see - * TLN_DeleteSpriteset() - */ TLN_Spriteset TLN_CreateSpriteset (TLN_Bitmap bitmap, TLN_SpriteData* data, int num_entries) { TLN_Spriteset spriteset = NULL; @@ -75,28 +56,6 @@ TLN_Spriteset TLN_CreateSpriteset (TLN_Bitmap bitmap, TLN_SpriteData* data, int return spriteset; } -/*! - * \brief - * Sets attributes and pixels of a given sprite inside a spriteset - * - * \param spriteset - * Spriteset to set the data - * - * \param entry - * The entry index inside the spriteset to modify [0, num_sprites - 1] - * - * \param data - * Pointer to a user-provided TLN_SpriteData structure with sprite description - * - * \param pixels - * Pointer to source pixel data - * - * \param pitch - * Number of bytes per scanline of the source pixel data - * - * \see - * TLN_CreateSpriteset() - */ bool TLN_SetSpritesetData (TLN_Spriteset spriteset, int entry, TLN_SpriteData* data, void* pixels, int pitch) { if (!CheckBaseObject (spriteset, OT_SPRITESET)) @@ -125,18 +84,6 @@ bool TLN_SetSpritesetData (TLN_Spriteset spriteset, int entry, TLN_SpriteData* d return true; } -/*! - * \brief - * Creates a duplicate of the specified spriteset and its associated palette - * - * \param src - * Spriteset to clone - * - * \returns - * A reference to the newly cloned spriteset, or NULL if error - * \see - * TLN_LoadSpriteset() - */ TLN_Spriteset TLN_CloneSpriteset (TLN_Spriteset src) { TLN_Spriteset spriteset; @@ -154,19 +101,6 @@ TLN_Spriteset TLN_CloneSpriteset (TLN_Spriteset src) return NULL; } -/*! - * \brief - * Deletes the specified spriteset and frees memory - * - * \param spriteset - * Spriteset to delete - * - * \remarks - * Don't delete a spriteset currently attached to a sprite! - * - * \see - * TLN_LoadSpriteset(), TLN_CloneSpriteset() - */ bool TLN_DeleteSpriteset (TLN_Spriteset spriteset) { if (CheckBaseObject (spriteset, OT_SPRITESET)) @@ -181,22 +115,6 @@ bool TLN_DeleteSpriteset (TLN_Spriteset spriteset) return false; } -/*! - * \brief - * Query the details about the specified sprite inside a spriteset - * - * \param spriteset - * Reference to the spriteset to get info about - * - * \param entry - * The entry index inside the spriteset [0, num_sprites - 1] - * - * \param info - * Pointer to application-allocated TLN_SpriteInfo structure that will receive the data - * - * \returns - * true if success or false if error - */ bool TLN_GetSpriteInfo (TLN_Spriteset spriteset, int entry, TLN_SpriteInfo* info) { if (CheckBaseObject (spriteset, OT_SPRITESET) && info) @@ -211,21 +129,6 @@ bool TLN_GetSpriteInfo (TLN_Spriteset spriteset, int entry, TLN_SpriteInfo* info return false; } -/*! - * \brief - * Returns a reference to the palette associated to the specified spriteset - * - * \param spriteset - * Spriteset to obtain the palette - * - * \remarks - * The palette of a spriteset is created at load time and cannot be modified. When TLN_ConfigSprite - * function is used to setup a sprite, the palette associated with the specified spriteset is automatically - * assigned to that sprite, but it can be later replaced with TLN_SetSpritePalette - * - * \see - * TLN_SetSpritePalette() - */ TLN_Palette TLN_GetSpritesetPalette (TLN_Spriteset spriteset) { if (CheckBaseObject (spriteset, OT_SPRITESET)) @@ -237,22 +140,9 @@ TLN_Palette TLN_GetSpritesetPalette (TLN_Spriteset spriteset) return NULL; } -/*! - * \brief - * Returns a reference to the palette associated to the specified spriteset - * - * \param spriteset - * Spriteset where to find the sprite - * - * \param name - * Name of the sprite to findo - * - * \returns - * sprite index (0 -> num_sprites - 1) if found, or -1 if not found - */ int TLN_FindSpritesetSprite (TLN_Spriteset spriteset, const char* name) { - hash_t find; + uint32_t find; int entry = -1; int c = 0; @@ -263,7 +153,7 @@ int TLN_FindSpritesetSprite (TLN_Spriteset spriteset, const char* name) return false; /* search by name hash */ - find = hash(0, name, strlen(name)); + find = _crc32(0, name, strlen(name)); for (c=0; c < spriteset->entries; c++) { const SpriteEntry* info = &spriteset->data[c]; @@ -271,4 +161,4 @@ int TLN_FindSpritesetSprite (TLN_Spriteset spriteset, const char* name) return c; } return -1; -} \ No newline at end of file +} diff --git a/src/Spriteset.h b/src/Spriteset.h index e12734d5..258c8829 100644 --- a/src/Spriteset.h +++ b/src/Spriteset.h @@ -13,12 +13,12 @@ #include "Object.h" #include "Tilengine.h" -#include "Hash.h" +#include "crc32.h" /* registro de sprite */ typedef struct { - hash_t hash; + uint32_t hash; int w,h; int offset; } diff --git a/src/Tilemap.c b/src/Tilemap.c index ccbc0145..60999e42 100644 --- a/src/Tilemap.c +++ b/src/Tilemap.c @@ -23,34 +23,6 @@ typedef struct } Rect; -/*! - * \brief - * Creates a new tilemap - * - * \param rows - * Number of rows (vertical dimension) - * - * \param cols - * Number of cols (horizontal dimension) - * - * \param tiles - * Array of tiles with data (see struct Tile) - * - * \param bgcolor - * Background color value (RGB32 packed) - * - * \param tileset - * Optional reference to associated tileset, can be NULL - * - * \returns - * Reference to the created tilemap, or NULL if error - * - * \remarks - * Make sure that the tiles[] array is has at least rows*cols items or application may crash - * - * \see - * TLN_DeleteTilemap(), struct Tile - */ TLN_Tilemap TLN_CreateTilemap (int rows, int cols, TLN_Tile tiles, uint32_t bgcolor, TLN_Tileset tileset) { TLN_Tilemap tilemap = NULL; @@ -63,7 +35,8 @@ TLN_Tilemap TLN_CreateTilemap (int rows, int cols, TLN_Tile tiles, uint32_t bgco tilemap->rows = rows; tilemap->cols = cols; tilemap->bgcolor = bgcolor; - tilemap->tileset = tileset; + tilemap->tilesets[0] = tileset; + tilemap->visible = true; if (tiles) memcpy (tilemap->tiles, tiles, tilemap->size - sizeof(struct Tilemap)); @@ -72,19 +45,6 @@ TLN_Tilemap TLN_CreateTilemap (int rows, int cols, TLN_Tile tiles, uint32_t bgco return tilemap; } -/*! - * \brief - * Creates a duplicate of the specified tilemap - * - * \param src - * Reference to the tilemap to clone - * - * \returns - * A reference to the newly cloned tilemap, or NULL if error - * - * \see - * TLN_LoadTilemap() - */ TLN_Tilemap TLN_CloneTilemap (TLN_Tilemap src) { TLN_Tilemap tilemap; @@ -102,16 +62,6 @@ TLN_Tilemap TLN_CloneTilemap (TLN_Tilemap src) return NULL; } -/*! - * \brief - * Returns the number of vertical tiles in the tilemap - * - * \param tilemap - * Reference of the tilemap to get info - * - * \see - * TLN_GetTilemapCols() - */ int TLN_GetTilemapRows (TLN_Tilemap tilemap) { if (CheckBaseObject (tilemap, OT_TILEMAP)) @@ -123,16 +73,6 @@ int TLN_GetTilemapRows (TLN_Tilemap tilemap) return 0; } -/*! - * \brief - * Returns the number of horizontal tiles in the tilemap - * - * \param tilemap - * Reference of the tilemap to get info - * - * \see - * TLN_GetTilemapCols() - */ int TLN_GetTilemapCols (TLN_Tilemap tilemap) { if (CheckBaseObject (tilemap, OT_TILEMAP)) @@ -144,27 +84,45 @@ int TLN_GetTilemapCols (TLN_Tilemap tilemap) return 0; } -/*! - * \brief - * Returns the optional associated tileset to the specified tilemap - * - * \param tilemap - * Reference of the tilemap to get info - * - * \see - * TLN_CreateTilemap(), TLN_LoadTilemap() - */ TLN_Tileset TLN_GetTilemapTileset (TLN_Tilemap tilemap) { - if (CheckBaseObject (tilemap, OT_TILEMAP)) + return TLN_GetTilemapTileset2(tilemap, 0); +} + +TLN_Tileset TLN_GetTilemapTileset2(TLN_Tilemap tilemap, int index) +{ + if (CheckBaseObject(tilemap, OT_TILEMAP)) { - TLN_SetLastError (TLN_ERR_OK); - return tilemap->tileset; + TLN_SetLastError(TLN_ERR_OK); + return tilemap->tilesets[index]; } else return NULL; } +bool TLN_SetTilemapTileset(TLN_Tilemap tilemap, TLN_Tileset tileset) +{ + return TLN_SetTilemapTileset2(tilemap, tileset, 0); +} + +bool TLN_SetTilemapTileset2(TLN_Tilemap tilemap, TLN_Tileset tileset, int index) +{ + if (!CheckBaseObject(tilemap, OT_TILEMAP)) + { + TLN_SetLastError(TLN_ERR_REF_TILEMAP); + return false; + } + if (!CheckBaseObject(tileset, OT_TILESET)) + { + TLN_SetLastError(TLN_ERR_REF_TILESET); + return false; + } + + tilemap->tilesets[index] = tileset; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + static TLN_Tile GetTilemapPtr (TLN_Tilemap tilemap, int row, int col) { if (rowrows && colcols) @@ -173,22 +131,6 @@ static TLN_Tile GetTilemapPtr (TLN_Tilemap tilemap, int row, int col) return NULL; } -/*! - * \brief - * Gets data of a single tile inside a tilemap - * - * \param tilemap - * Reference of the tilemap to get the tile - * - * \param row - * Vertical location of the tile (0 <= row < rows) - * - * \param col - * Horizontal location of the tile (0 <= col < cols) - * - * \param tile - * Reference to an application-allocated struct Tile that will get the data - */ bool TLN_GetTilemapTile (TLN_Tilemap tilemap, int row, int col, TLN_Tile tile) { if (CheckBaseObject (tilemap, OT_TILEMAP) && tile) @@ -211,37 +153,14 @@ bool TLN_GetTilemapTile (TLN_Tilemap tilemap, int row, int col, TLN_Tile tile) return false; } -/*! - * \brief - * Sets a tile of a tilemap - * - * \param tilemap - * Reference to the tilemap - * - * \param row - * Row (vertical position) of the tile [0 - num_rows - 1] - * - * \param col - * Column (horizontal position) of the tile [0 - num_cols - 1] - * - * \param tile - * Reference to the tile to set - * - * \returns - * true (success) or false (error) - */ bool TLN_SetTilemapTile (TLN_Tilemap tilemap, int row, int col, TLN_Tile tile) { if (CheckBaseObject (tilemap, OT_TILEMAP) && tile) { TLN_Tile dsttile = GetTilemapPtr (tilemap, row, col); - if (dsttile) + if (dsttile != NULL) { - dsttile->flags = tile->flags; - dsttile->index = tile->index; - if (tilemap->maxindex < tile->index) - tilemap->maxindex = tile->index; - + dsttile->value = tile != NULL ? tile->value : 0; TLN_SetLastError (TLN_ERR_OK); return true; } @@ -255,25 +174,20 @@ bool TLN_SetTilemapTile (TLN_Tilemap tilemap, int row, int col, TLN_Tile tile) return false; } -/*! - * \brief - * Deletes the specified tilemap and frees memory - * - * \param tilemap - * Reference to the tilemap to delete - * - * \remarks - * Don't delete a tilemap currently attached to a layer! - * - * \see - * TLN_LoadTilemap(), TLN_CloneTilemap() - */ +TLN_Tile TLN_GetTilemapTiles(TLN_Tilemap tilemap, int row, int col) +{ + if (!CheckBaseObject(tilemap, OT_TILEMAP)) + return NULL; + + return GetTilemapPtr(tilemap, row, col); +} + bool TLN_DeleteTilemap (TLN_Tilemap tilemap) { if (CheckBaseObject (tilemap, OT_TILEMAP)) { if (ObjectOwner (tilemap)) - TLN_DeleteTileset (tilemap->tileset); + TLN_DeleteTileset (tilemap->tilesets[0]); DeleteBaseObject (tilemap); TLN_SetLastError (TLN_ERR_OK); return true; @@ -290,37 +204,6 @@ static void ClipRect (Rect* src, Rect* dst) src->h = dst->h - src->y; } -/*! - * \brief - * Copies blocks of tiles between two tilemaps - * - * \param src - * Reference to the source tilemap - * - * \param srcrow - * Starting row (vertical position) inside the source tilemap - * - * \param srccol - * Starting column (horizontal position) inside the source tilemap - * - * \param rows - * Number of rows to copy - * - * \param cols - * Number of columns to copy - * - * \param dst - * Reference to the target tilemap - * - * \param dstrow - * Starting row (vertical position) inside the target tilemap - * - * \param dstcol - * Starting column (horizontal position) inside the target tilemap - * - * \remarks - * Use this function to implement tile streaming - */ bool TLN_CopyTiles (TLN_Tilemap src, int srcrow, int srccol, int rows, int cols, TLN_Tilemap dst, int dstrow, int dstcol) { int y, size; diff --git a/src/Tilemap.h b/src/Tilemap.h index 858f45dc..b9ff7624 100644 --- a/src/Tilemap.h +++ b/src/Tilemap.h @@ -14,15 +14,19 @@ #include "Object.h" #include "Tileset.h" +#define MAX_TILESETS 16 + /* mapa */ struct Tilemap { DEFINE_OBJECT; - int rows; /* filas */ - int cols; /* columnas */ - int maxindex; /* nบ de tile mแs alto */ - int bgcolor; /* color de fondo */ - struct Tileset* tileset; /* tileset asociado (si hay) */ + int rows; /* rows*/ + int cols; /* columns */ + int bgcolor; /* background color */ + int id; /* id property */ + bool visible; /* visible property */ + struct Tileset* tilesets[MAX_TILESETS]; /* attached tilesets */ + int num_tilesets; /* actual amount of tilesets */ Tile tiles[]; }; diff --git a/src/Tilengine.c b/src/Tilengine.c index 951429c0..3404e7c5 100644 --- a/src/Tilengine.c +++ b/src/Tilengine.c @@ -23,120 +23,114 @@ #include "Layer.h" #include "Sprite.h" #include "Tables.h" +#include "LoadTMX.h" /* magic number to recognize context object */ #define ID_CONTEXT 0x7E5D0AB1 TLN_Engine engine; /* current context */ -static TLN_Engine create_context(int hres, int vres, int bpp, int numlayers, int numsprites, int numanimations); +static TLN_Engine create_context(int hres, int vres, int numlayers, int numsprites, int numanimations); -/*! - * \brief - * Initializes the graphic engine in 32 bpp pixel format - * - * \param hres - * horizontal resolution in pixels - * - * \param vres - * vertical resolution in pixels - * - * \param numlayers - * number of layers - * - * \param numsprites - * number of sprites - * - * \param numanimations - * number of animations - * - * Performs initialisation of the main engine, creates the viewport with the specified dimensions - * and allocates the number of layers, sprites and animation slots - */ TLN_Engine TLN_Init (int hres, int vres, int numlayers, int numsprites, int numanimations) { - printf("Tilengine v%d.%d.%d %d-bit built %s %s\n", TILENGINE_VER_MAJ, TILENGINE_VER_MIN, TILENGINE_VER_REV, (int)(sizeof(UINTPTR_MAX) << 3), __DATE__, __TIME__); - return create_context (hres, vres, 32, numlayers, numsprites, numanimations); + printf("Tilengine v%d.%d.%d %d-bit %s built %s %s\n", + TILENGINE_VER_MAJ, + TILENGINE_VER_MIN, + TILENGINE_VER_REV, + (int)(sizeof(UINTPTR_MAX) << 3), +#if defined _DEBUG + "debug", +#else + "release", +#endif + __DATE__, + __TIME__); + return create_context (hres, vres, numlayers, numsprites, numanimations); } -/*! - * \brief - * As of version 1.12.1, this feature has been removed. Only 32 bpp is supported. The function call has - * been left for backwards binary compatibility but it defaults to TLN_Init() with 32 bpp. - */ -static TLN_Engine create_context(int hres, int vres, int bpp, int numlayers, int numsprites, int numanimations) +/* creates new engine context */ +static TLN_Engine create_context(int hres, int vres, int numlayers, int numsprites, int numanimations) { int c; TLN_Engine context; TLN_SetLastError (TLN_ERR_OK); - /* remove bpp, always 32 */ - bpp = 32; - /* create framebuffer */ context = (TLN_Engine)calloc(sizeof(Engine), 1); context->header = ID_CONTEXT; context->framebuffer.width = hres; context->framebuffer.height = vres; - context->framebuffer.pitch = (((hres * bpp)>>3) + 3) & ~0x03; - context->priority = (uint8_t*)malloc(context->framebuffer.pitch); - if (!context->priority) - { - TLN_DeleteContext (context); - TLN_SetLastError (TLN_ERR_OUT_OF_MEMORY); - return NULL; - } - - /* sprite collision buffer */ - context->collision = (uint16_t*)calloc(hres*sizeof(uint16_t), 1); - context->tmpindex = (uint8_t*)calloc(hres, 1); + context->framebuffer.pitch = (((hres * 32)>>3) + 3) & ~0x03; + context->target_fps = INTERNAL_FPS; - /* create static items */ - context->numlayers = numlayers; - context->layers = (Layer*)calloc (numlayers, sizeof(Layer)); - if (!context->layers) + /* create static layers */ + if (numlayers > 0) { - TLN_DeleteContext(context); - TLN_SetLastError (TLN_ERR_OUT_OF_MEMORY); - return NULL; + context->numlayers = numlayers; + context->layers = (Layer*)calloc(numlayers, sizeof(Layer)); + if (!context->layers) + { + TLN_DeleteContext(context); + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); + return NULL; + } + for (c = 0; c < context->numlayers; c++) + context->layers[c].mosaic.buffer = (uint32_t*)calloc(hres, sizeof(uint32_t)); + + /* buffer for intermediate scanline output */ + context->linebuffer = (uint32_t*)calloc(hres, sizeof(uint32_t)); + context->priority = (uint32_t*)malloc(context->framebuffer.pitch); } - for (c=0; cnumlayers; c++) - context->layers[c].mosaic.buffer = (uint8_t*)malloc (hres); - context->numsprites = numsprites; - context->sprites = (Sprite*)calloc (numsprites, sizeof(Sprite)); - if (!context->sprites) + /* create static sprites */ + if (numsprites > 0) { - TLN_DeleteContext(context); - TLN_SetLastError (TLN_ERR_OUT_OF_MEMORY); - return NULL; - } - for (c=0; cnumsprites; c++) - { - context->sprites[c].draw = GetSpriteDraw (MODE_NORMAL); - context->sprites[c].blitter = GetBlitter (bpp, true, false, false); - context->sprites[c].sx = context->sprites[c].sy = 1.0f; + context->numsprites = numsprites; + context->sprites = (Sprite*)calloc(numsprites, sizeof(Sprite)); + if (!context->sprites) + { + TLN_DeleteContext(context); + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); + return NULL; + } + for (c = 0; c < context->numsprites; c++) + { + Sprite* sprite = &context->sprites[c]; + sprite->draw = GetSpriteDraw(MODE_NORMAL); + sprite->blitter = SelectBlitter(true, false, false); + sprite->sx = sprite->sy = 1.0f; + } + ListInit(&context->list_sprites, &context->sprites[0].list_node, sizeof(Sprite), context->numsprites); + + /* sprite collision buffer */ + context->collision = (uint16_t*)calloc(hres, sizeof(uint16_t)); } - context->numanimations = numanimations; - context->animations = (Animation*)calloc (numanimations, sizeof(Animation)); - if (!context->animations) + /* create static animations */ + if (numanimations > 0) { - TLN_DeleteContext(context); - TLN_SetLastError (TLN_ERR_OUT_OF_MEMORY); - return NULL; + context->numanimations = numanimations; + context->animations = (Animation*)calloc(numanimations, sizeof(Animation)); + if (!context->animations) + { + TLN_DeleteContext(context); + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); + return NULL; + } + ListInit(&context->list_animations, &context->animations[0].list_node, sizeof(Animation), context->numanimations); } context->bgcolor = PackRGB32(0,0,0); - context->blit_fast = GetBlitter (bpp, false, false, false); + context->blit_fast = SelectBlitter (false, false, false); if (!CreateBlendTables ()) { TLN_DeleteContext(context); TLN_SetLastError (TLN_ERR_OUT_OF_MEMORY); return NULL; } - context->mod_table = SelectBlendTable (BLEND_MOD); + context->blend_table = SelectBlendTable (BLEND_MOD); /* set as default context if it's the first one */ if (engine == NULL) @@ -145,6 +139,10 @@ static TLN_Engine create_context(int hres, int vres, int bpp, int numlayers, int for (c = 0; cnumlayers; c++) TLN_DisableLayerClip(c); +#ifdef _DEBUG + TLN_SetLogLevel(TLN_LOG_ERRORS); +#endif + return context; } @@ -158,16 +156,6 @@ static bool check_context(TLN_Engine context) return false; } -/*! -* \brief -* Sets current engine context -* -* \param context -* TLN_Engine object to set as current context, returned by TLN_Init() -* -* \returns -* true if success or false if wrong context is supplied -*/ bool TLN_SetContext(TLN_Engine context) { if (check_context(context)) @@ -183,19 +171,11 @@ bool TLN_SetContext(TLN_Engine context) } } -/*! -* \brief -* Returns the current engine context -*/ TLN_Engine TLN_GetContext(void) { return engine; } -/*! -* \brief -* Deinitialises current engine context and frees used resources -*/ void TLN_Deinit(void) { if (engine != NULL) @@ -205,13 +185,6 @@ void TLN_Deinit(void) } } - /*! - * \brief - * Deletes explicit context - * - * \param context - * context reference to delete - */ bool TLN_DeleteContext(TLN_Engine context) { int c; @@ -242,106 +215,47 @@ bool TLN_DeleteContext(TLN_Engine context) if (context->collision) free(context->collision); - if (context->tmpindex) - free(context->tmpindex); + if (context->linebuffer) + free(context->linebuffer); free(context); return true; } -/*! - * \brief - * Sets logging level for current instance - * - * \param log_level - * value to set, member of the TLN_LogLevel enumeration - */ void TLN_SetLogLevel(TLN_LogLevel log_level) { if (engine != NULL) engine->log_level = log_level; } -/*! - * \brief - * Retrieves Tilengine dll version - * - * \returns - * Returns a 32-bit integer containing three packed numbers: - * bits 23:16 -> major version - * bits 15: 8 -> minor version - * bits 7: 0 -> bugfix revision - * - * \remarks - * Compare this number with the TILENGINE_HEADER_VERSION macro to check that both versions match! - * - */ uint32_t TLN_GetVersion (void) { TLN_SetLastError (TLN_ERR_OK); return TILENGINE_HEADER_VERSION; } -/*! - * \brief - * Returns the width in pixels of the framebuffer - * - * \see - * TLN_Init(), TLN_GetHeight() - */ +void TLN_SetTargetFps(int fps) +{ + engine->target_fps = fps; +} + +int TLN_GetTargetFps(void) +{ + return engine->target_fps; +} + int TLN_GetWidth (void) { TLN_SetLastError (TLN_ERR_OK); return engine->framebuffer.width; } -/*! - * \brief - * Returns the height in pixels of the framebuffer - * - * \see - * TLN_Init(), TLN_GetWidth() - */ int TLN_GetHeight (void) { TLN_SetLastError (TLN_ERR_OK); return engine->framebuffer.height; } -/*! - * \brief - * Returns the pixel format of the framebuffer - * - * \see - * TLN_InitBPP() - */ -int TLN_GetBPP (void) -{ - TLN_SetLastError (TLN_ERR_OK); - return 32; -} - -/*! - * \brief - * Sets the output surface for rendering - * - * \param data - * Pointer to the start of the target framebuffer - * - * \param pitch - * Number of bytes per each scanline of the framebuffer - * - * Sets the output surface for rendering. Tilengine doesn't provide a windowing or hardware - * video access. The application is responsible of allocating and maintaining the surface where - * tilengine does the rendering. It can be a SDL surface, a locked DirectX surface, an OpenGL texture, - * or whatever the application has access to. - * - * \remarks - * The render target pixel format must be 32 bits RGBA - * - * \see - * TLN_UpdateFrame() - */ void TLN_SetRenderTarget (uint8_t* data, int pitch) { engine->framebuffer.data = data; @@ -375,140 +289,147 @@ int TLN_GetRenderTargetPitch (void) return engine->framebuffer.pitch; } -/*! - * \brief - * Draws the frame to the previously specified render target - * - * \param time - * timestamp for animation control - * - * \remarks - * the timestamp value meaning is application defined, but its units must match the same used - * in the sequences xml file. It usually is the frame number or millisecond count - * - * \see - * TLN_SetRenderTarget() - */ -void TLN_UpdateFrame (int time) +/* basic reference list without duplicates */ +typedef struct { - TLN_BeginFrame (time); - while (TLN_DrawNextScanline ()){} - TLN_SetLastError (TLN_ERR_OK); + int index; + void* refs[TMX_MAX_TILESET]; } +RefList; -/*! - * \brief Starts active rendering of the current frame - * \param time Timestamp value - * \see TLN_DrawNextScanline(), TLN_BeginWindowFrame(), TLN_EndWindowFrame() - */ -void TLN_BeginFrame (int time) +/* finds reference in list */ +bool ref_find(RefList* refs, void* item) { int c; + for (c = 0; c < refs->index; c += 1) + { + if (refs->refs[c] == item) + return true; + } + return false; +} - UpdateAnimations (time); - engine->line = 0; +/* adds reference to list */ +bool ref_add(RefList* refs, void* item) +{ + if (refs->index < TMX_MAX_TILESET - 1 && !ref_find(refs, item)) + { + refs->refs[refs->index++] = item; + return true; + } + return false; +} + +/* Starts active rendering of the current frame */ +static void BeginFrame (int frame) +{ + /* update active animations */ + List* list; + int index; - /* limpia colisiones de sprites */ - for (c=0; cnumsprites; c++) - engine->sprites[c].collision = 0; + /* adjust to target fps */ + frame = (engine->frame*INTERNAL_FPS) / engine->target_fps; + engine->frame += 1; + + /* color cycle animations */ + if (engine->numanimations > 0) + { + list = &engine->list_animations; + index = list->first; + while (index != -1) + { + Animation* animation = &engine->animations[index]; + UpdateAnimation(animation, frame); + index = animation->list_node.next; + } + } + + /* sprite animations */ + if (engine->numsprites > 0) + { + list = &engine->list_sprites; + index = list->first; + while (index != -1) + { + Sprite* sprite = &engine->sprites[index]; + sprite->collision = false; + Animation* animation = &sprite->animation; + if (animation->enabled && !animation->paused) + UpdateAnimation(animation, frame); + index = sprite->list_node.next; + } + } + + /* tileset animations. calls just once per globally used tileset, avoids duplicate calls */ + RefList tilesets = { 0 }; + for (index = 0; index < engine->numlayers; index += 1) + { + Layer* layer = &engine->layers[index]; + if (layer->tilemap != NULL) + { + int ts; + for (ts = 0; ts < MAX_TILESETS; ts += 1) + { + TLN_Tileset tileset = layer->tilemap->tilesets[ts]; + if (tileset == NULL) + break; + + if (tileset->sp != NULL && ref_add(&tilesets, tileset)) + { + int c; + for (c = 0; c < tileset->sp->num_sequences; c += 1) + { + Animation* animation = &tileset->animations[c]; + if (animation->enabled && !animation->paused) + UpdateAnimation(animation, frame); + } + } + } + } + } /* frame callback */ - if (engine->frame) - engine->frame (time); + engine->line = 0; + if (engine->cb_frame) + engine->cb_frame (engine->frame); +} + +void TLN_UpdateFrame(int frame) +{ + BeginFrame(frame); + while (DrawScanline()) {} + TLN_SetLastError(TLN_ERR_OK); } -/*! - * \brief - * Returns the number of layers specified during initialisation - * - * \see - * TLN_Init() - */ int TLN_GetNumLayers (void) { TLN_SetLastError (TLN_ERR_OK); return engine->numlayers; } -/*! - * \brief - * Returns the number of sprites specified during initialisation - * - * \see - * TLN_Init() - */ - int TLN_GetNumSprites (void) { TLN_SetLastError (TLN_ERR_OK); return engine->numsprites; } -/*! - * \brief - * Specifies the address of the funcion to call for each drawn scanline - * - * \param callback - * Address of the function to call - * - * Tilengine renders its output line by line, just as the 2D graphics chips did. The - * raster callback is a way to simulate the "horizontal blanking interrupt" of those systems, - * where many parameters of the rendering can be modified per line. - * - * \remarks - * Setting a raster callback is optional, but much of the fun of using Tilengine comes from - * the use of raster effects - */ -void TLN_SetRasterCallback (void (*callback)(int)) +void TLN_SetRasterCallback (TLN_VideoCallback callback) { TLN_SetLastError (TLN_ERR_OK); - engine->raster = callback; + engine->cb_raster = callback; } -/*! - * \brief - * Specifies the address of the funcion to call for each drawn frame - * - * \param callback - * Address of the function to call - */ -void TLN_SetFrameCallback (void (*callback)(int)) +void TLN_SetFrameCallback (TLN_VideoCallback callback) { TLN_SetLastError (TLN_ERR_OK); - engine->frame = callback; + engine->cb_frame = callback; } -/*! - * \brief - * Sets the background color - * - * \param r - * red component (0-255) - * - * \param g - * green component (0-255) - * - * \param b - * blue component (0-255) - * - * The background color is the color of the pixel when there isn't any layer or sprite at - * that position. - * - * \remarks - * This funcion can be called during a raster callback to create gradient backgrounds - */ void TLN_SetBGColor (uint8_t r, uint8_t g, uint8_t b) { engine->bgcolor = PackRGB32 (r,g,b); } -/*! - * \brief - * Sets the background color from a Tilemap defined color - * - * \param tilemap - * Reference to the tilemap with the background color to set - */ bool TLN_SetBGColorFromTilemap (TLN_Tilemap tilemap) { if (CheckBaseObject (tilemap, OT_TILEMAP)) @@ -521,36 +442,16 @@ bool TLN_SetBGColorFromTilemap (TLN_Tilemap tilemap) return false; } -/*! - * \brief - * Disales background color rendering. If you know that the last background layer will always - * cover the entire screen, you can disable it to gain some performance - * \see - * TLN_SetBGColor() - */ void TLN_DisableBGColor (void) { engine->bgcolor = 0; } -/*! - * \brief - * Sets a static bitmap as background - * - * \param bitmap - * Reference to bitmap for the background. Set NULL to disable - * - * Sets an optional bitmap instead of a solid color where there is no layer or sprite. - * Unlike tilemaps or sprites, this bitmap cannot be moved and has no transparency - * - * \see - * TLN_SetBGPalette() - */ bool TLN_SetBGBitmap (TLN_Bitmap bitmap) { - if (bitmap) + if (bitmap != NULL) { - if (!CheckBaseObject (bitmap, OT_BITMAP)) + if (!CheckBaseObject(bitmap, OT_BITMAP)) return false; engine->bgpalette = bitmap->palette; } @@ -559,19 +460,9 @@ bool TLN_SetBGBitmap (TLN_Bitmap bitmap) return true; } -/*! - * \brief - * Changes the palette for the background bitmap - * - * \param palette - * Reference to palette - * - * \see - * TLN_SetBGBitmap() - */ bool TLN_SetBGPalette (TLN_Palette palette) { - if (!CheckBaseObject (palette, OT_PALETTE)) + if (!CheckBaseObject(palette, OT_PALETTE)) return false; engine->bgpalette = palette; @@ -579,21 +470,35 @@ bool TLN_SetBGPalette (TLN_Palette palette) return true; } -/*! - * \brief - * Sets custom blend function to use when BLEND_CUSTOM mode is selected - * \param blend_function - * pointer to a user-provided function that takes two parameters: source component intensity, - * destination component intensity, and returns the desired intensity. This function is - * called for each RGB component when blending is enabled - * \remarks - * This function is not called in realtime, but its result is precomputed into a look-up table - * when TLN_SetCustomBlendFunction() is called, so the performance impact is minimal, just as low - * as the other built-in blending modes - * \see - * TLN_SetSpriteBlendMode()|TLN_SetLayerBlendMode() - */ -void TLN_SetCustomBlendFunction (uint8_t (*blend_function)(uint8_t src, uint8_t dst)) +bool TLN_SetGlobalPalette(int index, TLN_Palette palette) +{ + if (index < 0 || index > NUM_PALETTES - 1) + { + TLN_SetLastError(TLN_ERR_IDX_PALETTE); + return false; + } + + if (palette != NULL && !CheckBaseObject(palette, OT_PALETTE)) + return false; + + engine->palettes[index] = palette; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +TLN_Palette TLN_GetGlobalPalette(int index) +{ + if (index < 0 || index > NUM_PALETTES - 1) + { + TLN_SetLastError(TLN_ERR_IDX_PALETTE); + return false; + } + + TLN_SetLastError(TLN_ERR_OK); + return engine->palettes[index]; +} + +void TLN_SetCustomBlendFunction (TLN_BlendFunction blend_function) { uint8_t* table = SelectBlendTable (BLEND_CUSTOM); int a,b; @@ -609,29 +514,12 @@ void TLN_SetCustomBlendFunction (uint8_t (*blend_function)(uint8_t src, uint8_t } } -/*! - * \brief - * Returns the number of objets used by the engine so far - * - * \remarks - * The objects is the total amount of tilesets, tilemaps, spritesets, palettes or sequences combined - * - * \see - * TLN_GetUsedMemory() - */ uint32_t TLN_GetNumObjects (void) { TLN_SetLastError (TLN_ERR_OK); return GetNumObjects (); } -/*! - * \brief - * Returns the total amount of memory used by the objects - * - * \see - * TLN_GetNumObjects() - */ uint32_t TLN_GetUsedMemory (void) { TLN_SetLastError (TLN_ERR_OK); @@ -658,18 +546,10 @@ const char* const errornames[] = "Resource file has invalid format", "A width or height parameter is invalid", "Unsupported function", + "Invalid ObjectList reference", + "Palette index out of range" }; -/*! - * \brief - * Sets the global error code of tilengine. Useful for custom loaders that need to set the error state. - * - * \param error - * Error code to set - * - * \see - * TLN_GetLastError() - */ void TLN_SetLastError (TLN_Error error) { if (check_context(engine)) @@ -680,13 +560,6 @@ void TLN_SetLastError (TLN_Error error) } } -/*! - * \brief - * Returns the last error after an invalid operation - * - * \see - * TLN_Error - */ TLN_Error TLN_GetLastError (void) { if (check_context(engine)) @@ -695,16 +568,6 @@ TLN_Error TLN_GetLastError (void) return TLN_ERR_NULL_POINTER; } -/*! - * \brief - * Returns the string description of the specified error code - * - * \param error - * Error code to get description - * - * \see - * TLN_GetLastError() - */ const char *TLN_GetErrorString (TLN_Error error) { if (error < TLN_MAX_ERR) diff --git a/src/Tileset.c b/src/Tileset.c index 7b7b22a0..5bffb720 100644 --- a/src/Tileset.c +++ b/src/Tileset.c @@ -14,6 +14,7 @@ #include #include +#include #include "Tilengine.h" #include "Tileset.h" #include "Palette.h" @@ -22,34 +23,6 @@ static bool HasTransparentPixels (uint8_t* src, int width); -/*! - * \brief - * Creates a new tileset - * - * \param numtiles - * Number of tiles that the tileset will hold - * - * \param width - * Width of each tile (must be multiple of 8) - * - * \param height - * Height of each tile (must be multiple of 8) - * - * \param palette - * Reference to the palette to assign - * - * \param sp - * Optional reference to the optional sequence pack with associated tileset animations, can be NULL - * - * \param attributes - * Optional array of attributes, one for each tile. Can be NULL - * - * \returns - * Reference to the created tileset, or NULL if error - * - * \see - * TLN_SetTilesetPixels() - */ TLN_Tileset TLN_CreateTileset (int numtiles, int width, int height, TLN_Palette palette, TLN_SequencePack sp, TLN_TileAttributes* attributes) { TLN_Tileset tileset; @@ -58,8 +31,6 @@ TLN_Tileset TLN_CreateTileset (int numtiles, int width, int height, TLN_Palette int c; int size; int size_tiles; - int size_color; - int size_attributes; for (c=0; c<=8; c++) { @@ -75,15 +46,16 @@ TLN_Tileset TLN_CreateTileset (int numtiles, int width, int height, TLN_Palette return NULL; } - numtiles++; size_tiles = width * height * numtiles; - size_color = height * numtiles; - size_attributes = sizeof(TLN_TileAttributes) * numtiles; - size = sizeof(struct Tileset) + size_tiles + size_color + size_attributes; + size = sizeof(struct Tileset) + size_tiles; tileset = (TLN_Tileset)CreateBaseObject (OT_TILESET, size); if (!tileset) + { + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); return NULL; + } + tileset->tstype = TILESET_TILES; tileset->width = width; tileset->height = height; tileset->hshift = hshift; @@ -91,44 +63,44 @@ TLN_Tileset TLN_CreateTileset (int numtiles, int width, int height, TLN_Palette tileset->hmask = width - 1; tileset->vmask = height - 1; tileset->numtiles = numtiles; - tileset->size_tiles = size_tiles; - tileset->size_color = size_color; tileset->palette = palette; tileset->sp = sp; - tileset->color_key = (bool*)(tileset->data + tileset->size_tiles); - tileset->attributes = (TLN_TileAttributes*)(tileset->data + tileset->size_tiles + tileset->size_color); + tileset->color_key = (bool*)calloc(numtiles, height); + tileset->attributes = (TLN_TileAttributes*)calloc(numtiles, sizeof(TLN_TileAttributes)); if (attributes != NULL) - memcpy (tileset->attributes, attributes, size_attributes); + memcpy (tileset->attributes, attributes, numtiles * sizeof(TLN_TileAttributes)); + tileset->tiles = (uint16_t*)calloc(numtiles, sizeof(uint16_t)); + for (c = 0; c < numtiles; c += 1) + tileset->tiles[c] = c; + + /* create animations */ + if (sp != NULL) + tileset->animations = (Animation*)calloc(sp->num_sequences, sizeof(Animation)); TLN_SetLastError (TLN_ERR_OK); return tileset; } -/*! - * \brief - * Sets pixel data for a tile in a tileset - * - * \param tileset - * Reference to the tileset - * - * \param entry - * Number of tile to set [0, num_tiles - 1] - * - * \param srcdata - * Pointer to pixel data to set - * - * \param srcpitch - * Bytes per line of source data - * - * \returns - * true if success, or false if error - * - * \remarks - * Care must be taken in providing pixel data and pitch as it can crash the aplication - * - * \see - * TLN_CreateTileset() - */ +TLN_Tileset TLN_CreateImageTileset(int numtiles, TLN_TileImage* images) +{ + TLN_Tileset tileset; + const int images_size = numtiles * sizeof(TLN_TileImage); + const int size = sizeof(struct Tileset) + images_size; + + tileset = (TLN_Tileset)CreateBaseObject(OT_TILESET, size); + if (tileset == NULL) + { + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); + return NULL; + } + + tileset->tstype = TILESET_IMAGES; + tileset->numtiles = numtiles; + tileset->images = (TLN_TileImage*)tileset->data; + memcpy(tileset->images, images, images_size); + return tileset; +} + bool TLN_SetTilesetPixels (TLN_Tileset tileset, int entry, uint8_t* srcdata, int srcpitch) { int c, line; @@ -137,9 +109,9 @@ bool TLN_SetTilesetPixels (TLN_Tileset tileset, int entry, uint8_t* srcdata, int if (!CheckBaseObject (tileset, OT_TILESET)) return false; - if (entry<1 || entry>tileset->numtiles) + if (tileset->tstype != TILESET_TILES || entry>tileset->numtiles) { - TLN_SetLastError (TLN_ERR_IDX_PICTURE); + TLN_SetLastError(TLN_ERR_IDX_PICTURE); return false; } @@ -157,19 +129,6 @@ bool TLN_SetTilesetPixels (TLN_Tileset tileset, int entry, uint8_t* srcdata, int return true; } -/*! - * \brief - * Creates a duplicate of the specified tileset and its associated palette - * - * \param src - * Tileset to clone - * - * \returns - * A reference to the newly cloned tileset, or NULL if error - * - * \see - * TLN_LoadTileset() - */ TLN_Tileset TLN_CloneTileset (TLN_Tileset src) { TLN_Tileset tileset; @@ -178,32 +137,36 @@ TLN_Tileset TLN_CloneTileset (TLN_Tileset src) return NULL; tileset = (TLN_Tileset)CloneBaseObject (src); - if (tileset) + if (tileset == NULL) + return NULL; + + const int size_tiles = src->numtiles * sizeof(uint16_t); + const int size_color = src->numtiles * src->height; + const int size_attributes = src->numtiles * sizeof(TLN_TileAttributes); + + tileset->tiles = (uint16_t*)malloc(size_tiles); + tileset->color_key = (bool*)malloc(size_color); + tileset->attributes = (TLN_TileAttributes*)malloc(size_attributes); + + if (tileset->tiles == NULL || tileset->color_key == NULL || tileset->attributes == NULL) { - TLN_SetLastError (TLN_ERR_OK); - tileset->color_key = (bool*)(tileset->data + tileset->size_tiles); - tileset->attributes = (TLN_TileAttributes*)(tileset->data + tileset->size_tiles + tileset->size_color); - return tileset; - } - else + TLN_DeleteTileset(tileset); + TLN_SetLastError(TLN_ERR_OUT_OF_MEMORY); return NULL; + } + + memcpy(tileset->tiles, src->tiles, size_tiles); + memcpy(tileset->color_key, src->color_key, size_color); + memcpy(tileset->attributes, src->attributes, size_attributes); + TLN_SetLastError(TLN_ERR_OK); + return tileset; } -/*! - * \brief - * Deletes the specified tileset and frees memory - * - * \param tileset - * Tileset to delete - * - * \remarks - * Don't delete a tileset currently attached to a layer! - * - * \see - * TLN_LoadTileset(), TLN_CloneTileset() - */ bool TLN_DeleteTileset (TLN_Tileset tileset) { + // TODO: implement refcount on cached tilesets, avoid deleting a cached instance + return true; + if (CheckBaseObject (tileset, OT_TILESET)) { if (ObjectOwner (tileset)) @@ -211,6 +174,12 @@ bool TLN_DeleteTileset (TLN_Tileset tileset) TLN_DeletePalette (tileset->palette); TLN_DeleteSequencePack (tileset->sp); } + free(tileset->tiles); + free(tileset->color_key); + free(tileset->attributes); + if (tileset->animations) + free(tileset->animations); + DeleteBaseObject (tileset); TLN_SetLastError (TLN_ERR_OK); return true; @@ -219,16 +188,6 @@ bool TLN_DeleteTileset (TLN_Tileset tileset) return false; } -/*! - * \brief - * Returns the width in pixels of each individual tile in the tileset - * - * \param tileset - * Reference to the tileset to get info from - * - * \see - * TLN_GetTileHeight() - */ int TLN_GetTileWidth (TLN_Tileset tileset) { if (CheckBaseObject (tileset, OT_TILESET)) @@ -240,16 +199,6 @@ int TLN_GetTileWidth (TLN_Tileset tileset) return 0; } -/*! - * \brief - * Returns the height in pixels of each individual tile in the tileset - * - * \param tileset - * Reference to the tileset to get info from - * - * \see - * TLN_GetTileWidth() - */ int TLN_GetTileHeight (TLN_Tileset tileset) { if (CheckBaseObject (tileset, OT_TILESET)) @@ -261,13 +210,6 @@ int TLN_GetTileHeight (TLN_Tileset tileset) return 0; } -/*! - * \brief - * Returns the number of different tiles in tileset - * - * \param tileset - * Reference to the tileset to get info from - */ int TLN_GetTilesetNumTiles(TLN_Tileset tileset) { if (CheckBaseObject(tileset, OT_TILESET)) @@ -279,21 +221,6 @@ int TLN_GetTilesetNumTiles(TLN_Tileset tileset) return 0; } -/*! - * \brief - * Returns a reference to the palette associated to the specified tileset - * - * \param tileset - * Reference to the tileset to get the palette - * - * \remarks - * The palette of a tileset is created at load time and cannot be modified. When TLN_SetLayer - * function is used to attach a tileset to a layer, the palette associated with the specified tileset is automatically - * assigned to that layer, but it can be later replaced with TLN_SetLayerPalette - * - * \see - * TLN_LoadTileset(), TLN_SetLayerPalette() - */ TLN_Palette TLN_GetTilesetPalette (TLN_Tileset tileset) { if (CheckBaseObject (tileset, OT_TILESET)) @@ -305,16 +232,6 @@ TLN_Palette TLN_GetTilesetPalette (TLN_Tileset tileset) return NULL; } -/*! - * \brief - * Returns a reference to the optional sequence pack associated to the specified tileset - * - * \param tileset - * Reference to the tileset to get the palette - * - * \see - * TLN_LoadTileset(), TLN_CreateTileset() - */ TLN_SequencePack TLN_GetTilesetSequencePack (TLN_Tileset tileset) { if (CheckBaseObject (tileset, OT_TILESET)) @@ -326,51 +243,24 @@ TLN_SequencePack TLN_GetTilesetSequencePack (TLN_Tileset tileset) return NULL; } -/*! - * \brief - * Copies tile graphic data inside a Tileset specified tileset - * - * \param tileset - * Reference to the tileset to get the palette - * - * \param src - * index of source tile - * - * \param dst - * index of target tile - * - */ -bool TLN_CopyTile (TLN_Tileset tileset, int src, int dst) +TLNAPI int TLN_GetTilesetNumAnimations(TLN_Tileset tileset) { - uint8_t* srcdata; - uint8_t* dstdata; - int tilesize; - - if (!CheckBaseObject (tileset, OT_TILESET)) - return false; + return CheckBaseObject(tileset, OT_TILESET) && tileset->sp != NULL ? tileset->sp->num_sequences : 0; +} - if (src>=tileset->numtiles) - { - TLN_SetLastError (TLN_ERR_IDX_PICTURE); - return false; - } +/* for image-based tilesets: returns bitmap with matching tileid */ +TLN_Bitmap GetTilesetBitmap(TLN_Tileset tileset, int tileid) +{ + int c; + if (!CheckBaseObject(tileset, OT_TILESET) || tileset->tstype != TILESET_IMAGES) + return NULL; - if (dst>=tileset->numtiles) + for (c = 0; c < tileset->numtiles; c += 1) { - TLN_SetLastError (TLN_ERR_IDX_PICTURE); - return false; + if (tileset->images[c].id == tileid) + return tileset->images[c].bitmap; } - - src += 1; - dst += 1; - tilesize = tileset->width * tileset->height; - srcdata = tileset->data + (src * tilesize); - dstdata = tileset->data + (dst * tilesize); - memcpy (dstdata, srcdata, tilesize); - memcpy (&tileset->color_key[dst*tileset->height], &tileset->color_key[src*tileset->height], tileset->height); - - TLN_SetLastError (TLN_ERR_OK); - return true; + return NULL; } /* devuelve si la lํnea usa color key */ diff --git a/src/Tileset.h b/src/Tileset.h index 89b3b479..b2948ff4 100644 --- a/src/Tileset.h +++ b/src/Tileset.h @@ -14,25 +14,40 @@ #include "Object.h" #include "Palette.h" #include "SequencePack.h" +#include "Bitmap.h" +#include "Animation.h" -/* set de tiles */ +/* types of tilesets */ +typedef enum +{ + TILESET_NONE, + TILESET_TILES, + TILESET_IMAGES, +} +TilesetType; + +/* Tileset definition */ struct Tileset { DEFINE_OBJECT; - int numtiles; /* nบ de tiles en el set */ - int width; /* tama๑o horizontal de un tile */ - int height; /* tama๑o vertical de un tile */ - int hshift; /* tipo horizontal */ - int vshift; /* tipo vertical */ - int hmask; /* mascara horizontal */ - int vmask; /* mascara vertical */ - int size_tiles; /* tama๑o de la secci๓n de tiles */ - int size_color; /* tama๑o de la secci๓n de color key */ - struct Palette* palette; /* paleta original */ - struct SequencePack* sp; /* secuencias asociadas (si hay) */ - bool* color_key; /* puntero a array indicando si cada lํnea tiene color key */ - TLN_TileAttributes* attributes; /* puntero a array de atributos, uno por tile */ - uint8_t data[]; + TilesetType tstype; /* tileset type */ + int numtiles; /* number of tiles */ + int width; /* horizontal tile size */ + int height; /* vertical tile size */ + int hshift; /* horizontal shift */ + int vshift; /* vertical shift */ + int hmask; /* horizontal bitmask */ + int vmask; /* vertical bitmask */ + int size_tiles; /* size of tiles collection section */ + int tiles_per_row; /* number of tiles per row */ + TLN_Palette palette; /* palette */ + TLN_SequencePack sp; /* associated sequences (if any) */ + Animation* animations; /* active tile animations */ + TLN_TileImage* images; /* image tiles array */ + TLN_TileAttributes* attributes; /* attribute array */ + bool* color_key; /* array telling if each line has color key or is solid */ + uint16_t* tiles; /* tile indexes for animation */ + uint8_t data[]; /* variable size data for images[], attributes[], color_key[] and pixels */ }; #define GetTilesetLine(tileset,index,y) \ @@ -41,4 +56,6 @@ struct Tileset #define GetTilesetPixel(tileset,index,x,y) \ tileset->data[(((index << tileset->vshift) + y) << tileset->hshift) + x] +TLN_Bitmap GetTilesetBitmap(TLN_Tileset tileset, int tileid); + #endif diff --git a/src/World.c b/src/World.c new file mode 100644 index 00000000..a962d587 --- /dev/null +++ b/src/World.c @@ -0,0 +1,165 @@ +#include "Tilengine.h" +#include "Engine.h" +#include "Layer.h" +#include "Sprite.h" +#include "LoadTMX.h" +#include "Palette.h" + +#define MAX_TMX_ITEM 100 + +/* info for current world */ +static TMXInfo tmxinfo; +static int first; + +bool TLN_LoadWorld(const char* filename, int first_layer) +{ + int c; + + if (!TMXLoad(filename, &tmxinfo)) + return NULL; + + if (tmxinfo.num_layers > MAX_TMX_ITEM) + tmxinfo.num_layers = MAX_TMX_ITEM; + + /* load and assign each layer type */ + first = first_layer; + for (c = 0; c < tmxinfo.num_layers; c += 1) + { + TMXLayer* tmxlayer = &tmxinfo.layers[c]; + const int layerindex = tmxinfo.num_layers - c - 1 + first; + switch (tmxlayer->type) + { + case LAYER_NONE: + break; + + case LAYER_TILE: + { + TLN_Tilemap tilemap = TLN_LoadTilemap(filename, tmxlayer->name); + TLN_SetLayerTilemap(layerindex, tilemap); + } + break; + + case LAYER_OBJECT: + { + TLN_ObjectList objectlist = TLN_LoadObjectList(filename, tmxlayer->name); + TLN_SetLayerObjects(layerindex, objectlist, NULL); + } + break; + + case LAYER_BITMAP: + { + TLN_Bitmap bitmap = TLN_LoadBitmap(tmxlayer->image); + TLN_SetLayerBitmap(layerindex, bitmap); + } + break; + } + + /* direct set of layer properties */ + Layer* layer = GetLayer(layerindex); + layer->world.xfactor = tmxlayer->parallaxx; + layer->world.yfactor = tmxlayer->parallaxy; + layer->world.offsetx = (int)tmxlayer->offsetx; + layer->world.offsety = (int)tmxlayer->offsety; + + /* opacity selects blend mode */ + if (tmxlayer->visible) + { + int opacity = (int)(tmxlayer->opacity * 100); + if (opacity > 87) + TLN_SetLayerBlendMode(layerindex, BLEND_NONE, 0); + else if (opacity > 62) + TLN_SetLayerBlendMode(layerindex, BLEND_MIX75, 0); + else if (opacity > 37) + TLN_SetLayerBlendMode(layerindex, BLEND_MIX50, 0); + else if (opacity > 12) + TLN_SetLayerBlendMode(layerindex, BLEND_MIX25, 0); + } + else + TLN_DisableLayer(layerindex); + } + + /* sets background color if defined */ + if (tmxinfo.bgcolor != 0) + { + Color bgcolor; + bgcolor.value = tmxinfo.bgcolor; + TLN_SetBGColor(bgcolor.r, bgcolor.g, bgcolor.b); + } + else + TLN_DisableBGColor(); + return true; +} + +void TLN_ReleaseWorld(void) +{ + int c; + + for (c = 0; c < tmxinfo.num_layers; c += 1) + { + TMXLayer* tmxlayer = &tmxinfo.layers[c]; + const int layerindex = tmxinfo.num_layers - c - 1 + first; + + Layer* layer = GetLayer(layerindex); + layer->ok = false; + switch (tmxlayer->type) + { + case LAYER_NONE: + break; + + case LAYER_TILE: + TLN_DeleteTilemap(layer->tilemap); + break; + + case LAYER_OBJECT: + TLN_DeleteObjectList(layer->objects); + break; + + case LAYER_BITMAP: + TLN_DeleteBitmap(layer->bitmap); + break; + } + } +} + +bool TLN_SetLayerParallaxFactor(int nlayer, float x, float y) +{ + Layer *layer; + if (nlayer >= engine->numlayers) + { + TLN_SetLastError(TLN_ERR_IDX_LAYER); + return false; + } + + layer = &engine->layers[nlayer]; + layer->world.xfactor = x; + layer->world.yfactor = y; + layer->dirty = true; + TLN_SetLastError(TLN_ERR_OK); + return true; +} + +void TLN_SetWorldPosition(int x, int y) +{ + engine->xworld = x; + engine->yworld = y; + engine->dirty = true; +} + +bool TLN_SetSpriteWorldPosition(int nsprite, int x, int y) +{ + Sprite *sprite; + if (nsprite >= engine->numsprites) + { + TLN_SetLastError(TLN_ERR_IDX_SPRITE); + return false; + } + + sprite = &engine->sprites[nsprite]; + sprite->xworld = x; + sprite->yworld = y; + sprite->world_space = true; + sprite->dirty = true; + + TLN_SetLastError(TLN_ERR_OK); + return true; +} diff --git a/src/aes.h b/src/aes.h index 429afe19..ef545a32 100644 --- a/src/aes.h +++ b/src/aes.h @@ -13,7 +13,11 @@ #include #include -/****************************** MACROS ******************************/ +#ifdef __cplusplus +extern "C" { +#endif + + /****************************** MACROS ******************************/ #define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time /*********************** FUNCTION DECLARATIONS **********************/ @@ -21,80 +25,80 @@ // AES /////////////////// // Key setup must be done before any AES en/de-cryption functions can be used. -void aes_key_setup(const uint8_t key[], // The key, must be 128, 192, or 256 bits - uint32_t w[], // Output key schedule to be used later - int keysize); // Bit length of the key, 128, 192, or 256 + void aes_key_setup(const uint8_t key[], // The key, must be 128, 192, or 256 bits + uint32_t w[], // Output key schedule to be used later + int keysize); // Bit length of the key, 128, 192, or 256 -void aes_encrypt(const uint8_t in[], // 16 bytes of plaintext - uint8_t out[], // 16 bytes of ciphertext - const uint32_t key[], // From the key setup - int keysize); // Bit length of the key, 128, 192, or 256 + void aes_encrypt(const uint8_t in[], // 16 bytes of plaintext + uint8_t out[], // 16 bytes of ciphertext + const uint32_t key[], // From the key setup + int keysize); // Bit length of the key, 128, 192, or 256 -void aes_decrypt(const uint8_t in[], // 16 bytes of ciphertext - uint8_t out[], // 16 bytes of plaintext - const uint32_t key[], // From the key setup - int keysize); // Bit length of the key, 128, 192, or 256 + void aes_decrypt(const uint8_t in[], // 16 bytes of ciphertext + uint8_t out[], // 16 bytes of plaintext + const uint32_t key[], // From the key setup + int keysize); // Bit length of the key, 128, 192, or 256 /////////////////// // AES - CBC /////////////////// -int aes_encrypt_cbc(const uint8_t in[], // Plaintext - uint32_t in_len, // Must be a multiple of AES_BLOCK_SIZE - uint8_t out[], // Ciphertext, same length as plaintext - const uint32_t key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long - -int aes_decrypt_cbc(const uint8_t in[], // Cyphertext - uint32_t in_len, // Must be a multiple of AES_BLOCK_SIZE - uint8_t out[], // Plaintext, same length as cyphertext - const uint32_t key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long + int aes_encrypt_cbc(const uint8_t in[], // Plaintext + uint32_t in_len, // Must be a multiple of AES_BLOCK_SIZE + uint8_t out[], // Ciphertext, same length as plaintext + const uint32_t key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long + + int aes_decrypt_cbc(const uint8_t in[], // Cyphertext + uint32_t in_len, // Must be a multiple of AES_BLOCK_SIZE + uint8_t out[], // Plaintext, same length as cyphertext + const uint32_t key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long // Only output the CBC-MAC of the input. -int aes_encrypt_cbc_mac(const uint8_t in[], // plaintext - uint32_t in_len, // Must be a multiple of AES_BLOCK_SIZE - uint8_t out[], // Output MAC - const uint32_t key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long + int aes_encrypt_cbc_mac(const uint8_t in[], // plaintext + uint32_t in_len, // Must be a multiple of AES_BLOCK_SIZE + uint8_t out[], // Output MAC + const uint32_t key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long /////////////////// // AES - CTR /////////////////// -void increment_iv(uint8_t iv[], // Must be a multiple of AES_BLOCK_SIZE - int counter_size); // Bytes of the IV used for counting (low end) - -void aes_encrypt_ctr(const uint8_t in[], // Plaintext - uint32_t in_len, // Any byte length - uint8_t out[], // Ciphertext, same length as plaintext - const uint32_t key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long - -void aes_decrypt_ctr(const uint8_t in[], // Ciphertext - uint32_t in_len, // Any byte length - uint8_t out[], // Plaintext, same length as ciphertext - const uint32_t key[], // From the key setup - int keysize, // Bit length of the key, 128, 192, or 256 - const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long + void increment_iv(uint8_t iv[], // Must be a multiple of AES_BLOCK_SIZE + int counter_size); // Bytes of the IV used for counting (low end) + + void aes_encrypt_ctr(const uint8_t in[], // Plaintext + uint32_t in_len, // Any byte length + uint8_t out[], // Ciphertext, same length as plaintext + const uint32_t key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long + + void aes_decrypt_ctr(const uint8_t in[], // Ciphertext + uint32_t in_len, // Any byte length + uint8_t out[], // Plaintext, same length as ciphertext + const uint32_t key[], // From the key setup + int keysize, // Bit length of the key, 128, 192, or 256 + const uint8_t iv[]); // IV, must be AES_BLOCK_SIZE bytes long /////////////////// // AES - CCM /////////////////// // Returns True if the input parameters do not violate any constraint. -int aes_encrypt_ccm(const uint8_t plaintext[], // IN - Plaintext. - uint32_t plaintext_len, // IN - Plaintext length. - const uint8_t associated_data[], // IN - Associated Data included in authentication, but not encryption. - unsigned short associated_data_len, // IN - Associated Data length in bytes. - const uint8_t nonce[], // IN - The Nonce to be used for encryption. - unsigned short nonce_len, // IN - Nonce length in bytes. - uint8_t ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC. - uint32_t *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len. - uint32_t mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16. - const uint8_t key[], // IN - The AES key for encryption. - int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256. + int aes_encrypt_ccm(const uint8_t plaintext[], // IN - Plaintext. + uint32_t plaintext_len, // IN - Plaintext length. + const uint8_t associated_data[], // IN - Associated Data included in authentication, but not encryption. + unsigned short associated_data_len, // IN - Associated Data length in bytes. + const uint8_t nonce[], // IN - The Nonce to be used for encryption. + unsigned short nonce_len, // IN - Nonce length in bytes. + uint8_t ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC. + uint32_t *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len. + uint32_t mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16. + const uint8_t key[], // IN - The AES key for encryption. + int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256. // Returns True if the input parameters do not violate any constraint. // Use mac_auth to ensure decryption/validation was preformed correctly. @@ -102,26 +106,30 @@ int aes_encrypt_ccm(const uint8_t plaintext[], // IN - Plaintext. // this, call with mac_auth = NULL. The proper proceedure is to decrypt with // authentication enabled (mac_auth != NULL) and make a second call to that // ignores authentication explicitly if the first call failes. -int aes_decrypt_ccm(const uint8_t ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC. - uint32_t ciphertext_len, // IN - Ciphertext length in bytes. - const uint8_t assoc[], // IN - The Associated Data, required for authentication. - unsigned short assoc_len, // IN - Associated Data length in bytes. - const uint8_t nonce[], // IN - The Nonce to use for decryption, same one as for encryption. - unsigned short nonce_len, // IN - Nonce length in bytes. - uint8_t plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - mac_len. - uint32_t *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len . - uint32_t mac_len, // IN - The length of the MAC that was calculated. - int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the authentication. - const uint8_t key[], // IN - The AES key for decryption. - int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256. + int aes_decrypt_ccm(const uint8_t ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC. + uint32_t ciphertext_len, // IN - Ciphertext length in bytes. + const uint8_t assoc[], // IN - The Associated Data, required for authentication. + unsigned short assoc_len, // IN - Associated Data length in bytes. + const uint8_t nonce[], // IN - The Nonce to use for decryption, same one as for encryption. + unsigned short nonce_len, // IN - Nonce length in bytes. + uint8_t plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - mac_len. + uint32_t *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len . + uint32_t mac_len, // IN - The length of the MAC that was calculated. + int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the authentication. + const uint8_t key[], // IN - The AES key for decryption. + int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256. /////////////////// // Test functions /////////////////// -int aes_test(); -int aes_ecb_test(); -int aes_cbc_test(); -int aes_ctr_test(); -int aes_ccm_test(); + int aes_test(); + int aes_ecb_test(); + int aes_cbc_test(); + int aes_ctr_test(); + int aes_ccm_test(); + +#ifdef __cplusplus +} +#endif #endif // AES_H diff --git a/src/cJSON.c b/src/cJSON.c new file mode 100644 index 00000000..cbdec413 --- /dev/null +++ b/src/cJSON.c @@ -0,0 +1,2932 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { + if (!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 7) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +static void *internal_malloc(size_t size) +{ + return malloc(size); +} +static void internal_free(void *pointer) +{ + free(pointer); +} +static void *internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return false; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/src/cJSON.h b/src/cJSON.h new file mode 100644 index 00000000..6e0bde93 --- /dev/null +++ b/src/cJSON.h @@ -0,0 +1,277 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 7 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type __stdcall +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall +#endif +#else /* !WIN32 */ +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check if the item is a string and return its valuestring */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/arrray that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Hash.c b/src/crc32.c similarity index 96% rename from src/Hash.c rename to src/crc32.c index 0cf734e7..8afe5304 100644 --- a/src/Hash.c +++ b/src/crc32.c @@ -40,9 +40,9 @@ * CRC32 code derived from work by Gary S. Brown. */ -#include "Hash.h" +#include "crc32.h" -static const unsigned int crc32_tab[] = +static const unsigned int _crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, @@ -89,7 +89,7 @@ static const unsigned int crc32_tab[] = 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; -hash_t hash (unsigned int crc, const void *buf, size_t size) +unsigned int _crc32 (unsigned int crc, const void *buf, size_t size) { unsigned char* p; @@ -97,7 +97,7 @@ hash_t hash (unsigned int crc, const void *buf, size_t size) crc = crc ^ ~0U; while (size--) - crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + crc = _crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); crc ^= ~0U; return crc; diff --git a/src/crc32.h b/src/crc32.h new file mode 100644 index 00000000..e87266d4 --- /dev/null +++ b/src/crc32.h @@ -0,0 +1,21 @@ +/*- + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ + +#ifndef _CRC32_H +#define _CRC32_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + unsigned int _crc32(unsigned int crc, const void *buf, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libretro/libretro.c b/src/libretro/libretro.c index 78007c74..a5db4575 100644 --- a/src/libretro/libretro.c +++ b/src/libretro/libretro.c @@ -15,6 +15,8 @@ #ifdef WIN32 #include #define chdir SetCurrentDirectoryA +#else +#include #endif static int VIDEO_WIDTH = 480; @@ -246,15 +248,30 @@ bool retro_load_game(const struct retro_game_info *info) check_variables(); /* load "game.lua" and parse */ - if (info) + if (info && info->path) chdir(info->path); retval = luaL_loadfile(L, "game.lua"); log_cb(RETRO_LOG_INFO, "loading game.lua: %d\n", retval); + if (retval != 0) + { + log_cb(RETRO_LOG_ERROR, "%s\n", lua_tostring(L, -1)); + return false; + } retval = lua_pcall(L, 0, 0, 0); log_cb(RETRO_LOG_INFO, "init script: %d\n", retval); - + if (retval != 0) + { + log_cb(RETRO_LOG_ERROR, "%s\n", lua_tostring(L, -1)); + return false; + } + /* get config{} struct from lua */ lua_getglobal(L, "config"); + if (!lua_istable(L, -1)) + { + log_cb(RETRO_LOG_ERROR, "game.lua did not define config{}\n"); + return false; + } VIDEO_WIDTH = getIntField(L, "hres"); VIDEO_HEIGHT = getIntField(L, "vres"); numlayers = getIntField(L, "numlayers"); diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 00000000..01375738 --- /dev/null +++ b/src/md5.c @@ -0,0 +1,296 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSS's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#ifndef HAVE_OPENSSL + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. + */ +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void MD5_Init(MD5_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +void MD5_Final(unsigned char *result, MD5_CTX *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 00000000..03a2d963 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,51 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/tilengine_libretro.vcxproj b/src/tilengine_libretro.vcxproj index 3f8bac22..848408a4 100644 --- a/src/tilengine_libretro.vcxproj +++ b/src/tilengine_libretro.vcxproj @@ -152,10 +152,12 @@ + + - + @@ -163,7 +165,9 @@ + + @@ -178,6 +182,7 @@ + @@ -186,14 +191,19 @@ + + + - + + + @@ -210,4 +220,4 @@ - \ No newline at end of file + diff --git a/src/tilengine_libretro.vcxproj.filters b/src/tilengine_libretro.vcxproj.filters index 3adf2c11..08a104ae 100644 --- a/src/tilengine_libretro.vcxproj.filters +++ b/src/tilengine_libretro.vcxproj.filters @@ -30,15 +30,24 @@ Archivos de origen - + Archivos de origen - + + Archivos de origen + + Archivos de origen Archivos de origen + + Archivos de origen + + + Archivos de origen + Archivos de origen @@ -60,9 +69,15 @@ Archivos de origen + + Archivos de origen + Archivos de origen + + Archivos de origen + Archivos de origen @@ -105,11 +120,14 @@ Archivos de origen - + Archivos de origen + + Archivos de encabezado + Archivos de encabezado @@ -125,6 +143,15 @@ Archivos de encabezado + + Archivos de encabezado + + + Archivos de encabezado + + + Archivos de encabezado + Archivos de encabezado @@ -134,18 +161,27 @@ Archivos de encabezado - + + Archivos de encabezado + + Archivos de encabezado - + Archivos de encabezado Archivos de encabezado + + Archivos de encabezado + Archivos de encabezado + + Archivos de encabezado + Archivos de encabezado @@ -182,11 +218,5 @@ Archivos de encabezado - - Archivos de encabezado - - - Archivos de encabezado - - \ No newline at end of file + diff --git a/src/upx.exe b/src/upx.exe deleted file mode 100644 index adf956c5..00000000 Binary files a/src/upx.exe and /dev/null differ