commit cc618ba61c349c42b85ac40796448d5970982d19 Author: bzt Date: Fri Nov 29 14:31:39 2019 +0100 Initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c3a1a70 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ + Copyright (C) 2019 bzt (bztsrc@gitlab) + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ff5928f --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +Minetest MTS Editor +=================== + +This is a simple editor for [Minetest](https://www.minetest.net) Schematic files +([MTS](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/mts_format.md)) + +``` +MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license + +./mtsedit [-d|-p|-P|-g] [-m map] <.mts|.schematic> [out.mts] + + -d: dump layers to output + -p: save preview + -P: preview structure cut in half + -g: generate slab and stairs from block image + -m map: replace block type mapping + out.mts: output to file + +``` + +Batch Mode +---------- + +You can convert MTS files and generate preview images for them from script with this tool. +[Read more](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/usage.md). + +Interactive Mode +---------------- + +A picture can be more informative than thousand words. [Read more](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/usage.md). + + + +Compilation +----------- + +It is very simple, written in ANSI C. The one and only dependency it has is the SDL2 library. Just issue `make` in the "src" +directory, and that will autodetect your operating system (Linux, BSD, MacOSX and Windows with mingw). + +It also needs some [data files](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/blocks.md), which can be found +[here](https://gitlab.com/bztsrc/mtsedit/tree/master/data). + +bzt diff --git a/data/Andesite_00.png b/data/Andesite_00.png new file mode 100644 index 0000000..7c5a735 Binary files /dev/null and b/data/Andesite_00.png differ diff --git a/data/Bed_Red_00.png b/data/Bed_Red_00.png new file mode 100644 index 0000000..59e6f0e Binary files /dev/null and b/data/Bed_Red_00.png differ diff --git a/data/Bed_Red_01.png b/data/Bed_Red_01.png new file mode 100644 index 0000000..4cd6d8a Binary files /dev/null and b/data/Bed_Red_01.png differ diff --git a/data/Bedrock_00.png b/data/Bedrock_00.png new file mode 100644 index 0000000..51aa3ea Binary files /dev/null and b/data/Bedrock_00.png differ diff --git a/data/Bookshelf_00.png b/data/Bookshelf_00.png new file mode 100644 index 0000000..505647d Binary files /dev/null and b/data/Bookshelf_00.png differ diff --git a/data/Brewing_Stand_00.png b/data/Brewing_Stand_00.png new file mode 100644 index 0000000..f42cf36 Binary files /dev/null and b/data/Brewing_Stand_00.png differ diff --git a/data/Brick_Block_00.png b/data/Brick_Block_00.png new file mode 100644 index 0000000..50a63af Binary files /dev/null and b/data/Brick_Block_00.png differ diff --git a/data/Cactus_00.png b/data/Cactus_00.png new file mode 100644 index 0000000..ab5a62e Binary files /dev/null and b/data/Cactus_00.png differ diff --git a/data/Carpet_White_00.png b/data/Carpet_White_00.png new file mode 100644 index 0000000..36e5f44 Binary files /dev/null and b/data/Carpet_White_00.png differ diff --git a/data/Cauldron_00.png b/data/Cauldron_00.png new file mode 100644 index 0000000..8a55601 Binary files /dev/null and b/data/Cauldron_00.png differ diff --git a/data/Chest_00.png b/data/Chest_00.png new file mode 100644 index 0000000..9ef3665 Binary files /dev/null and b/data/Chest_00.png differ diff --git a/data/Chest_01.png b/data/Chest_01.png new file mode 100644 index 0000000..aa3e863 Binary files /dev/null and b/data/Chest_01.png differ diff --git a/data/Cobblestone_00.png b/data/Cobblestone_00.png new file mode 100644 index 0000000..0834ad4 Binary files /dev/null and b/data/Cobblestone_00.png differ diff --git a/data/Cobblestone_Slab_00.png b/data/Cobblestone_Slab_00.png new file mode 100644 index 0000000..d605644 Binary files /dev/null and b/data/Cobblestone_Slab_00.png differ diff --git a/data/Cobblestone_Stair_00.png b/data/Cobblestone_Stair_00.png new file mode 100644 index 0000000..807f62d Binary files /dev/null and b/data/Cobblestone_Stair_00.png differ diff --git a/data/Cobblestone_Stair_01.png b/data/Cobblestone_Stair_01.png new file mode 100644 index 0000000..4332b83 Binary files /dev/null and b/data/Cobblestone_Stair_01.png differ diff --git a/data/Cobblestone_Stair_02.png b/data/Cobblestone_Stair_02.png new file mode 100644 index 0000000..e314e8f Binary files /dev/null and b/data/Cobblestone_Stair_02.png differ diff --git a/data/Cobblestone_Stair_03.png b/data/Cobblestone_Stair_03.png new file mode 100644 index 0000000..6093e50 Binary files /dev/null and b/data/Cobblestone_Stair_03.png differ diff --git a/data/Cobblestone_Wall_00.png b/data/Cobblestone_Wall_00.png new file mode 100644 index 0000000..9334f03 Binary files /dev/null and b/data/Cobblestone_Wall_00.png differ diff --git a/data/Cobweb_00.png b/data/Cobweb_00.png new file mode 100644 index 0000000..26b2593 Binary files /dev/null and b/data/Cobweb_00.png differ diff --git a/data/Crafting_Table_00.png b/data/Crafting_Table_00.png new file mode 100644 index 0000000..8989823 Binary files /dev/null and b/data/Crafting_Table_00.png differ diff --git a/data/Crafting_Table_01.png b/data/Crafting_Table_01.png new file mode 100644 index 0000000..8ba1930 Binary files /dev/null and b/data/Crafting_Table_01.png differ diff --git a/data/Dandelion_00.png b/data/Dandelion_00.png new file mode 100644 index 0000000..60886c1 Binary files /dev/null and b/data/Dandelion_00.png differ diff --git a/data/Dirt_00.png b/data/Dirt_00.png new file mode 100644 index 0000000..48fb3d2 Binary files /dev/null and b/data/Dirt_00.png differ diff --git a/data/End_Portal_Frame_00.png b/data/End_Portal_Frame_00.png new file mode 100644 index 0000000..3ad3e28 Binary files /dev/null and b/data/End_Portal_Frame_00.png differ diff --git a/data/Fence_00.png b/data/Fence_00.png new file mode 100644 index 0000000..930ad7e Binary files /dev/null and b/data/Fence_00.png differ diff --git a/data/Fence_01.png b/data/Fence_01.png new file mode 100644 index 0000000..b1770a0 Binary files /dev/null and b/data/Fence_01.png differ diff --git a/data/Furnace_00.png b/data/Furnace_00.png new file mode 100644 index 0000000..583eb45 Binary files /dev/null and b/data/Furnace_00.png differ diff --git a/data/Furnace_01.png b/data/Furnace_01.png new file mode 100644 index 0000000..17d1417 Binary files /dev/null and b/data/Furnace_01.png differ diff --git a/data/Glass_00.png b/data/Glass_00.png new file mode 100644 index 0000000..cccbfca Binary files /dev/null and b/data/Glass_00.png differ diff --git a/data/Gold_Block_00.png b/data/Gold_Block_00.png new file mode 100644 index 0000000..46772f5 Binary files /dev/null and b/data/Gold_Block_00.png differ diff --git a/data/Grass_00.png b/data/Grass_00.png new file mode 100644 index 0000000..fa227d6 Binary files /dev/null and b/data/Grass_00.png differ diff --git a/data/Grass_Block_00.png b/data/Grass_Block_00.png new file mode 100644 index 0000000..aa037d9 Binary files /dev/null and b/data/Grass_Block_00.png differ diff --git a/data/Ice_00.png b/data/Ice_00.png new file mode 100644 index 0000000..1fd659c Binary files /dev/null and b/data/Ice_00.png differ diff --git a/data/Iron_Bar_00.png b/data/Iron_Bar_00.png new file mode 100644 index 0000000..089b7d6 Binary files /dev/null and b/data/Iron_Bar_00.png differ diff --git a/data/Iron_Bar_01.png b/data/Iron_Bar_01.png new file mode 100644 index 0000000..476f725 Binary files /dev/null and b/data/Iron_Bar_01.png differ diff --git a/data/Iron_Door_00.png b/data/Iron_Door_00.png new file mode 100644 index 0000000..b1cba66 Binary files /dev/null and b/data/Iron_Door_00.png differ diff --git a/data/Iron_Door_01.png b/data/Iron_Door_01.png new file mode 100644 index 0000000..4c1404c Binary files /dev/null and b/data/Iron_Door_01.png differ diff --git a/data/Iron_Trapdoor_00.png b/data/Iron_Trapdoor_00.png new file mode 100644 index 0000000..ba26752 Binary files /dev/null and b/data/Iron_Trapdoor_00.png differ diff --git a/data/Ladder_00.png b/data/Ladder_00.png new file mode 100644 index 0000000..cd9a11d Binary files /dev/null and b/data/Ladder_00.png differ diff --git a/data/Ladder_01.png b/data/Ladder_01.png new file mode 100644 index 0000000..5b594e8 Binary files /dev/null and b/data/Ladder_01.png differ diff --git a/data/Lava_Source_00.png b/data/Lava_Source_00.png new file mode 100644 index 0000000..e5aac5c Binary files /dev/null and b/data/Lava_Source_00.png differ diff --git a/data/Leaves_00.png b/data/Leaves_00.png new file mode 100644 index 0000000..94f99cf Binary files /dev/null and b/data/Leaves_00.png differ diff --git a/data/Mob_Spawner_00.png b/data/Mob_Spawner_00.png new file mode 100644 index 0000000..89238d4 Binary files /dev/null and b/data/Mob_Spawner_00.png differ diff --git a/data/Mossy_Cobblestone_00.png b/data/Mossy_Cobblestone_00.png new file mode 100644 index 0000000..52a1fa1 Binary files /dev/null and b/data/Mossy_Cobblestone_00.png differ diff --git a/data/Oak_Door_00.png b/data/Oak_Door_00.png new file mode 100644 index 0000000..5377e04 Binary files /dev/null and b/data/Oak_Door_00.png differ diff --git a/data/Oak_Door_01.png b/data/Oak_Door_01.png new file mode 100644 index 0000000..54e287f Binary files /dev/null and b/data/Oak_Door_01.png differ diff --git a/data/Oak_Pressure_Plate_00.png b/data/Oak_Pressure_Plate_00.png new file mode 100644 index 0000000..a38f5a0 Binary files /dev/null and b/data/Oak_Pressure_Plate_00.png differ diff --git a/data/Oak_Wall_Sign_00.png b/data/Oak_Wall_Sign_00.png new file mode 100644 index 0000000..1dc3b13 Binary files /dev/null and b/data/Oak_Wall_Sign_00.png differ diff --git a/data/Oak_Wall_Sign_01.png b/data/Oak_Wall_Sign_01.png new file mode 100644 index 0000000..29bf9ca Binary files /dev/null and b/data/Oak_Wall_Sign_01.png differ diff --git a/data/Oak_Wood_00.png b/data/Oak_Wood_00.png new file mode 100644 index 0000000..91a5c62 Binary files /dev/null and b/data/Oak_Wood_00.png differ diff --git a/data/Oak_Wood_Planks_00.png b/data/Oak_Wood_Planks_00.png new file mode 100644 index 0000000..e24a04c Binary files /dev/null and b/data/Oak_Wood_Planks_00.png differ diff --git a/data/Oak_Wood_Slab_00.png b/data/Oak_Wood_Slab_00.png new file mode 100644 index 0000000..32cf2ed Binary files /dev/null and b/data/Oak_Wood_Slab_00.png differ diff --git a/data/Oak_Wood_Stair_00.png b/data/Oak_Wood_Stair_00.png new file mode 100644 index 0000000..11a26bc Binary files /dev/null and b/data/Oak_Wood_Stair_00.png differ diff --git a/data/Oak_Wood_Stair_01.png b/data/Oak_Wood_Stair_01.png new file mode 100644 index 0000000..6c04611 Binary files /dev/null and b/data/Oak_Wood_Stair_01.png differ diff --git a/data/Oak_Wood_Stair_02.png b/data/Oak_Wood_Stair_02.png new file mode 100644 index 0000000..a2cf68e Binary files /dev/null and b/data/Oak_Wood_Stair_02.png differ diff --git a/data/Oak_Wood_Stair_03.png b/data/Oak_Wood_Stair_03.png new file mode 100644 index 0000000..8b8ab73 Binary files /dev/null and b/data/Oak_Wood_Stair_03.png differ diff --git a/data/Obsidian_00.png b/data/Obsidian_00.png new file mode 100644 index 0000000..ea71b56 Binary files /dev/null and b/data/Obsidian_00.png differ diff --git a/data/Poppy_00.png b/data/Poppy_00.png new file mode 100644 index 0000000..d82c670 Binary files /dev/null and b/data/Poppy_00.png differ diff --git a/data/Prismarine_00.png b/data/Prismarine_00.png new file mode 100644 index 0000000..f681ac7 Binary files /dev/null and b/data/Prismarine_00.png differ diff --git a/data/Prismarine_Brick_00.png b/data/Prismarine_Brick_00.png new file mode 100644 index 0000000..6c3bc95 Binary files /dev/null and b/data/Prismarine_Brick_00.png differ diff --git a/data/Prismarine_Dark_00.png b/data/Prismarine_Dark_00.png new file mode 100644 index 0000000..05629a1 Binary files /dev/null and b/data/Prismarine_Dark_00.png differ diff --git a/data/Red_Mushroom_00.png b/data/Red_Mushroom_00.png new file mode 100644 index 0000000..ee24581 Binary files /dev/null and b/data/Red_Mushroom_00.png differ diff --git a/data/Sandstone_00.png b/data/Sandstone_00.png new file mode 100644 index 0000000..eae34df Binary files /dev/null and b/data/Sandstone_00.png differ diff --git a/data/Sandstone_Carved_00.png b/data/Sandstone_Carved_00.png new file mode 100644 index 0000000..2935d44 Binary files /dev/null and b/data/Sandstone_Carved_00.png differ diff --git a/data/Sandstone_Slab_00.png b/data/Sandstone_Slab_00.png new file mode 100644 index 0000000..eaa3307 Binary files /dev/null and b/data/Sandstone_Slab_00.png differ diff --git a/data/Sandstone_Smooth_00.png b/data/Sandstone_Smooth_00.png new file mode 100644 index 0000000..eae34df Binary files /dev/null and b/data/Sandstone_Smooth_00.png differ diff --git a/data/Sandstone_Stair_00.png b/data/Sandstone_Stair_00.png new file mode 100644 index 0000000..63b0be8 Binary files /dev/null and b/data/Sandstone_Stair_00.png differ diff --git a/data/Sandstone_Stair_01.png b/data/Sandstone_Stair_01.png new file mode 100644 index 0000000..262b019 Binary files /dev/null and b/data/Sandstone_Stair_01.png differ diff --git a/data/Sandstone_Stair_02.png b/data/Sandstone_Stair_02.png new file mode 100644 index 0000000..7685043 Binary files /dev/null and b/data/Sandstone_Stair_02.png differ diff --git a/data/Sandstone_Stair_03.png b/data/Sandstone_Stair_03.png new file mode 100644 index 0000000..9f7c345 Binary files /dev/null and b/data/Sandstone_Stair_03.png differ diff --git a/data/Sea_Lantern_00.png b/data/Sea_Lantern_00.png new file mode 100644 index 0000000..ac23abb Binary files /dev/null and b/data/Sea_Lantern_00.png differ diff --git a/data/Snow_00.png b/data/Snow_00.png new file mode 100644 index 0000000..7664504 Binary files /dev/null and b/data/Snow_00.png differ diff --git a/data/Snow_Layer_00.png b/data/Snow_Layer_00.png new file mode 100644 index 0000000..36e5f44 Binary files /dev/null and b/data/Snow_Layer_00.png differ diff --git a/data/Spruce_Fence_00.png b/data/Spruce_Fence_00.png new file mode 100644 index 0000000..dc60ab4 Binary files /dev/null and b/data/Spruce_Fence_00.png differ diff --git a/data/Spruce_Fence_01.png b/data/Spruce_Fence_01.png new file mode 100644 index 0000000..0d9ef5d Binary files /dev/null and b/data/Spruce_Fence_01.png differ diff --git a/data/Sprucewood_00.png b/data/Sprucewood_00.png new file mode 100644 index 0000000..68f4af5 Binary files /dev/null and b/data/Sprucewood_00.png differ diff --git a/data/Sprucewood_Slab_00.png b/data/Sprucewood_Slab_00.png new file mode 100644 index 0000000..7aae83d Binary files /dev/null and b/data/Sprucewood_Slab_00.png differ diff --git a/data/Sprucewood_Stair_00.png b/data/Sprucewood_Stair_00.png new file mode 100644 index 0000000..cc0c8f8 Binary files /dev/null and b/data/Sprucewood_Stair_00.png differ diff --git a/data/Sprucewood_Stair_01.png b/data/Sprucewood_Stair_01.png new file mode 100644 index 0000000..f670745 Binary files /dev/null and b/data/Sprucewood_Stair_01.png differ diff --git a/data/Sprucewood_Stair_02.png b/data/Sprucewood_Stair_02.png new file mode 100644 index 0000000..7820c13 Binary files /dev/null and b/data/Sprucewood_Stair_02.png differ diff --git a/data/Sprucewood_Stair_03.png b/data/Sprucewood_Stair_03.png new file mode 100644 index 0000000..3c6f1bd Binary files /dev/null and b/data/Sprucewood_Stair_03.png differ diff --git a/data/Stone_00.png b/data/Stone_00.png new file mode 100644 index 0000000..51aa3ea Binary files /dev/null and b/data/Stone_00.png differ diff --git a/data/Stone_Brick_00.png b/data/Stone_Brick_00.png new file mode 100644 index 0000000..fdf56b7 Binary files /dev/null and b/data/Stone_Brick_00.png differ diff --git a/data/Stone_Brick_Carved_00.png b/data/Stone_Brick_Carved_00.png new file mode 100644 index 0000000..10d24c7 Binary files /dev/null and b/data/Stone_Brick_Carved_00.png differ diff --git a/data/Stone_Brick_Cracked_00.png b/data/Stone_Brick_Cracked_00.png new file mode 100644 index 0000000..0834ad4 Binary files /dev/null and b/data/Stone_Brick_Cracked_00.png differ diff --git a/data/Stone_Brick_Slab_00.png b/data/Stone_Brick_Slab_00.png new file mode 100644 index 0000000..860db29 Binary files /dev/null and b/data/Stone_Brick_Slab_00.png differ diff --git a/data/Stone_Brick_Stair_00.png b/data/Stone_Brick_Stair_00.png new file mode 100644 index 0000000..da9b476 Binary files /dev/null and b/data/Stone_Brick_Stair_00.png differ diff --git a/data/Stone_Brick_Stair_01.png b/data/Stone_Brick_Stair_01.png new file mode 100644 index 0000000..ad29367 Binary files /dev/null and b/data/Stone_Brick_Stair_01.png differ diff --git a/data/Stone_Brick_Stair_02.png b/data/Stone_Brick_Stair_02.png new file mode 100644 index 0000000..7c4d664 Binary files /dev/null and b/data/Stone_Brick_Stair_02.png differ diff --git a/data/Stone_Brick_Stair_03.png b/data/Stone_Brick_Stair_03.png new file mode 100644 index 0000000..18bbc3e Binary files /dev/null and b/data/Stone_Brick_Stair_03.png differ diff --git a/data/Stone_Brick_Wall_00.png b/data/Stone_Brick_Wall_00.png new file mode 100644 index 0000000..9334f03 Binary files /dev/null and b/data/Stone_Brick_Wall_00.png differ diff --git a/data/Stone_Button_00.png b/data/Stone_Button_00.png new file mode 100644 index 0000000..9adf8e7 Binary files /dev/null and b/data/Stone_Button_00.png differ diff --git a/data/Stone_Button_01.png b/data/Stone_Button_01.png new file mode 100644 index 0000000..0b024f9 Binary files /dev/null and b/data/Stone_Button_01.png differ diff --git a/data/Stone_Pressure_Plate_00.png b/data/Stone_Pressure_Plate_00.png new file mode 100644 index 0000000..63bfa03 Binary files /dev/null and b/data/Stone_Pressure_Plate_00.png differ diff --git a/data/Stone_Slab_00.png b/data/Stone_Slab_00.png new file mode 100644 index 0000000..bd0d40f Binary files /dev/null and b/data/Stone_Slab_00.png differ diff --git a/data/Stone_Stair_00.png b/data/Stone_Stair_00.png new file mode 100644 index 0000000..637a260 Binary files /dev/null and b/data/Stone_Stair_00.png differ diff --git a/data/Stone_Stair_01.png b/data/Stone_Stair_01.png new file mode 100644 index 0000000..4618d33 Binary files /dev/null and b/data/Stone_Stair_01.png differ diff --git a/data/Stone_Stair_02.png b/data/Stone_Stair_02.png new file mode 100644 index 0000000..b4f443a Binary files /dev/null and b/data/Stone_Stair_02.png differ diff --git a/data/Stone_Stair_03.png b/data/Stone_Stair_03.png new file mode 100644 index 0000000..4a2d3f5 Binary files /dev/null and b/data/Stone_Stair_03.png differ diff --git a/data/Stonebrick_Cracked_00.png b/data/Stonebrick_Cracked_00.png new file mode 100644 index 0000000..0834ad4 Binary files /dev/null and b/data/Stonebrick_Cracked_00.png differ diff --git a/data/TNT_00.png b/data/TNT_00.png new file mode 100644 index 0000000..018cec2 Binary files /dev/null and b/data/TNT_00.png differ diff --git a/data/Tall_Grass_00.png b/data/Tall_Grass_00.png new file mode 100644 index 0000000..1523e8c Binary files /dev/null and b/data/Tall_Grass_00.png differ diff --git a/data/Torch_00.png b/data/Torch_00.png new file mode 100644 index 0000000..00c1031 Binary files /dev/null and b/data/Torch_00.png differ diff --git a/data/Trapdoor_00.png b/data/Trapdoor_00.png new file mode 100644 index 0000000..572fe88 Binary files /dev/null and b/data/Trapdoor_00.png differ diff --git a/data/Water_Source_00.png b/data/Water_Source_00.png new file mode 100644 index 0000000..87b8292 Binary files /dev/null and b/data/Water_Source_00.png differ diff --git a/data/Wool_00.png b/data/Wool_00.png new file mode 100644 index 0000000..f19794c Binary files /dev/null and b/data/Wool_00.png differ diff --git a/data/blocks.csv b/data/blocks.csv new file mode 100644 index 0000000..d4802ad --- /dev/null +++ b/data/blocks.csv @@ -0,0 +1,75 @@ +_Name,BlockID,Biome Specific,Mineclone2,Minetest Game +Cobblestone,4,,mcl_core:cobble,default:cobble +Dirt,3,biome:node_filler,mcl_core:dirt,default:dirt +Stone,1,biome:node_stone,mcl_core:stone,default:stone +Stone_Slab,44,,mcl_stairs:slab_stone,default:slab_stone +Stone_Stair,67,,mcl_stairs:stair_stone,default:stair_stone +Cobblestone_Stair,67,,mcl_stairs:stair_cobble,default:stair_cobble +Mossy_Cobblestone,48,,mcl_core:mossycobble,default:mossycobble +Grass_Block,2,biome:node_top,mcl_core:dirt_with_grass,default:dirt_with_grass +Oak_Wood,17,,mcl_core:tree,default:tree +Oak_Wood_Planks,5/125,,mcl_core:wood,default:wood +Oak_Wood_Slab,126,,mcl_stairs:slab_wood,default:slab_wood +Oak_Wood_Stair,53,,mcl_stairs:stair_wood,default:stair_wood +Crafting_Table,58,,mcl_crafting_table:crafting_table,default:crafting_table +Red_Mushroom,40,,mcl_mushrooms:mushroom_red,default:mushroom_red +Sprucewood,17,,mcl_core:sprucewood,default:sprucewood +Sprucewood_Slab,,,mcl_stairs:slab_sprucewood,default:slab_sprucewood +Sprucewood_Stair,134,,mcl_stairs:stair_sprucewood,default:stair_sprucewood +Spruce_Fence,188,,mcl_fences:spruce_fence,default:spruce_fence +Chest,54,,mcl_chests:chest,default:chest +Furnace,61,,mcl_furnaces:furnace,default:furnace +Torch,50,,mcl_torches:torch_wall,default:torch_wall +TNT,46,,mcl_tnt:tnt,default:tnt +Brick_Block,45,,mcl_core:brick_block,default:brick_block +Lava_Source,11,,mcl_core:lava_source,default:lava_source +Ice,212,,mcl_core:ice,default:ice +Leaves,18,,mcl_core:leaves,default:leaves +Glass,20,,mcl_core:glass,default:glass +Cauldron,118,,mcl_cauldrons:cauldron,default:cauldron +Bookshelf,47,,mcl_books:bookshelf,default:bookshelf +Sandstone,24,biome:node_stone,mcl_core:sandstone,default:sandstone +Sandstone_Carved,,,mcl_core:sandstonecarved,default:sandstonecarved +Sandstone_Smooth,,,mcl_core:sandstonesmooth,default:sandstonesmooth +Sandstone_Slab,,,mcl_stairs:slab_sandstone,default:slab_sandstone +Sandstone_Stair,128,,mcl_stairs:stair_sandstone,default:stair_sandstone +Ladder,65,,mcl_core:ladder,default:ladder +Fence,85,,mcl_fences:fence,default:fence +Oak_Door,64,,mcl_doors:wooden_door,default:wooden_door.png +Iron_Door,71,,mcl_doors:iron_door,default:iron_door +Water_Source,9,,mcl_core:water_source,default:water +Stone_Brick,98,biome:node_stone,mcl_core:stonebrick,default:stonebrick +Stone_Brick_Slab,43,,mcl_stairs:slab_stonebrick,default:slab_stonebrick +Stone_Brick_Stair,109,,mcl_stairs:stair_stonebrick,default:stair_stonebrick +Trapdoor,96,,mcl_doors:trapdoor,default:trapdoor +Iron_Trapdoor,167,,mcl_doors:iron_trapdoor,default:iron_trapdoor +Gold_Block,89/41,,mcl_core:goldblock,default:goldblock +Snow,80,,mcl_core:snow,default:snow +Snow_Layer,78,,mcl_core:snow_layer,default:snow_layer +Dandelion,37,,mcl_flowers:dandelion,default:dandelion +Poppy,38,,mcl_flowers:poppy,default:poppy +Grass,31,,mcl_flowers:grass,default:grass +Tall_Grass,31,,mcl_flowers:tallgrass,default:tallgrass +Iron_Bar,101,,xpanes:bar_flat,xpanes:bar_flat +Mob_Spawner,52,,mcl_mobspawners:spawner,default:spawner +End_Portal_Frame,120,,mcl_portals:end_portal_frame,default:end_portal_frame +Oak_Pressure_Plate,72,,mesecons_pressureplates:pressure_plate_wood_off,mesecons_pressureplates:pressure_plate_wood_off +Stone_Pressure_Plate,70,,mesecons_pressureplates:pressure_plate_stone_off,mesecons_pressureplates:pressure_plate_stone_off +Andesite,,biome:node_stone,mcl_core:andesite,default:andesite +Bedrock,7,,mcl_core:bedrock,default:bedrock +Obsidian,49,,mcl_core:obsidian,default:obsidian +Cactus,81,,mcl_core:cactus,default:cactus +Brewing_Stand,117,,mcl_core:brewing_stand,default:brewing_stand +Stone_Brick_Carved,,,mcl_core:stonebrickcarved,default:stonebrickcarved +Stone_Brick_Cracked,,,mcl_core:stonebrickcracked,default:stonebrickcracked +Bed_Red,26,,mcl_beds:red,default:red +Carpet_White,171,,mcl_wool:white_carpet,default:white_carpet +Wool,35,,mcl_wool:white,default:White_wool +Cobblestone_Wall,139,,mcl_walls:cobble,default:wall_cobble +Stone_Brick_Wall,,,mcl_walls:stonebrick,default:wall_stonebrick +Prismarine,168,,mcl_ocean:prismarine,default:prismarine +Prismarine_Brick,,,mcl_ocean:prismarine_brick,default:prismarine_brick +Prismarine_Dark,,,mcl_ocean:prismarine_dark,default:prismarine_dark +Sea_Lantern,169,,mcl_ocean:sea_lantern,default:sea_Lantern +Cobweb,30,,mcl_core:cobweb,default:cobweb +Oak_Wall_Sign,68,,mcl_signs:wall_sign,default:wall_sign diff --git a/docs/blocks.md b/docs/blocks.md new file mode 100644 index 0000000..d9c893e --- /dev/null +++ b/docs/blocks.md @@ -0,0 +1,52 @@ +MTSEdit Block Types Data +======================== + +Directory Paths +--------------- + +Block definitions are looked for in several directories, even depending on operating system. Regardless if you set the +`MTSDATA` environment variable, you can specify the directory explicitly. + +1. the directory specified in '$MTSDATA' if enviroment variable defined (Linux, MacOSX, Windows) +2. in the user's home directory '$HOME/.config/mtsedit' (Linux) +3. in the user's home directory '$HOME/Library/Application Support/MTSEdit' (MacOSX) +4. system wide directory '/usr/share/mtsedit' or '/usr/local/share/mtsedit' (Linux and MacOSX) +5. in the executable's directory, most likely 'C:\\Program Files (x86)\\MTSEdit\\data' (Windows) +6. in the executable's parent directory, '../data' (source repository, and if installed to '/opt/mtsedit/bin' on Linux) + +Blocks.csv +---------- + +In the `data` directory, mtsedit looks for this CSV file. It is used to define blocks used in different file formats. +Each line contains comma separated values, which might be surrounded by quotes. Commas inside quoted strings does +not count as coloumn separators. The lines are ended in '\\n' newline character, carrige return '\\r' is optional. + +### Header + +The first row is special, it defines the coloumn names. The first 3 coloumns are irrelevant and not parsed, others +define the name of the block mapping. + +### Coloumns + +| Number | Format | Description | +|-------:|---------|---------------------------------------------------------------------------------------| +| 1st | string | canonical name of the block (also used to identify block images) | +| 2nd | intlist | list of slash '/' separated integer numbers, the Minecraft BlockID in schematic files | +| 3rd | string | an optional biome specific name for the block (like "biome:node_stone") | +| 4th+ | string | block's name in the mapping (this coloumn may be repated as many times as you like) | + +Block Images +------------ + +Each block type may have more, but must have at least one image in the `data` directory. These are named as: +``` +(block type)_(param2).png +``` +The first part "block type" cames from the canonical name (1st coloumn), but with spaces converted to underscores '\_'. The +second part, "param2" is a hexa-decimal value and represents the orientation. If the block has only one image, then it must be +"00", meaning block's image when the block is facing North. Other values are: "01" rotated by 90 degrees counter-clockwise +(facing West), "02" rotated by 180 degrees (faces South), "03" rotated by 90 degrees clock-wise (faces East). Values "04"-"1F" +are only used for blocks that can be rotated upside-down too, like stairs, but I honestly have no clue how. + +Hint: the MTSEdit tool can generate slab and stair images from block pictures using the `-g` option. This will only generate the +first 4 orientation, a slab, and a mirrored verion of the original block. diff --git a/docs/mts_format.md b/docs/mts_format.md new file mode 100644 index 0000000..6f709bb --- /dev/null +++ b/docs/mts_format.md @@ -0,0 +1,92 @@ +File Formats +============ + +Schematic +--------- + +These are [Minecraft NBT](https://minecraft.gamepedia.com/Schematic_file_format) files, used by many Minecraft-compatible +editors. Only the [blocks](https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening/Block_IDs) section is parsed, +and only used to import schematics. MTSEdit does not save .schematic files. + +MTS +--- + +MTS files are [MineTest Schematic files](https://dev.minetest.net/Minetest_Schematic_File_Format) used by the +[Minetest Engine](https://www.minetest.net). This is the default format of MTSEdit for both loading and saving. + +All values are stored in __BIG endian__ format. + +### Header + +| Offset | Length | Description | +|--------:|-------:|------------------------------------------| +| 0 | 4 | magic "MTSM" | +| 4 | 2 | file format version, currently 3 or 4 | +| 6 | 2 | size X | +| 8 | 2 | size Y | +| 10 | 2 | size Z | +| 12 | Y | layer probability values, only if ver 4 | +| 12/12+Y | 2 | number of strings in Name-ID table | + +Layer probability values: an unsigned char for each layer, bit 7 is reserved and must be zero, meaning 0x1F maps to 100%. This +block only exists if the file version is 4 and not 3. + +The header is followed by the Name-ID Table, which is followed by the Block Definitons section. + +### Name-ID Table + +For each string, the following record format repeats: + +| Offset | Length | Description | +|-------:|-------:|--------------------------| +| 0 | 2 | length of the string (N) | +| 2 | N | string, block type name | + +The block IDs in the next section reference these. + +MTSEdit uses different block type mappings to generate this table. Those mappings are specified in the +[blocks.csv](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/blocks.md) file in the `data` directory. + +### Block Definitons Section + +This part of the file is zlib compressed, with the deflate algorithm using gz header bytes +([RFC 1950](http://tools.ietf.org/html/rfc1950), but not with the gzip header which has magic bytes too). After uncompressing, +the format is as follows: + +| Offset | Length | Description | +|--------:|--------:|------------------------------------------| +| 0 | 2*X*Y*Z | block IDs (param0) | +| 2*X*Y*Z | X*Y*Z | probability values (param1) | +| 3*X*Y*Z | X*Y*Z | rotation info (param2) | + +To get the block ID from `param0` for a given coordinate (x,y,z), you should calculate the index param0\[z*Z*Y + y*X + x\]. +Each block ID is stored on a big endian 2 bytes, although I haven't seen any MTS files yet that used more than 255 block types +per file. Also most files store "air" block type in the first type (block ID being 0), but this is not mandatory unfortunately. + +Orientation is NOT defined in any way, there's absolutely no convention. MTSEdit however prefers to save schematics in a +standardized way, that is, the entrance is on the South side. You can use the "Change Oriantation" icon in the editor to +set that right before you save. In the editor's isometric view, South is on the bottom left corner. + +Probability values in `param1` are encoded on 8-bits. They are indexed the same way as block IDs. Version 3 ranges from 0 to 0xFF, +while version 4 uses the same values as for layer probability, from 0 to 0x7F. According to the spec, in version 4 bit 7 means node +force placement, but I haven't seen any MTS files yet with that bit set. + +Rotation information is stored in `param2`. This is the worst documented part of the MTS files. It sometimes has large values, +but MTSEdit will simply ignore those. What I could make out from C++ source, orentation is as follows: 0 means normal orientation +(block faces North), 1 means rotated by 90 degrees (faces West), 2 means rotated by 180 degrees (faces South), and 3 means rotated +by 90 degrees counter clock-wise (faces East). I have absoultely no idea what 4-31 supposed to mean, right now MTSEdit only handles +8, by 4-7 meaning the same orientation, but up-side down. Only these 8 values are understood by MTSEdit, but I would like add +support for the rest if someone could explain what they mean. + +#### Addition to the MTS Specification + +Normally MTS files can't store ground level information. MTSEdit solves this by setting `param2` to 0x20 for air blocks on +ground level. This is not a perfect solution (the ground level might have no air blocks), but at least it does not interfere +with the Minetest Engine. + +Another addition is that MTS files can be saved with biome specific block type names. In the Lua API, register_biome() calls +can set default block types for certain blocks, such as node_top, node_filler, node_stone etc. MTSEdit can save files with those +names, such as `biome:node_stone`. When reading these schematics, block types that start with "biome:" should be handled indirectly +by looking up the property of the biome where the schematics is placed. This is not supported by the Minetest Engine yet, but Lua +modules can implement this easily. + diff --git a/docs/mtsedit.png b/docs/mtsedit.png new file mode 100644 index 0000000..bf4d135 Binary files /dev/null and b/docs/mtsedit.png differ diff --git a/docs/mtseditdoc.png b/docs/mtseditdoc.png new file mode 100644 index 0000000..2def7a6 Binary files /dev/null and b/docs/mtseditdoc.png differ diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..c09f735 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,119 @@ +MTSEDit User Manual +=================== + +This little tool can be compiled on any system easily. It's only dependency is SDL2. Without an argument, the Windows version +will ask the user for a file name. All the others print a usage to stdout. + +Batch Mode +---------- + +You can use this tool from scripts to bulk convert schematic files or generate preview for them. + +### Dump Schematic to Standard Output + +``` +./mtsedit -d structure.mts +``` + +### Convertion + +``` +./mtsedit from.schematic structure.mts +``` +You can convert Minecraft Schematics with this tool. You can also convert MTS files using different block types: + +``` +./mtsedit -m "Minetest Game" mcl2.mts mtg.mts +``` + +### Generate Preview + +``` +./mtsedit -p structure.mts +``` +This will save "structure.png". + +``` +./mtsedit -P structure.mts +``` +With uppercase "P", you can cut the structure in half to peek inside. + +### Generate Pictures for Slab and Stairs + +This wasn't a planned feature, but I needed it to generate differently rotated block images from block node images. + +``` +./mtsedit -g Gold_Block_00.png +``` +This will save "Gold_Block_Slab_00.png", "Gold_Block_Stair_00.png", "Gold_Block_Stair_01.png", "Gold_Block_Stair_02.png" and +"Gold_Block_Stair_03.png". + +Interactive Mode +---------------- + +This is the default. If you start MTSEdit with a single parameter, being an MTS file's path, it will open it in a GUI editor. + +### Main Editor Window + + + +You can find the toolbar `A` on the left. The icons are: +1. Load / Reload Structure +2. Save As +3. Save Preview +4. Change Orientation +5. Add Blocks to Palette + +Beneath the icons is the palette `B`. You can add blocks to here from the Add Blocks menu, and then you can quickly access them +by shortcuts. + +On the bottom left corner, you find the Layer-related options `C`: + +1. Layer Up +2. Layer Down +3. Set Ground Level +4. Current Layer +5. Current Layer's Probability + +The bottom line is the status bar `D`. You'll see the block names here as you hover over them. Messages like file saved also shown +here. + +Everything else on the screen is the main editor area `E`. + +### Keyboard Shortcuts + +Main editor window: + +| Key | Description | +|-----------------------------------|---------------------------------------------------| +| mouse left button | place the selected block | +| mouse right button | rotate block | +| middle buttons (scroll) | scroll the editor area | +| Ctrl + scroll | rotate the current block | +| Shift + scroll | move the upper layers up and down | +| PgUp / PgDn | change current layer | +| arrows | select current block | +| Space | place selected block | +| Backspace | erase current block | +| Ctrl + arrows | scroll the editor area | +| Shift + up / down | move the upper layers up and down | +| Shift + left / right | rotate the current block | +| + / - | change current layer's probability | +| L | (re)load schematic | +| S | save schematic | +| Shift + S | open save as window | +| P | save preview PNG | +| Shift + P | save preview with structure cut in half | +| R | change orientation, rotate the entire structure | +| M / Tab | open block map, search all available blocks | +| 0, Backquote | select Air block (erase mode) | +| 1 - 9 | quickly access one of the blocks from the palette | +| G | set current layer as ground level | +| Q / Esc | quit MTSEdit | + +Add blocks to palette window: + +| Key | Description | +|-----------------------------------|---------------------------------------------------| +| any key | instant search blocks | +| Ctrl + number | ass the Nth search result to the palette | diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..c535c84 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,62 @@ +TARGET = mtsedit +MINGWSDL = ../.. +SRCS = $(wildcard *.c) + +CFLAGS = -Wall -Wextra -ansi -pedantic -g + +# MacOSX +ifneq ("$(wildcard /Library/Frameworks/SDL*)","") +CFLAGS += -I/usr/include/SDL2 +LIBS = -framework SDL +else +# Linux +ifneq ("$(wildcard /usr/include/SDL2/SDL.h)","") +CFLAGS += -I/usr/include/SDL2 +LIBS = -lSDL2 +else +# Windows MinGW +ifneq ("$(wildcard $(MINGWSDL)/i686-w64-mingw32/include/SDL2/SDL.h)","") +CFLAGS += -I$(MINGWSDL)/i686-w64-mingw32/include/SDL2 +LIBDIRS = -static-libgcc -L$(MINGWSDL)/i686-w64-mingw32/lib +LIBS = -lSDL2 -lcomdlg32 -luser32 +endif +endif +endif + +OBJS = icons.o font.o $(SRCS:.c=.o) + +all: configure $(OBJS) $(TARGET) + +configure: +ifeq ("$(LIBS)","") + @echo "No ui driver can be detected. Install libsdl2-dev." + @false +endif + +icons.o: icons.png + @$(LD) -r -b binary -o icons.o icons.png + +font.o: font.psf.gz + @$(LD) -r -b binary -o font.o font.psf.gz + +%: %.c main.h lang.h + $(CC) $(CFLAGS) $< -c $@ + +$(TARGET): $(OBJS) + $(CC) $(LIBDIRS) $(OBJS) -o $(TARGET) $(LIBS) + +install: $(TARGET) + install -m 755 -g bin $(TARGET) /usr/bin + @mkdir -p /usr/share/mtsedit + cp ../data/* /usr/share/mtsedit + +package: $(TARGET) + @mkdir MTSEdit + @ln -s ../../data MTSEdit/data + @ln -s ../$(TARGET).exe MTSEdit/$(TARGET).exe + @ln -s ../SDL2.dll MTSEdit/SDL2.dll + zip $(TARGET)-x32-win.zip MTSEdit || true + @rm -rf MTSEdit + +clean: + @rm $(TARGET) $(TARGET).exe *.o *.zip 2>/dev/null || true diff --git a/src/edit.c b/src/edit.c new file mode 100644 index 0000000..0cf58f8 --- /dev/null +++ b/src/edit.c @@ -0,0 +1,308 @@ +/* + * mtsedit/edit.c + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief The main edit area + * + */ + +#include "main.h" + +int dx = 13, dz = 8, cx = 127, cz = 127, ox = 0, oy = 0, up = 0; + +/** + * Redraw the Edit Area + */ +void edit_redraw(int full) +{ + int i, j, k = 160, l, X, Z, x, y, z, sx, sy, mx, tx, ty; + unsigned char *b = (uint8_t*)&theme[THEME_TABBG]; + SDL_Rect dst, src; + + mx = ((bg->w / 2 / dx) + (bg->h / 2 / dz)) /2; + sx = bg->w / 2; + sy = bg->h / 2; + + if(full) { + dst.x = dst.y = 0; dst.w = bg->w; dst.h = bg->h; + SDL_FillRect(bg, &dst, theme[THEME_TABBG]); + src.x = 0; src.y = 4 * 32; dst.x = bg->w - 32; dst.y = bg->h - 32; + src.w = src.h = dst.w = dst.h = 32; + SDL_BlitSurface(icons, &src, bg, &dst); + memset(fg->pixels, 0, bg->pitch * fg->h); + src.w = src.h = dst.w = dst.h = 32; src.x = src.y = 0; + for(y = mx > currlayer ? 0 : currlayer - mx; y < currlayer; y++) { + for(z = -mx; z < mx; z++) { + for(x = -mx; x < mx; x++) { + dst.x = sx + dx * (x - z); + dst.y = sy + dz * (z + x + 2*(currlayer - y)); + X = 127 + x + oy + ox; + Z = 127 + z + oy - ox; + if(X < 0 || X > 255 || Z < 0 || Z > 255 || + dst.x + 32 < 0 || dst.x > bg->w || dst.y + 32 < 0 || dst.y > bg->h) continue; + i = nodes[y][Z][X].param0; + j = nodes[y][Z][X].param2 & 0x1F; + if(i) { + if(!blocks[i].dr[j]) { + blocks[i].dr[j] = (unsigned char *)malloc(32 * 32 * 4); + if(!blocks[i].dr[j]) error(lang[ERR_MEM]); + memcpy(blocks[i].dr[j], blocks[i].img[j] ? blocks[i].img[j] : (blocks[i].img[0] ? blocks[i].img[0] : + (uint8_t*)icons->pixels + 32 * icons->pitch), 32 * 32 * 4); + for(l = 0; l < 32 * 32 * 4; l += 4) { + blocks[i].dr[j][l+0] = (b[0]*k + (256 - k)*blocks[i].dr[j][l+0])>>8; + blocks[i].dr[j][l+1] = (b[0]*k + (256 - k)*blocks[i].dr[j][l+1])>>8; + blocks[i].dr[j][l+2] = (b[0]*k + (256 - k)*blocks[i].dr[j][l+2])>>8; + } + } + blk->pixels = blocks[i].dr[j]; + SDL_BlitSurface(blk, &src, bg, &dst); + } + } + } + } + for(z = -mx; z < mx; z++) { + for(x = -mx; x < mx; x++) { + dst.x = sx + dx * (x - z); + dst.y = sy + dz * (z + x); + X = 127 + x + oy + ox; + Z = 127 + z + oy - ox; + if(X < 0 || X > 255 || Z < 0 || Z > 255 || + dst.x + 32 < 0 || dst.x > bg->w || dst.y + 32 < 0 || dst.y > bg->h) continue; + i = nodes[currlayer][Z][X].param0; + j = nodes[currlayer][Z][X].param2 & 0x1F; + if(i) { + blk->pixels = blocks[i].img[j] ? blocks[i].img[j] : (blocks[i].img[0] ? blocks[i].img[0] : + (uint8_t*)icons->pixels + 32 * icons->pitch); + SDL_BlitSurface(blk, &src, bg, &dst); + } + } + } + if(up > mx) up = mx; + for(y = currlayer + 1; y < (currlayer + mx < 255 ? currlayer + mx : 255); y++) { + for(z = -mx; z < mx; z++) { + for(x = -mx; x < mx; x++) { + dst.x = sx + dx * (x - z); + dst.y = sy + dz * (z + x + (currlayer - y - 1) - mx + up); + X = 127 + x + oy + ox; + Z = 127 + z + oy - ox; + if(X < 0 || X > 255 || Z < 0 || Z > 255 || + dst.x + 32 < 36 || dst.x > fg->w || dst.y + 32 < 0 || dst.y > fg->h) continue; + i = nodes[y][Z][X].param0; + j = nodes[y][Z][X].param2 & 0x1F; + if(i) { + if(!blocks[i].tr[j]) { + blocks[i].tr[j] = (unsigned char *)malloc(32 * 32 *4); + if(!blocks[i].tr[j]) error(lang[ERR_MEM]); + memcpy(blocks[i].tr[j], blocks[i].img[j] ? blocks[i].img[j] : (blocks[i].img[0] ? blocks[i].img[0] : + (uint8_t*)icons->pixels + 32 * icons->pitch), 32 * 32 * 4); + for(l = 0; l < 32 * 32 * 4; l += 4) blocks[i].tr[j][l+3] >>= 2; + } + blk->pixels = blocks[i].tr[j]; + SDL_BlitSurface(blk, &src, fg, &dst); + } + } + } + } + src.x = src.y = dst.y = 0; dst.x = 36; src.w = dst.w = bg->w; src.h = dst.h = bg->h; + SDL_BlitSurface(bg, &src, screen, &dst); + } + src.w = dst.w = src.h = dst.h = 32; src.x = src.y = 0; + sx = (screen->w - 36) / 2; + sy = (screen->h - font->height) / 2; + for(z = cz - 127 - oy + ox; z <= cz - 125 - oy + ox; z++) { + for(x = cx - 127 - oy - ox; x <= cx - 125 - oy - ox; x++) { + dst.x = sx + dx * (x - z) + 36; + dst.y = sy + dz * (z + x); + X = 127 + x + oy + ox; + Z = 127 + z + oy - ox; + if(X < 0 || X > 255 || Z < 0 || Z > 255 || + dst.x + 32 < 36 || dst.x > screen->w || dst.y + 32 < 0 || dst.y > screen->h) continue; + i = nodes[currlayer][Z][X].param0; + j = nodes[currlayer][Z][X].param2 & 0x1F; + if(Z == cz && X == cx) { + tx = dst.x; ty = dst.y; + blk->pixels = (uint8_t*)icons->pixels + 64 * icons->pitch; + SDL_BlitSurface(blk, &src, screen, &dst); + if(i) { + blk->pixels = blocks[i].img[j] ? blocks[i].img[j] : (blocks[i].img[0] ? blocks[i].img[0] : + (uint8_t*)icons->pixels + 32 * icons->pitch); + SDL_BlitSurface(blk, &src, screen, &dst); + } + blk->pixels = (uint8_t*)icons->pixels + 96 * icons->pitch; + SDL_BlitSurface(blk, &src, screen, &dst); + } else + if(i) { + blk->pixels = blocks[i].img[j] ? blocks[i].img[j] : (blocks[i].img[0] ? blocks[i].img[0] : + (uint8_t*)icons->pixels + 32 * icons->pitch); + SDL_BlitSurface(blk, &src, screen, &dst); + } + } + } + src.x = tx - 36 - 32; dst.x = tx - 32; src.y = dst.y = ty; src.w = dst.w = 32; src.h = dst.h = 64; + SDL_BlitSurface(bg, &src, screen, &dst); + if(!full) SDL_BlitSurface(fg, &src, screen, &dst); + src.x = tx - 36 + 32; dst.x = tx + 32; + SDL_BlitSurface(bg, &src, screen, &dst); + if(!full) SDL_BlitSurface(fg, &src, screen, &dst); + src.x = tx - 36 - 32; dst.x = tx - 32; src.y = dst.y = ty + 32; src.w = dst.w = 96; src.h = dst.h = 32; + SDL_BlitSurface(bg, &src, screen, &dst); + if(!full) SDL_BlitSurface(fg, &src, screen, &dst); + src.x = tx - 36 - 32; dst.x = tx - 32; src.y = dst.y = ty - 16; src.w = dst.w = 96; src.h = dst.h = 16; + SDL_BlitSurface(bg, &src, screen, &dst); + if(full) { + src.x = src.y = dst.y = 0; dst.x = 36; src.w = dst.w = bg->w; src.h = dst.h = bg->h; + } else { + SDL_BlitSurface(fg, &src, screen, &dst); + src.x = tx - 36; dst.x = tx; src.y = dst.y = ty; src.w = dst.w = 32; src.h = dst.h = 32; + } + SDL_BlitSurface(fg, &src, screen, &dst); +} + +/** + * Hide the cursor + */ +void edit_hidecursor() +{ + SDL_Rect src, dst; + int z = cz - 127 - oy + ox, x = cx - 127 - oy - ox; + + src.x = dst.x = ((screen->w - 36) / 2) + dx * (x - z); + src.y = dst.y = ((screen->h - font->height) / 2) + dz * (z + x); + dst.x += 36; src.w = dst.w = src.h = dst.h = 32; + SDL_BlitSurface(bg, &src, screen, &dst); +} + +/** + * Edit Area scrolling event handler + */ +void edit_scroll(SDL_Event *event) +{ + if(shift) { + if(event->wheel.y) { + up -= event->wheel.y; + if(up < -128) up = -128; + if(up > 127) up = 127; + } else + edit_rotate(currlayer, cz, cx); + } else if(ctrl) { + edit_rotate(currlayer, cz, cx); + } else { + oy += event->wheel.y; + if(oy < -128) oy = -128; + if(oy > 127) oy = 127; + ox += event->wheel.x; + if(ox < -128) ox = -128; + if(ox > 127) ox = 127; + } +} + +/** + * Edit Area mouse over event handler + */ +void edit_mouseover(SDL_Event *event) +{ + int l, k; + + l = (int)(event->motion.y - (bg->h / 2)); + if(l < 0) l -= dz; + l /= dz; l--; + k = (int)(event->motion.x - 36 - (bg-> w / 2) - dx/2); + if(k < 0) k -= dx; + k -= (l & 1 ? dx/2 : 0); + k /= dx; + cz = (l - k) / 2; + cx = l - cz; + cz += 127 + oy - ox; + cx += 127 + oy + ox; + if(event->motion.state) { + blocks[nodes[currlayer][cz][cx].param0].numref--; + k = event->motion.state == 1 ? palette[activeblock] : 0; + blocks[k].numref++; + nodes[currlayer][cz][cx].param0 = k; + nodes[currlayer][cz][cx].param1 = k ? 127 : 0; + } + status = nodes[currlayer][cz][cx].param0 ? blocks[nodes[currlayer][cz][cx].param0].name : NULL; +} + +/** + * Edit Area mouse down event handler + */ +void edit_mousedown(_unused SDL_Event *event) +{ + int i; + + if(event->button.button != 1) + edit_rotate(currlayer,cz,cx); + else { + blocks[nodes[currlayer][cz][cx].param0].numref--; + i = palette[activeblock]; + blocks[i].numref++; + nodes[currlayer][cz][cx].param0 = i; + nodes[currlayer][cz][cx].param1 = i ? 127 : 0; + status = i ? blocks[i].name : NULL; + } +} + +/** + * Rotate a single block + */ +void edit_rotate(int y, int z, int x) +{ + unsigned char rot_table[24 * 4] = { + 0, 1, 2, 3, + 1, 2, 3, 0, + 2, 3, 0, 1, + 3, 0, 1, 2, + 4, 13, 10, 19, + 5, 14, 11, 16, + 6, 15, 8, 17, + 7, 12, 9, 18, + 8, 17, 6, 15, + 9, 18, 7, 12, + 10, 19, 4, 13, + 11, 16, 5, 14, + 12, 9, 18, 7, + 13, 10, 19, 4, + 14, 11, 16, 5, + 15, 8, 17, 6, + 16, 5, 14, 11, + 17, 6, 15, 8, + 18, 7, 12, 9, + 19, 4, 13, 10, + 20, 23, 22, 21, + 21, 20, 23, 22, + 22, 21, 20, 23, + 23, 22, 21, 20}; + + if(!nodes[y][z][x].param0) return; + if(nodes[y][z][x].param2 < 4) nodes[y][z][x].param2++; + else if(nodes[y][z][x].param2 == 3 && !blocks[nodes[y][z][x].param0].img[4]) nodes[y][z][x].param2 = 0; + else { + /* this is the code from Minetest engine. But I don't trust it. */ + nodes[y][z][x].param2 &= 0x1F; + nodes[y][z][x].param2 |= rot_table[((nodes[y][z][x].param2 % 24) * 4) + 1]; + } + if(!blocks[nodes[y][z][x].param0].img[nodes[y][z][x].param2]) + nodes[y][z][x].param2 = nodes[y][z][x].param2 == 3 && blocks[nodes[y][z][x].param0].img[1] ? 1 : 0; +} diff --git a/src/font.psf.gz b/src/font.psf.gz new file mode 100644 index 0000000..dbba2a8 Binary files /dev/null and b/src/font.psf.gz differ diff --git a/src/icons.png b/src/icons.png new file mode 100644 index 0000000..762a36e Binary files /dev/null and b/src/icons.png differ diff --git a/src/lang.c b/src/lang.c new file mode 100644 index 0000000..337f5e7 --- /dev/null +++ b/src/lang.c @@ -0,0 +1,122 @@ +/* + * mtsedit/lang.c + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief Multilanguage support + * + */ + +#include "lang.h" + +/** + * Translations + */ +char *dict[NUMLANGS][NUMTEXTS + 1] = { + { + "en", + "dump layers to output", + "save preview", + "preview structure cut in half", + "generate slab and stairs from block image", + "replace block type mapping", + "output to file", + "Loading...", + "Memory allocation error", + "Unable to locate data directory. Set MTSDATA environment variable.", + "Unable to parse blocks.csv", + "no block image for", + "no matching node for", + "Unable to save MTS file", + "Unable to load MTS file", + "Unsupported file format", + "Unable to save preview", + "No biome specific blocks in this structure.", + "File saved.", + "File (re)loaded.", + "Preview saved.", + "Rotated block images generated.", + "North", + "Current Layer", + "Layer probability", + "Set Ground level", + "Gnd", + "Load", + "Save", + "Save Preview", + "Change Orientation", + "All Blocks", + "Layer Up", + "Layer Down", + "Add Blocks to Palette", + "Search:", + "Save Schematic to File", + "Filename:", + "Mapping:", + "Biome type:", + "Save" + }, + { + "hu", + "kidumpolja a rétegeket a kimenetre", + "előnézeti képet ment", + "előnézet félbevágott struktúráról", + "palló és lépcső generálás blokk képből", + "lecseréli a palettát", + "kimenet lementése", + "Betöltés...", + "Memória foglalási hiba", + "Nem találom az adatkönyvtárat. Álltsd be az MTSDATA környezeti változót.", + "Nem sikerült betölteni a blocks.csv-t", + "nincs kép ehhez", + "nincs nód ehhez", + "Nem sikerült elmenteni az MTS fájlt", + "Nem sikerült betölteni az MTS fájlt", + "Nem támogatott fájl formátum", + "Nem sikerült elmenteni az előnézeti képet", + "Nincs biom specifikus blokk a struktúrában", + "Fájl lemente.", + "Fájl (újra/be)töltve.", + "Előnézeti kép lementve.", + "Elforgatott blokk képek kigenerálva.", + "Észak", + "Aktuális szint", + "Réteg előfordulás", + "Földszint beállítása", + "Fsz", + "Betöltés", + "Mentés", + "Előnézet mentés", + "Elforgatás", + "Minden blokk", + "Réteg fel", + "Réteg le", + "Blokkok hozzáadása a palettához", + "Keresés:", + "Sematika mentése fájlba", + "Fájlnév:", + "Paletta:", + "Biom típus:", + "Elment" + } +}; diff --git a/src/lang.h b/src/lang.h new file mode 100644 index 0000000..f674b94 --- /dev/null +++ b/src/lang.h @@ -0,0 +1,77 @@ +/* + * mtsedit/lang.h + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief Multilanguage definitions + * + */ + +enum { + INF_DUMP = 0, + INF_PRE1, + INF_PRE2, + INF_GEN, + INF_MAP, + INF_OUT, + LOADING, + ERR_MEM, + ERR_DATADIR, + ERR_CSV, + ERR_IMG, + ERR_NODE, + ERR_SAVE, + ERR_LOAD, + ERR_BADFILE, + ERR_PREVIEW, + ERR_NOBIOM, + SAVED, + LOADED, + PREVIEWSAVED, + IMGSGEN, + NORTH, + CURRLAYER, + LAYERPROB, + GROUNDLEVEL, + GND, + LOAD, + SAVE, + SAVEPREVIEW, + CHANGEORIENT, + ALLBLOCKS, + LAYERUP, + LAYERDOWN, + ADDBLOCKS, + SEARCH, + SAVEAS, + FILENAME, + MAPPING, + BIOMETYPE, + SAVEBTN, + + /* must be the last */ + NUMTEXTS +}; + +#define NUMLANGS 2 + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..e7100c3 --- /dev/null +++ b/src/main.c @@ -0,0 +1,326 @@ +/* + * mtsedit/main.c + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief Main function + * + */ + +#define STB_IMAGE_IMPLEMENTATION +#include "main.h" +/* those SDL guys are not familiar with the C standard. They redefine main(), but incorrectly, they lack envp */ +#ifdef main +#undef main +int main(int argc, char** argv, char** envp); +#endif + +/* translation dictionary */ +extern char *dict[NUMLANGS][NUMTEXTS + 1]; +char **lang = &dict[0][1]; + +/** + * Windows workaround + */ +#ifdef __WIN32__ +#include + +static void UnEscapeQuotes(char *arg) +{ + char *last = NULL, *c_curr, *c_last; + + while (*arg) { + if (*arg == '"' && (last != NULL && *last == '\\')) { + c_curr = arg; + c_last = last; + + while (*c_curr) { + *c_last = *c_curr; + c_last = c_curr; + c_curr++; + } + *c_last = '\0'; + } + last = arg; + arg++; + } +} + +/* Parse a command line buffer into arguments */ +static int ParseCommandLine(char *cmdline, char **argv) +{ + char *bufp; + char *lastp = NULL; + int argc, last_argc; + + argc = last_argc = 0; + for (bufp = cmdline; *bufp;) { + /* Skip leading whitespace */ + while (SDL_isspace(*bufp)) { + ++bufp; + } + /* Skip over argument */ + if (*bufp == '"') { + ++bufp; + if (*bufp) { + if (argv) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + lastp = bufp; + while (*bufp && (*bufp != '"' || *lastp == '\\')) { + lastp = bufp; + ++bufp; + } + } else { + if (*bufp) { + if (argv) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while (*bufp && !SDL_isspace(*bufp)) { + ++bufp; + } + } + if (*bufp) { + if (argv) { + *bufp = '\0'; + } + ++bufp; + } + + /* Strip out \ from \" sequences */ + if (argv && last_argc != argc) { + UnEscapeQuotes(argv[last_argc]); + } + last_argc = argc; + } + if (argv) { + argv[argc] = NULL; + } + return (argc); +} + +int APIENTRY WinMain(__attribute__((unused)) HINSTANCE hInstance, __attribute__((unused)) HINSTANCE hPrevInstance, + __attribute__((unused)) LPSTR lpCmdLine, __attribute__((unused)) int nCmdShow) +{ + OPENFILENAME ofn; + char *cmdline = GetCommandLine(); + int ret, argc = ParseCommandLine(cmdline, NULL); + char **argv = SDL_stack_alloc(char*, argc+2); + char fn[1024]; + ParseCommandLine(cmdline, argv); + if(!argv[1]) { + memset(&fn,0,sizeof(fn)); + memset(&ofn,0,sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = NULL; + ofn.hInstance = hInstance; + ofn.lpstrFilter = "MTS Files (*.mts)\0*.mts\0All Files\0*\0\0"; + ofn.lpstrFile = fn; + ofn.nMaxFile = sizeof(fn)-1; + ofn.lpstrTitle = "Please Enter a File Name"; + if (GetOpenFileName(&ofn)) { + argc++; + argv[1] = fn; + } + } + SDL_SetMainReady(); + ret = main(argc, argv, (char**)GetEnvironmentStrings()); + SDL_stack_free(argv); + exit(ret); + return ret; +} +#endif + +/** + * Common error handler + */ +void error(char *msg) +{ +#ifdef __WIN32__ + MessageBox(NULL, msg, "MTSEdit", MB_ICONEXCLAMATION | MB_OK); +#else + fprintf(stderr, "mtsedit: %s\r\n", msg); +#endif + exit(1); +} + +/** + * The real main procedure + */ +int main(int argc, char** argv, char** envp) +{ + FILE *f; + int i, j, opt = 0, ret = 0; + char **env = envp, *newmap = NULL; +#ifndef __WIN32__ + char *home = NULL; +#endif + /* detect the language and load dictionary for it */ + for(env = envp; env && *env; env++) + if(!memcmp(*env, "LANG=", 5)) { + for(i = 0; i < NUMLANGS; i++) { + if(!memcmp(*env + 5, dict[i][0], strlen(dict[i][0]))) { + lang = &dict[i][1]; + break; + } + } + break; + } + + /* find our data directory. Environment variable takes preference */ + for(env = envp; env && *env; env++) { + if(!memcmp(*env, "MTSDATA=", 8)) { + path = (char*)malloc(strlen(*env)+256); + if(!path) error(lang[ERR_MEM]); + i = strlen(*env)-9; + memcpy(path, *env + 8, i+1); + if(path[i] != DIRSEP) { path[++i] = DIRSEP; i++; } + strcpy(path + i, "blocks.csv"); + f = fopen(path, "r"); + if(f) { path[i] = 0; fclose(f); } + else { free(path); path = NULL; } + } +#ifndef __WIN32__ + if(!memcmp(*env, "HOME=", 5)) + home = *env + 5; +#endif + } +#ifndef __WIN32__ + /* in user's home */ + if(!path && home) { + path = (char*)malloc(strlen(home)+256); + if(!path) error(lang[ERR_MEM]); + i = strlen(home)-1; + memcpy(path, home, i + 1); + if(path[i] != DIRSEP) { path[++i] = DIRSEP; i++; } + /* Linux and BSDs */ + strcpy(path + i, ".config/mtsedit/blocks.csv"); + f = fopen(path, "r"); + if(f) { path[strlen(path)-10] = 0; fclose(f); } + else { + strcpy(path + i, "Library/Application Support/MTSEdit/blocks.csv"); + f = fopen(path, "r"); + if(f) { path[strlen(path)-10] = 0; fclose(f); } + else { free(path); path = NULL; } + } + } + /* system wide */ + if(!path) { + path = (char*)malloc(26+256); + if(!path) error(lang[ERR_MEM]); + strcpy(path, "/usr/share/mtsedit/blocks.csv"); + f = fopen(path, "r"); + if(f) { path[strlen(path)-10] = 0; fclose(f); } + else { + strcpy(path, "/usr/local/share/mtsedit/blocks.csv"); + f = fopen(path, "r"); + if(f) { path[strlen(path)-10] = 0; fclose(f); } + else { free(path); path = NULL; } + } + } +#endif + /* same directory as the executable */ + if(!path) { + path = (char*)malloc(strlen(argv[0])+256); + if(!path) error(lang[ERR_MEM]); + strcpy(path, argv[0]); + home = strrchr(path, DIRSEP); + if(home) home++; else { sprintf(path, ".%c", DIRSEP); home = path + 2; } + sprintf(home, "data%cblocks.csv", DIRSEP); + f = fopen(path, "r"); + if(f) { path[strlen(path)-10] = 0; fclose(f); } + else { + /* last resort, development repository or /opt/mtsedit/bin, data in parent directory */ + sprintf(home, "..%cdata%cblocks.csv", DIRSEP, DIRSEP); + f = fopen(path, "r"); + if(f) { path[strlen(path)-10] = 0; fclose(f); } + else { free(path); path = NULL; } + } + } + if(!path) error(lang[ERR_DATADIR]); + fn = path + strlen(path); + if(argc < 2) { + printf("MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license\r\n\r\n" + "./mtsedit [-d|-p|-P|-g] [-m map] <.mts|.schematic> [out.mts]\r\n\r\n -d: %s\r\n -p: %s\r\n -P: %s\r\n" + " -g: %s\r\n -m map: %s\r\n out.mts: %s\r\n", + lang[INF_DUMP], lang[INF_PRE1], lang[INF_PRE2], lang[INF_GEN], lang[INF_MAP], lang[INF_OUT]); + exit(0); + } + i = 1; + for(j = 1; j < argc; j++) { + if(argv[j][0] == '-') { + switch(argv[j][1]) { + case 'd': opt = 1; break; + case 'p': opt = 2; break; + case 'P': opt = 3; break; + case 'g': opt = 4; break; + case 'm': if(!opt) { opt = 5; } newmap = argv[++j]; break; + } + } else { + i = j; break; + } + } + strncpy(mtsfile, argv[i], sizeof(mtsfile)); + savelen = savepos = strlen(argv[i]); + tmpblk = (unsigned char*)malloc(32 * 32 * 4); + if(!tmpblk) error(lang[ERR_MEM]); + + if(argv[i+1] && !opt) opt = 5; + if(opt && opt != 4) { + parseblocks(); + mts_load(mtsfile); + if(newmap) { + j = atoi(newmap); + if(j) savepal = j - 1; + else { + for(j = 0; j < numpalettes; j++) + if(!strcmp(palettes[j], newmap)) { savepal = j; break; } + } + } + if(argv[i+1]) { + strncpy(mtsfile, argv[i+1], sizeof(mtsfile)); + ret = mts_save(); + } + } + switch(opt) { + case 1: ret = mts_dump(); break; + case 2: + case 3: ret = mts_view(opt-2); break; + case 4: ret = mts_stairgen(); break; + case 5: break; + default: + /* start the main user interface */ + sdlmain(); + break; + } + + free(path); + free(tmpblk); + return ret; +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..47daff9 --- /dev/null +++ b/src/main.h @@ -0,0 +1,168 @@ +/* + * mtsedit/main.h + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief Main header + * + */ + +#ifndef _MSC_VER +#define _inline __inline__ +#define _pack __attribute__((packed)) +#define _unused __attribute__((unused)) +#else +#define _inline +#define _pack +#define _unused +#endif +#ifndef __cplusplus +#define _register register +#else +#define _register +#endif + +#define THEME_INPUT 0 +#define THEME_TABA 1 +#define THEME_FG 2 +#define THEME_TABU 3 +#define THEME_INACT 4 +#define THEME_BGLOGO 5 +#define THEME_BG 6 +#define THEME_INPBG 7 +#define THEME_TABBG 8 +#define THEME_SAVEACT 9 +#define THEME_SAVEINACT 10 + +#include +#include +#include +#include +#include "stb_image.h" +#include "lang.h" +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#ifdef __WIN32__ +extern char binary_icons_png_start; +extern char binary_icons_png_end; +extern char binary_font_psf_gz_start; +extern char binary_font_psf_gz_end; +#define icons_start binary_icons_png_start +#define icons_end binary_icons_png_end +#define font_start binary_font_psf_gz_start +#define font_end binary_font_psf_gz_end +#define DIRSEP '\\' +#else +extern char _binary_icons_png_start; +extern char _binary_icons_png_end; +extern char _binary_font_psf_gz_start; +extern char _binary_font_psf_gz_end; +#define icons_start _binary_icons_png_start +#define icons_end _binary_icons_png_end +#define font_start _binary_font_psf_gz_start +#define font_end _binary_font_psf_gz_end +#define DIRSEP '/' +#endif + +typedef struct { + char *name; + char **blocknames; + int numref; + char dobiome; + unsigned char blockids[8]; + unsigned char *img[32]; + unsigned char *dr[32]; + unsigned char *tr[32]; +} mtsblock_t; + +extern int numpalettes, lenpalettes, palette[16], strmaxw, savelen, savepos, savepal, savebiome, numresults, *results; +extern int currlayer, gndlayer, mts_x, mts_y, mts_z, min_x, min_y, min_z, dx, dz, cx, cz, ox, oy, up; +extern int shift, ctrl, activeblock; +extern char **palettes, layerprob[256]; +extern int numblocks; +extern mtsblock_t *blocks; + +typedef struct { + unsigned short param0; + unsigned char param1; + unsigned char param2; +} node_t; +extern node_t nodes[256][256][256]; + +/* PC Screen Font as used by Linux Console */ +typedef struct { + unsigned int magic; + unsigned int version; + unsigned int headersize; + unsigned int flags; + unsigned int numglyph; + unsigned int bytesperglyph; + unsigned int height; + unsigned int width; + unsigned char glyphs; +} _pack psf_t; + +extern psf_t *font; +extern uint32_t theme[]; +extern char *path, *fn, mtsfile[MAXPATHLEN], *status, **lang; +extern unsigned char *tmpblk; +extern SDL_Window *window; +extern SDL_Surface *screen, *icons, *blk, *fg, *bg, *cursor; + +void error(char *msg); + +void mts_load(); +int mts_save(); +int mts_view(int type); +int mts_dump(); +void mts_layerprob(int diff); +void mts_rotate(); +int mts_stairgen(); + +void parseblocks(); +void freeblocks(); + +void sdlprint(int x, int y, int fg, int bg, char *s); +void sdldosave(); +void sdlredraw(); +void sdlmain(); + +void edit_redraw(int full); +void edit_hidecursor(); +void edit_scroll(SDL_Event *event); +void edit_mouseover(SDL_Event *event); +void edit_mousedown(_unused SDL_Event *event); +void edit_rotate(int y, int z, int x); + +void save_redraw(); +void save_scroll(SDL_Event *event); +void save_mousedown(SDL_Event *event); +void save_key(SDL_Event *event); + +void search_redraw(); +void search_scroll(SDL_Event *event); +void search_mousedown(SDL_Event *event); +void search_mouseover(SDL_Event *event); +void search_key(SDL_Event *event); diff --git a/src/mts.c b/src/mts.c new file mode 100644 index 0000000..f44496a --- /dev/null +++ b/src/mts.c @@ -0,0 +1,746 @@ +/* + * mtsedit/mts.c + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief MTS file format importer / exporter + * + */ + +#include "main.h" + +char mtsfile[MAXPATHLEN]; +/* block type mapping palettes */ +int numpalettes = 0, lenpalettes = 0; +char **palettes = NULL; +/* blocks types */ +int numblocks = 0; +mtsblock_t *blocks = NULL; + +/* the actual MTS in memory */ +int mts_x = 0, mts_y = 0, mts_z = 0; +int currlayer = 127, gndlayer = 0; +char layerprob[256]; +node_t nodes[256][256][256]; + +/** + * Load a Minecraft NBT Schematic file + */ +#define SCHEM_GETINT(v,t) do{switch(t){case 1:v=d[0];d++;break;case 2:v=(d[0]<<8)|d[1];d+=2;break; \ + case 3:v=(d[0]<<24)|(d[1]<<16)|(d[2]<<8)|d[3];d+=4;break;}}while(0) +void schem_load(unsigned char *data, unsigned int size) +{ + int i, j, k, x, y, z, min_x = 0, min_y = 0, min_z = 0; + unsigned char *d, *end = data + size; + + /* we don't care about that overcomplicated NBT mess */ + mts_y = mts_z = mts_x = 0; + for(d = data; d < end && (!mts_y || !mts_z || !mts_x); d++) { + if(!memcmp(d, "\000\006Height", 8)) { d += 8; SCHEM_GETINT(mts_y, d[-9]); d--; } else + if(!memcmp(d, "\000\006Length", 8)) { d += 8; SCHEM_GETINT(mts_z, d[-9]); d--; } else + if(!memcmp(d, "\000\005Width", 7)) { d += 7; SCHEM_GETINT(mts_x, d[-8]); d--; } + } + if(!mts_y || !mts_z || !mts_x || mts_y > 255 || mts_z > 255 || mts_x > 255) { mts_y = mts_z = mts_x = 0; return; } + blocks[0].numref -= mts_y * mts_z * mts_x; + min_x = 127 - mts_x / 2; + min_y = 127 - mts_y / 2; + min_z = 127 - mts_z / 2; + /* now that we know the dimensions, find the blocks data */ + for(d = data; d < end; d++) { + if(!memcmp(d, "\007\000\006Blocks", 9)) { + d += 9; + SCHEM_GETINT(i, 3); + if(i != mts_y * mts_z * mts_x) return; + for(y = 0; y < mts_y; y++) + for(z = 0; z < mts_z; z++) + for(x = 0; x < mts_x; x++, d++) { + if(!*d) continue; + nodes[y+min_y][z+min_z][x+min_x].param1 = 127; + for(i = 1, k = 0; i < numblocks; i++) + for(j = 0; j < 8; j++) + if(blocks[i].blockids[j] == *d) { k = i; i = numblocks; break; } + if(k) { + nodes[y+min_y][z+min_z][x+min_x].param0 = k; + blocks[k].numref++; + } else + fprintf(stderr, "mtsedit: %s: %s %d\r\n", mtsfile, lang[ERR_NODE], *d); + } + status = lang[LOADED]; + break; + } + } +} + +/** + * Load an MTS file + */ +void mts_load() +{ + FILE *f; + unsigned char *data = NULL, *buff = NULL, *un = NULL, *b; + unsigned int size = 0, idx; + int i, j, k, l, n, x, y, z, min_x = 0, min_y = 0, min_z = 0; + unsigned short *tr; + + status = lang[ERR_LOAD]; + mts_x = mts_y = mts_z = 0; currlayer = 127; + memset(layerprob, 127, sizeof(layerprob)); + memset(nodes, 0, sizeof(nodes)); + for(i = 0; i < numblocks; i++) { + blocks[i].dobiome = 0; + blocks[i].numref = 0; + } + blocks[0].numref = 256 * 256 * 256; + savepal = savebiome = 0; + + f = fopen(mtsfile, "rb"); + if(f) { + fseek(f, 0L, SEEK_END); + size = (unsigned int)ftell(f); + fseek(f, 0L, SEEK_SET); + data = (unsigned char*)malloc(size); + if(!data) error(lang[ERR_MEM]); + fread(data, size, 1, f); + fclose(f); + status = lang[ERR_BADFILE]; + /* if it's an MTS file */ + if(!memcmp(data, "MTSM", 4)) { + if(!data[4] && (data[5] == 3 || data[5] == 4) && !data[6] && data[7] && !data[8] && data[9] && + !data[10] && data[11]) { + mts_x = data[7]; mts_y = data[9]; mts_z = data[11]; + blocks[0].numref -= mts_y * mts_z * mts_x; + min_x = 127 - mts_x / 2; + min_y = 127 - mts_y / 2; + min_z = 127 - mts_z / 2; + buff = data + 12; + if(data[5] == 4) { + memcpy(layerprob + min_y, buff, mts_y); + buff += mts_y; + } + n = (buff[0] << 8) | buff[1]; + if(n > 0 && n < 1024) { + buff += 2; + tr = (unsigned short*)malloc(n * sizeof(unsigned short)); + if(!tr) error(lang[ERR_MEM]); + for(i = 0; i < n; i++) { + tr[i] = numblocks; + j = buff[1]; + buff += 2; + for(k = 0; k < numblocks; k++) + for(l = 0; l < numpalettes + 1; l++) + if((!blocks[k].blocknames && l && (int)strlen(blocks[k].name) == j && + !memcmp(blocks[k].name, buff, j)) || (blocks[k].blocknames && blocks[k].blocknames[l] && + (int)strlen(blocks[k].blocknames[l]) == j && !memcmp(blocks[k].blocknames[l], buff, j))) { + if(!l && k) { blocks[k].dobiome = 1; savebiome = 1; } + if(l && l - 1 > savepal) savepal = l - 1; + tr[i] = k; + break; + } + if(tr[i] == numblocks) { + x = buff[j]; buff[j] = 0; + fprintf(stderr, "mtsedit: %s: %s %d '%s'\r\n", mtsfile, lang[ERR_NODE], i, buff); + buff[j] = x; + idx = numblocks++; + blocks = (mtsblock_t*)realloc(blocks, numblocks * sizeof(mtsblock_t)); + if(!blocks) error(lang[ERR_MEM]); + memset(&blocks[idx], 0, sizeof(mtsblock_t)); + blocks[idx].name = (char*)malloc(j + 1); + if(!blocks[idx].name) error(lang[ERR_MEM]); + memcpy(blocks[idx].name, buff, j); + blocks[idx].name[j] = 0; + } + buff += j; + } + size -= (int)(buff - data); + un = (uint8_t*)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)buff, size, 4096, &i, 1); + if(un) { buff = un; size = (unsigned int)i; } + if(4 * mts_x * mts_y * mts_z <= (int)size) { + for(z = 0, b = buff; z < mts_z; z++) + for(y = 0; y < mts_y; y++) + for(x = 0; x < mts_x; x++, b += 2) { + nodes[y+min_y][z+min_z][x+min_x].param0 = tr[(b[0] << 8) | b[1]]; + blocks[nodes[y+min_y][z+min_z][x+min_x].param0].numref++; + } + for(z = 0; z < mts_z; z++) + for(y = 0; y < mts_y; y++) + for(x = 0; x < mts_x; x++, b++) + nodes[y+min_y][z+min_z][x+min_x].param1 = data[5] == 3 ? b[0] >> 1 : b[0]; + for(z = 0; z < mts_z; z++) + for(y = 0; y < mts_y; y++) + for(x = 0; x < mts_x; x++, b++) + nodes[y+min_y][z+min_z][x+min_x].param2 = b[0]; + gndlayer = currlayer = min_y; + for(y = 0; y < mts_y; y++) + for(z = 0; z < mts_z; z++) + for(x = 0; x < mts_x; x++) + if(nodes[y+min_y][z+min_z][x+min_x].param2 == 0x20) { + gndlayer = currlayer = y+min_y; + nodes[y+min_y][z+min_z][x+min_x].param2 = 0; + } + status = lang[LOADED]; + } else mts_x = mts_y = mts_z = 0; + free(tr); + if(un) free(un); + } else mts_x = mts_y = mts_z = 0; + buff = NULL; + } + } else { + /* Could be a Minecraft NBT */ + if(data[0] == 0x1f && data[1] == 0x8b) { + /* skip over gzip header */ + buff = data + 3; + i = *buff++; buff += 6; + if(i & 4) { j = *buff++; j += (*buff++ << 8); buff += j; } + if(i & 8) { while(*buff++ != 0); } + if(i & 16) { while(*buff++ != 0); } + if(i & 2) buff += 2; + size -= (int)(buff - data); + } else buff = data; + buff = (uint8_t*)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)buff, size, 4096, &i, 0); + if(buff) { free(data); data = buff; buff = NULL; size = (unsigned int)i; } + if(!memcmp(data + 3, "Schematic", 9)) schem_load(data, size); + } + /* make sure that all non-air blocks have their probability set */ + for(y = 0; y < mts_y; y++) + for(z = 0; z < mts_z; z++) + for(x = 0; x < mts_x; x++, b++) + if(nodes[y+min_y][z+min_z][x+min_x].param0 && !nodes[y+min_y][z+min_z][x+min_x].param1) + nodes[y+min_y][z+min_z][x+min_x].param1 = 127; + } + if(data) free(data); + if(buff) free(buff); +} + +/** + * Save an MTS file + */ +int mts_save() +{ + char outfile[MAXPATHLEN], *c; + unsigned short *tr, *tr2; + unsigned char *b, *b2, hdr[12]; + int i, j, k, x, y, z, mix = 255, max = 0, miy = 255, may = 0, miz = 255, maz = 0; + FILE *f; + + status = lang[ERR_SAVE]; + tr = (unsigned short*)malloc(numblocks * sizeof(unsigned short)); + if(!tr) error(lang[ERR_MEM]); + memset(tr, 0, numblocks * sizeof(unsigned short)); + tr2 = (unsigned short*)malloc(numblocks * sizeof(unsigned short)); + if(!tr2) error(lang[ERR_MEM]); + memset(tr2, 0, numblocks * sizeof(unsigned short)); + /* get boundind box and block ID translation tables, sanitize probability values */ + j = 1; + for(y = 0; y < 256; y++) { + for(z = k = 0; z < 256; z++) + for(x = 0; x < 256; x++) { + if(nodes[y][z][x].param0) { + if(!tr2[nodes[y][z][x].param0]) { + tr2[nodes[y][z][x].param0] = j; + tr[j++] = nodes[y][z][x].param0; + } + if(x < mix) mix = x; + if(x > max) max = x; + if(y < miy) miy = y; + if(y > may) may = y; + if(z < miz) miz = z; + if(z > maz) maz = z; + if(!nodes[y][z][x].param1) nodes[y][z][x].param1 = 127; + if(!layerprob[y]) layerprob[y] = 127; + k = 1; + } else + nodes[y][z][x].param1 = 0; + } + if(!k) layerprob[y] = 0; + } + if(mix > max) { free(tr); free(tr2); return 1; } + + for(y = miy; y <= may; y++) { + /* close windows and doors */ + for(z = miz+1; z < maz; z++) + for(x = mix+1; x < max; x++) + if(!nodes[y][z][x].param0 && !nodes[y][z][x].param1 && ( + (nodes[y][z-1][x].param1 && (nodes[y][z+1][x].param1 || (z<254 && nodes[y][z+2][x].param1))) || + (nodes[y][z][x-1].param1 && (nodes[y][z][x+1].param1 || (x<254 && nodes[y][z][x+2].param1))))) + nodes[y][z][x].param1 = 127; + /* mark air in rooms temporarly */ + for(z = miz+1; z < maz; z++) + for(x = mix+1; x < max; x++) + if(!nodes[y][z][x].param0 && !nodes[y][z][x].param1 && + (nodes[y][z-1][x].param1 || nodes[y][z][x-1].param1)) + nodes[y][z][x].param1 = 1; + /* clear rooms which are connected with the outside */ + do { + k = 0; + for(z = miz+1; z < maz; z++) + for(x = mix+1; x < max; x++) + if(nodes[y][z][x].param1 == 1 && + (!nodes[y][z-1][x].param1 || !nodes[y][z+1][x].param1 || !nodes[y][z][x-1].param1 || + !nodes[y][z][x+1].param1)) { + nodes[y][z][x].param1 = 0; + k = 1; + } + } while(k); + /* make air in closed rooms permanent */ + for(z = miz+1; z < maz; z++) + for(x = mix+1; x < max; x++) + if(nodes[y][z][x].param1 == 1) nodes[y][z][x].param1 = 127; + /* mark ground level */ + if(y == gndlayer) + for(z = miz; z <= maz; z++) + for(x = mix; x <= max; x++) + if(!nodes[y][z][x].param0) nodes[y][z][x].param2 = 0x20; + } + + /* get filename, buffer and open file */ + strcpy(outfile, mtsfile); + c = strrchr(outfile, '.'); + if(!c) c = &outfile[strlen(outfile)]; + strcpy(c, ".mts"); + f = fopen(outfile,"wb"); + if(!f) return 1; + b = (unsigned char*)malloc((max-mix+1) * (may-miy+1) * (maz-miz+1) * 4); + if(!b) error(lang[ERR_MEM]); + memset(b, 0, (max-mix+1) * (may-miy+1) * (maz-miz+1) * 4); + memset(hdr, 0, sizeof(hdr)); + + /* write header */ + memcpy(hdr, "MTSM", 4); + for(hdr[5] = 3, y = miy; y <= may; y++) + if(layerprob[y] != 127) { hdr[5] = 4; break; } + hdr[7] = max-mix+1; + hdr[9] = may-miy+1; + hdr[11] = maz-miz+1; + fwrite(hdr, 12, 1, f); + if(hdr[5] == 4) + fwrite(layerprob + miy, may-miy+1, 1, f); + + /* write Name-ID table */ + hdr[0] = 0; hdr[1] = j; + fwrite(hdr, 2, 1, f); + if(savepal < 0 || savepal >= numpalettes) savepal = 0; + for(i = 0; i < j; i++) { + c = !blocks[tr[i]].blocknames ? blocks[tr[i]].name : (savebiome && blocks[tr[i]].blocknames[0] && + blocks[tr[i]].dobiome ? blocks[tr[i]].blocknames[0] : (blocks[tr[i]].blocknames[savepal+1] ? + blocks[tr[i]].blocknames[savepal+1] : blocks[tr[i]].name)); + hdr[1] = strlen(c); + fwrite(hdr, 2, 1, f); + fwrite(c, hdr[1], 1, f); + } + + /* construct param buffer */ + for(z = miz, b2 = b; z <= maz; z++) + for(y = miy; y <= may; y++) + for(x = mix; x <= max; x++, b2 += 2) { + b2[0] = tr2[nodes[y][z][x].param0] >> 8; + b2[1] = tr2[nodes[y][z][x].param0] & 0xFF; + } + for(z = miz; z <= maz; z++) + for(y = miy; y <= may; y++) + for(x = mix; x <= max; x++, b2++) + *b2 = hdr[5] == 4 ? nodes[y][z][x].param1 : ((nodes[y][z][x].param1 << 1) | (nodes[y][z][x].param1 ? 1 : 0)); + for(z = miz; z <= maz; z++) + for(y = miy; y <= may; y++) + for(x = mix; x <= max; x++, b2++) + *b2 = nodes[y][z][x].param2; + /* compress and save */ + x = 1; + b2 = stbi_zlib_compress(b, (max-mix+1) * (may-miy+1) * (maz-miz+1) * 4, &i, 9); + if(b2) { + fwrite(b2, i, 1, f); + free(b2); + status = lang[SAVED]; + x = 0; + } + fclose(f); + free(b); + free(tr); + free(tr2); + return x; +} + +/** + * Save a PNG preview file + */ +int mts_view(int type) +{ + int i, j, w, h, x, y, z, mix = 255, max = 0, miy = 255, may = 0, miz = 255, maz = 0; + int miw = 65536, maw = 0, mih = 65536, mah = 0, r = 0, k, n; + char pngfile[MAXPATHLEN], *c; + unsigned char *buff = NULL; + SDL_Surface *preview, *blk; + SDL_Rect src, dst; + FILE *f; + + status = lang[ERR_PREVIEW]; + for(y = 0; y < 256; y++) + for(z = 0; z < 256; z++) + for(x = 0; x < 256; x++) { + if(nodes[y][z][x].param0) { + if(x < mix) mix = x; + if(x > max) max = x; + if(y < miy) miy = y; + if(y > may) may = y; + if(z < miz) miz = z; + if(z > maz) maz = z; + i = dx * ((x-128) - (z-128)); + j = dz * ((z-128) + (x-128)); + if(i < miw) miw = i; + if(i > maw) maw = i; + if(j < mih) mih = j; + if(j > mah) mah = j; + } + } + if(mix > max) return 1; + w = (((max - mix) > (maz - miz) ? (max - mix) : (maz - miz)) + 1) * 2 * dx + 32; + h = mah - mih + dz + (2 * (may - miy) + 2) * dz + 32; + blk = SDL_CreateRGBSurfaceFrom(NULL, 32, 32, 32, 32*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + preview = SDL_CreateRGBSurface(0, w, h, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + src.x = src.y = 0; src.w = src.h = dst.w = dst.h = 32; + n = ((max-mix)+(maz-miz))/2; + for(y = miy; y <= may; y++) + for(z = miz; z <= maz; z++) { + if(type) r = ((z-miz+1)*(max-mix+1)/(maz-miz+1))/(may-y+1); + for(x = miz; x <= max - r; x++) { + if(nodes[y][z][x].param0) { + dst.x = dx * ((x-(max-mix)/2) - (z-(maz-miz)/2) - 1) + w/2; + dst.y = h - (mah - mih) - (y-miy+2)*2*dz + dz * ((z-miz) + (x-mix)); + i = nodes[y][z][x].param0; + j = nodes[y][z][x].param2 & 0x1F; + if(i && !blocks[i].img[j]) j = 0; + if(i && blocks[i].img[j]) { + memcpy(tmpblk, blocks[i].img[j], 32 * 32 * 4); + for(j = 0; j < 32 * 32 * 4; j += 4) { + k = (int)(tmpblk[j+0]) * ((x-mix)+(z-miz)) / n; + tmpblk[j+0] = k > 255 ? 255 : k; + k = (int)(tmpblk[j+1]) * ((x-mix)+(z-miz)) / n; + tmpblk[j+1] = k > 255 ? 255 : k; + k = (int)(tmpblk[j+2]) * ((x-mix)+(z-miz)) / n; + tmpblk[j+2] = k > 255 ? 255 : k; + } + blk->pixels = tmpblk; + SDL_BlitSurface(blk, &src, preview, &dst); + } + } + } + } + buff = (unsigned char *)stbi_write_png_to_mem(preview->pixels, preview->pitch, preview->w, preview->h, 4, &i); + if(buff) { + strcpy(pngfile, mtsfile); + c = strrchr(pngfile, '.'); + if(!c) c = &pngfile[strlen(pngfile)]; + strcpy(c, ".png"); + f = fopen(pngfile,"wb"); + if(f) { + fwrite(buff, i, 1, f); + fclose(f); + status = lang[PREVIEWSAVED]; + fprintf(stderr, "mtsedit: %s: %s\r\n", pngfile, status); + } + free(buff); + } + SDL_FreeSurface(preview); + SDL_FreeSurface(blk); + return 0; +} + +/** + * Change layer probability + */ +void mts_layerprob(int diff) +{ + if(diff < 0 && (int)layerprob[currlayer] > 0) layerprob[currlayer]--; + if(diff > 0 && (int)layerprob[currlayer] < 127) layerprob[currlayer]++; +} + +/** + * Rotate the entire MTS structure + */ +void mts_rotate() +{ + int x, y, z; + node_t layer[256][256]; + + for(y = 0; y < 256; y++) { + memcpy(layer, nodes[y], sizeof(node_t)*256*256); + for(z = 0; z < 256; z++) + for(x = 0; x < 256; x++) { + memcpy(&nodes[y][z][x], &layer[x][255-z], sizeof(node_t)); + edit_rotate(y, z, x); + } + } +} + +/** + * Dump structure + */ +int mts_dump() +{ + unsigned short *tr, *tr2; + int i, j, x, y, z, mix = 255, max = 0, miy = 255, may = 0, miz = 255, maz = 0; + + tr = (unsigned short*)malloc(numblocks * sizeof(unsigned short)); + if(!tr) error(lang[ERR_MEM]); + memset(tr, 0, numblocks * sizeof(unsigned short)); + tr2 = (unsigned short*)malloc(numblocks * sizeof(unsigned short)); + if(!tr2) error(lang[ERR_MEM]); + memset(tr2, 0, numblocks * sizeof(unsigned short)); + j = 1; + for(y = 0; y < 256; y++) + for(z = 0; z < 256; z++) + for(x = 0; x < 256; x++) { + if(nodes[y][z][x].param0) { + if(!tr2[nodes[y][z][x].param0]) { + tr2[nodes[y][z][x].param0] = j; + tr[j++] = nodes[y][z][x].param0; + } + if(x < mix) mix = x; + if(x > max) max = x; + if(y < miy) miy = y; + if(y > may) may = y; + if(z < miz) miz = z; + if(z > maz) maz = z; + } + } + if(mix > max) { free(tr); free(tr2); return 1; } + printf("-------------------------------- %s --------------------------------\r\n", mtsfile); + printf("Map (x: %d y: %d z: %d):\r\n", max-mix+1, may-miy+1, maz-miz+1); + if(savepal < 0 || savepal >= numpalettes) savepal = 0; + for(i = 0; i < j; i++) + printf("%4x: %s\r\n", i, blocks[tr[i]].blocknames && blocks[tr[i]].blocknames[savepal+1] ? + blocks[tr[i]].blocknames[savepal+1] : blocks[tr[i]].name); + + for(y = miy; y <= may; y++) { + printf("\r\nLayer %d (probability %d%s):\r\n", y - miy, layerprob[y], y == gndlayer ? ", Ground level" : ""); + for(z = miz; z <= maz; z++) { + if(j < 15) printf(" "); + for(x = mix; x <= max; x++) { + if(nodes[y][z][x].param0) { + if(j < 15) printf("%x", tr2[nodes[y][z][x].param0]); + else printf(" %02x", tr2[nodes[y][z][x].param0]); + } else printf(j < 15 ? "." : " .."); + } + printf(" "); + for(x = mix; x <= max; x++) + printf(" %02x", nodes[y][z][x].param1); + printf(" "); + for(x = mix; x <= max; x++) + printf(" %02x", y == gndlayer && !nodes[y][z][x].param0 ? 32 : nodes[y][z][x].param2); + printf("\r\n"); + } + } + + free(tr); + free(tr2); + return 0; +} + +/** + * Generate slab and stair pictures from block image + */ +int mts_stairgen() +{ + FILE *f; + stbi__context sc; + stbi__result_info ri; + int i, j, w, h, l, x0,x1,x2, y0,y1,y2,y3,y4; + unsigned int size; + unsigned char *d, *data, *block, *slab, *stair; + char *c; + + f = fopen(mtsfile, "rb"); + if(f) { + fseek(f, 0L, SEEK_END); + size = (unsigned int)ftell(f); + fseek(f, 0L, SEEK_SET); + data = (unsigned char*)malloc(size); + if(!data) error(lang[ERR_MEM]); + fread(data, size, 1, f); + fclose(f); + c = strrchr(mtsfile, '_'); + if(!c) c = strrchr(mtsfile, '.'); + if(!c) c = &mtsfile[strlen(mtsfile)]; + sc.read_from_callbacks = 0; + sc.img_buffer = sc.img_buffer_original = data; + sc.img_buffer_end = sc.img_buffer_original_end = data + size; + ri.bits_per_channel = 8; + block = (unsigned char*)stbi__png_load(&sc, &w, &h, &l, 0, &ri); + free(data); + if(!block) return 1; + slab = (unsigned char *)malloc(w * h * 4); + if(!slab) error(lang[ERR_MEM]); + memset(slab, 0, w * h * 4); + stair = (unsigned char *)malloc(w * h * 4); + if(!stair) error(lang[ERR_MEM]); + /* detect cube edges */ + for(d = block; d[3] < 128; d += 4); + x1 = ((int)(d - block) % (w * 4)) / 4; + y0 = (int)(d - block) / (w * 4); + for(i = 0, d = block; i < w/2 && d[3] < 128; i++) + for(j = 0, d = block + i*4; j < h/2 && d[3] < 128; j++, d += w*4); + x0 = ((int)(d - block) % (w * 4)) / 4 - 1; + y1 = (int)(d - block) / (w * 4); + x2 = x1 + (x1 - x0) + 1; + y2 = y1 + (y1 - y0); + for(; d[3] > 128; d += w*4); + d -= w*4; + y3 = (int)(d - block) / (w * 4); + y4 = y3 + (y1 - y0); +#if DEBUG + *((uint32_t*)(slab + y0*w*4 + x1*4)) = 0xFF0000FF; + *((uint32_t*)(slab + y1*w*4 + x0*4)) = 0xFF0000FF; + *((uint32_t*)(slab + y1*w*4 + x2*4)) = 0xFF00FF00; + *((uint32_t*)(slab + y2*w*4 + x1*4)) = 0xFF00FF00; + *((uint32_t*)(slab + y3*w*4 + x0*4)) = 0xFF0000FF; + *((uint32_t*)(slab + y3*w*4 + x2*4)) = 0xFF00FF00; + *((uint32_t*)(slab + y4*w*4 + x1*4)) = 0xFF00FF00; +#endif + + /* rotated block */ + for(i = x0; i <= x2+1; i++) { + for(j = y0; j <= y4; j++) + *((uint32_t*)(slab + j*w*4 + (x2 - i + x0 + (x0 & 1))*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + data = (unsigned char *)stbi_write_png_to_mem(slab, w*4, w, h, 4, &l); + if(data) { + strcpy(c, "_01.png"); + f = fopen(mtsfile,"wb"); + if(f) { fwrite(data, l, 1, f); fclose(f); } + free(data); + } + + /* slab */ + memset(slab, 0, w * h * 4); + for(i = x0; i <= x1; i++) { + l = (y1 - y0) * (i - x0) / (x1 - x0); + for(j = y0; j < y1 + (y3 - y1)/2 + l; j++) + *((uint32_t*)(slab + (j+(y3 - y1)/2+1)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + for(j = y3 + l; j < h; j++) + *((uint32_t*)(slab + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + for(i = x1+1; i <= x2; i++) { + l = (y1 - y0) * ((x2-i+1) - x1) / (x2 - x1); + for(j = y0; j < y2 + (y3 - y1)/2 + l; j++) + *((uint32_t*)(slab + (j+(y3 - y1)/2+1)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + for(j = y4 + l; j < h; j++) + *((uint32_t*)(slab + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + + data = (unsigned char *)stbi_write_png_to_mem(slab, w*4, w, h, 4, &l); + if(data) { + strcpy(c, "_Slab_00.png"); + f = fopen(mtsfile,"wb"); + if(f) { fwrite(data, l, 1, f); fclose(f); } + free(data); + } + + /* rotated stairs */ + memcpy(stair, slab, w * h * 4); + for(i = x0; i <= x1 + (x2 - x1)/2; i++) { + l = (y1 - y0) * (i - x0) / (x1 - x0); + for(j = y0 + l; j < y4; j++) + *((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + for(i = x1; i <= x2; i++) { + l = (y1 - y0) * (i - x1) / (x1 - x0); + for(j = y0 + l; j <= y0 + l + 1; j++) + *((uint32_t*)(stair + (j+(y1-y0)/2)*w*4 + (i - (x1 - x0)/2)*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + data = (unsigned char *)stbi_write_png_to_mem(stair, w*4, w, h, 4, &l); + if(data) { + strcpy(c, "_Stair_00.png"); + f = fopen(mtsfile,"wb"); + if(f) { fwrite(data, l, 1, f); fclose(f); } + free(data); + } + + memcpy(stair, slab, w * h * 4); + for(i = x0 + (x1 - x0)/2; i <= x2; i++) { + l = (y1 - y0) * ((x2-i+1) - x1) / (x2 - x1); + for(j = y1 + l; j < y4; j++) + *((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + for(i = x0; i <= x1; i++) { + l = (y1 - y0) * ((x1-i+1) - x1) / (x1 - x0); + for(j = y1 + l; j <= y2 + l; j++) + *((uint32_t*)(stair + (j+(y1-y0)/2)*w*4 + (i+(x1 - x0)/2)*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + data = (unsigned char *)stbi_write_png_to_mem(stair, w*4, w, h, 4, &l); + if(data) { + strcpy(c, "_Stair_01.png"); + f = fopen(mtsfile,"wb"); + if(f) { fwrite(data, l, 1, f); fclose(f); } + free(data); + } + + memcpy(stair, slab, w * h * 4); + for(i = x0 + (x1 - x0)/2; i <= x1 + (x2 - x1)/2; i++) { + l = (y1 - y0) * (i - x0) / (x1 - x0); + for(j = y0; j <= y0 + l; j++) + *((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + for(; i <= x2; i++) { + for(j = y0; j <= y4; j++) + *((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + for(i = x0; i < x1; i++) { + l = (y1 - y0) * (i - x0) / (x1 - x0); + for(j = y1 + l - 1; j <= y1 + (y3 - y1)/2 + l; j++) + *((uint32_t*)(stair + (j-(y1-y0)/2)*w*4 + (i + (x1 - x0)/2)*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + data = (unsigned char *)stbi_write_png_to_mem(stair, w*4, w, h, 4, &l); + if(data) { + strcpy(c, "_Stair_02.png"); + f = fopen(mtsfile,"wb"); + if(f) { fwrite(data, l, 1, f); fclose(f); } + free(data); + } + + memcpy(stair, slab, w * h * 4); + for(i = x0; i <= x0 + (x1 - x0)/2 +1; i++) { + for(j = y0; j <= y4; j++) + *((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + for(; i <= x2; i++) { + l = (y1 - y0) * (i - (x0 + (x1 - x0)/2)) / (x2 - x1); + for(j = y0; j <= y1 + (y2 - y1)/2 - l; j++) + *((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + for(i = x1+1; i <= x2; i++) { + l = (y1 - y0) * ((x2-i+1) - x1) / (x2 - x1); + for(j = y2 + l - 1; j <= y2 + (y3 - y1)/2 + l; j++) + *((uint32_t*)(stair + (j-(y1-y0)/2)*w*4 + (i-(x1-x0)/2)*4)) = *((uint32_t*)(block + j*w*4 + i*4)); + } + data = (unsigned char *)stbi_write_png_to_mem(stair, w*4, w, h, 4, &l); + if(data) { + strcpy(c, "_Stair_03.png"); + f = fopen(mtsfile,"wb"); + if(f) { fwrite(data, l, 1, f); fclose(f); } + free(data); + } + + fprintf(stderr, "mtsedit: %s\r\n", lang[IMGSGEN]); + return 0; + } + return 1; +} diff --git a/src/save.c b/src/save.c new file mode 100644 index 0000000..e072943 --- /dev/null +++ b/src/save.c @@ -0,0 +1,239 @@ +/* + * mtsedit/save.c + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief Save file window + * + */ + +#include "main.h" + +int savelen, savepos, savefld = 0, savepal = 0, savebiome = 0, biomepos = 0, biomeblk = -1, maxbiome = 0, biomescr = 0; +extern char cur[2]; + +/** + * Redraw the Save file window + */ +void save_redraw() +{ + unsigned int i, j; + int k, l; + SDL_Rect rect; + + strmaxw = screen->w - 5; + sdlprint((screen->w - 47 - strlen(lang[SAVEAS]) * (font->width+1)) / 2 + 47, 4, THEME_INPUT, THEME_BG, lang[SAVEAS]); + rect.y = 12 + font->height; + sdlprint(42, rect.y, THEME_FG, THEME_BG, lang[FILENAME]); + rect.x = 160; rect.w = screen->w - 165; rect.h = font->height + 2; + SDL_FillRect(screen, &rect, theme[THEME_INPBG]); + strmaxw = screen->w - 5; + sdlprint(161,rect.y + 1, !savefld ? THEME_INPUT : THEME_INACT, THEME_INPBG, mtsfile); + if(!savefld) { + cur[0] = mtsfile[savepos] ? mtsfile[savepos] : ' '; + sdlprint(161 + savepos * (font->width+1), rect.y + 1, THEME_INPBG, THEME_INPUT, cur); + } + rect.y = 16 + 2*font->height; + sdlprint(42, rect.y, THEME_FG, THEME_BG, lang[MAPPING]); + i = lenpalettes * (font->width + 1) + 2; + strmaxw = screen->w - 5 - font->width; + rect.x = 160; rect.w = i < screen->w - 165 - font->width ? i : screen->w - 165 - font->width; rect.h = font->height + 2; + l = rect.w; + SDL_FillRect(screen, &rect, theme[THEME_INPBG]); + if(savepal < 0 || savepal >= numpalettes) savepal = 0; + sdlprint(161,rect.y + 1, savefld == 1 ? THEME_INPUT : THEME_INACT, THEME_INPBG, palettes[savepal]); + strmaxw = screen->w - 5; + sdlprint(160 + rect.w, rect.y+1, THEME_BG, savefld == 1 ? THEME_INPUT : THEME_FG, "\031"); + + sdlprint(42, 20 + 3*font->height, THEME_FG, THEME_BG, lang[BIOMETYPE]); + sdlprint(160, 20 + 3*font->height, savefld == 3 ? THEME_INPUT : THEME_FG, THEME_BG, + savebiome ? "\xE2\x98\x92" : "\xE2\x98\x90"); + + rect.y = 24 + 4*font->height; rect.x = 42; rect.w = screen->w - 47; + if(savebiome) { + j = screen->h - 2*font->height - 8; + rect.h = j - rect.y; + SDL_FillRect(screen, &rect, theme[THEME_INPBG]); + for(k = 0, i = 1; (int)i < numblocks; i++) { + if(blocks[i].numref && blocks[i].blocknames && blocks[i].blocknames[0] && (rect.y + font->height) < j) { + if(k >= biomescr) { + if(biomeblk == k) blocks[i].dobiome ^= 1; + strmaxw = screen->w / 2 - 4; + sdlprint(44,rect.y, savefld == 4 && biomepos == k ? THEME_INPUT : THEME_INACT, THEME_INPBG, blocks[i].name); + strmaxw = screen->w - 5; + sdlprint(screen->w/2,rect.y, savefld == 4 && biomepos == k ? THEME_INPUT : THEME_INACT, THEME_INPBG, + blocks[i].blocknames[blocks[i].dobiome ? 0 : savepal + 1]); + rect.y += font->height; + } + k++; + } + } + biomeblk = -1; + if(k > maxbiome) maxbiome = k; + if(!k) sdlprint(44, rect.y, THEME_INACT, THEME_INPBG, lang[ERR_NOBIOM]); + rect.y = j + 4; + } + rect.h = font->height + 2; + SDL_FillRect(screen, &rect, theme[savefld == 5 ? THEME_SAVEACT : THEME_SAVEINACT]); + sdlprint((screen->w - 47 - strlen(lang[SAVEBTN]) * (font->width+1)) / 2 + 42, rect.y+1, + savefld == 5 ? THEME_INPUT : THEME_FG, + savefld == 5 ? THEME_SAVEACT : THEME_SAVEINACT, lang[SAVEBTN]); + if(savefld == 2) { + rect.w = l; rect.x = 160 + font->width; rect.y = 16 + 3*font->height; rect.h = font->height; + for(i = 0; (int)i < numpalettes; i++, rect.y += rect.h) { + SDL_FillRect(screen, &rect, theme[savepal == (int)i ? THEME_INPUT : THEME_INPBG]); + sdlprint(rect.x, rect.y, savepal == (int)i ? THEME_BG : THEME_FG, savepal == (int)i ? THEME_INPUT : THEME_INPBG, + palettes[i]); + } + } +} + +/** + * Save file window scrolling event handler + */ +void save_scroll(SDL_Event *event) +{ + if(!savebiome) return; + biomescr -= event->wheel.y; + if(biomescr < 0) biomescr = 0; + if(biomescr + 1 >= maxbiome) biomescr = maxbiome - 1; +} + +/** + * Save file window mouse down event handler + */ +void save_mousedown(SDL_Event *event) +{ + int i = savebiome ? screen->h - 2*font->height - 4 : 24 + 4*font->height; + if(event->button.x < 42) return; + if(savefld == 2) { + if(event->button.x >= (int)(160 + font->width) && event->button.x < (int)(160 + (lenpalettes+1) * (font->width + 1)) && + event->button.y >= (int)(16 + 3*font->height) && event->button.y < (int)(16 + (3+numpalettes)*font->height)) { + savefld = 3; + savepal = (event->button.y - 16 - 3*font->height) / font->height; + } + return; + } + if(event->button.y >= (int)(12 + font->height) && event->button.y < (int)(16 + 2*font->height) && savefld) { + savefld = 0; savepos = savelen = strlen(mtsfile); + } + if(event->button.y >= (int)(16 + 2*font->height) && event->button.y < (int)(20 + 3*font->height)) { + savefld = 2; + } + if(event->button.y >= (int)(20 + 3*font->height) && event->button.y < (int)(24 + 4*font->height)) { + savefld = 3; + savebiome ^= 1; + biomepos = 0; + } + if(event->button.y >= i && event->button.y < (int)(i + font->height)) sdldosave(); + else if(savebiome && event->button.y >= (int)(24 + 4*font->height) && event->button.y < i) { + biomepos = biomescr + (event->button.y - (24 + 4*font->height)) / font->height; + if(savefld != 4) savefld = 4; + else biomeblk = biomepos; + } +} + +/** + * Save file window key event handler + */ +void save_key(SDL_Event *event) +{ + int i, j; + + switch(event->type) { + case SDL_TEXTINPUT: + j = strlen(event->text.text); + if(!savefld && savelen + j < (int)(sizeof(mtsfile))) { + for(i = savelen - 1; i >= savepos; i--) mtsfile[i + j] = mtsfile[i]; + memcpy(mtsfile + savepos, &event->text.text, j); + savepos += j; + savelen += j; + mtsfile[savelen] = 0; + } + break; + case SDL_KEYUP: + switch (event->key.keysym.sym) { + case SDLK_TAB: + if(savefld == 5) savefld = 0; else + if(savefld == 1) savefld = 3; else savefld++; + if(!savebiome && savefld == 4) savefld++; + break; + case SDLK_BACKSPACE: + if(!savefld && savepos) { + savepos--; + for(i = savepos; i < savelen + 1; i++) mtsfile[i] = mtsfile[i+1]; + savelen--; + } + break; + case SDLK_DELETE: + if(!savefld && savepos < savelen) { + for(i = savepos; i < savelen + 1; i++) mtsfile[i] = mtsfile[i+1]; + savelen--; + } + break; + case SDLK_UP: + if(!savefld) savepos = 0; + if(savefld == 1 || savefld == 2) { + if(!savepal) savepal = numpalettes - 1; else savepal--; + } + if(savefld == 4) { + if(!biomepos && maxbiome) biomepos = maxbiome - 1; else biomepos--; + if(biomepos < biomescr) biomescr = biomepos; + i = ((screen->h - 2*font->height - 8) - (24 + 4*font->height)) / font->height; + if(biomepos >= biomescr + i) biomescr = biomepos - i; + } + break; + case SDLK_DOWN: + if(!savefld) savepos = savelen; + if(savefld == 1 || savefld == 2) { + if(savepal + 1 >= numpalettes) savepal = 0; else savepal++; + } + if(savefld == 4) { + if(biomepos + 1 >= maxbiome) biomepos = 0; else biomepos++; + if(biomepos < biomescr) biomescr = biomepos; + i = ((screen->h - 2*font->height - 8) - (24 + 4*font->height)) / font->height; + if(biomepos >= biomescr + i) biomescr = biomepos - i; + } + break; + case SDLK_LEFT: + if(!savefld && savepos) savepos--; + if(savefld == 1 || savefld == 2) { + if(!savepal) savepal = numpalettes - 1; else savepal--; + } + break; + case SDLK_RIGHT: + if(!savefld && savepos < savelen) savepos++; + if(savefld == 1 || savefld == 2) { + if(savepal + 1 >= numpalettes) savepal = 0; else savepal++; + } + break; + case SDLK_RETURN: sdldosave(); break; + default: + if(savefld == 1) savefld = 2; else if(savefld == 2) savefld = 1; + if(savefld == 3) { savebiome ^= 1; biomepos = 0; } + if(savefld == 4) { biomeblk = biomepos; } + break; + } + break; + } +} diff --git a/src/sdl.c b/src/sdl.c new file mode 100644 index 0000000..3038417 --- /dev/null +++ b/src/sdl.c @@ -0,0 +1,489 @@ +/* + * mtsedit/sdl.c + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief SDL specific UI code + * + */ + +#include "main.h" + +uint32_t theme[] = { 0xFFF0F0F0, 0xFFF4F4F4, 0xFFBEBEBE, 0xFF808080, 0xFF5C5C5C, 0xFF4C4C4C, 0xFF454545, 0xFF383838, 0xFF303030, + 0xFFC00000, 0xFF800000 }; + +int quitting = 0, activetool = -1, overtool = -1, activeblock = 0, overblock = -1, palette[16], strmaxw, shift = 0, ctrl = 0; +char *status = NULL; +unsigned char *tmpblk = NULL; +SDL_Window *window = NULL; +SDL_Surface *icons = NULL; +SDL_Surface *screen = NULL; +SDL_Surface *fg = NULL; +SDL_Surface *bg = NULL; +SDL_Surface *blk = NULL; +SDL_Cursor *working, *pointer; + +psf_t *font = NULL; + +/** + * Print string to screen + */ +void sdlprint(int x, int y, int fg, int bg, char *s) +{ + unsigned char *glyph; + unsigned int c, offs, i, j, line, mask, bytesperline = (font->width + 7) / 8; + + for(; *s && x < strmaxw;) { + if((*s & 128) != 0) { + if(!(*s & 32)) { c = ((*s & 0x1F)<<6)|(*(s+1) & 0x3F); s++; } else + if(!(*s & 16)) { c = ((*s & 0xF)<<12)|((*(s+1) & 0x3F)<<6)|(*(s+2) & 0x3F); s += 2; } else + if(!(*s & 8)) { c = ((*s & 0x7)<<18)|((*(s+1) & 0x3F)<<12)|((*(s+2) & 0x3F)<<6)|(*(s+3) & 0x3F); *s += 3; } + else c = 0; + } else c = *s; + s++; + glyph = (unsigned char*)font + font->headersize + (c < font->numglyph ? c : 0) * font->bytesperglyph; + offs = (y * screen->pitch) + (x * 4); + for(j = 0; j < font->height; j++){ + line = offs; + mask = 1<<(font->width-1); + for(i = 0; i < font->width && (int)(x + i) < strmaxw; i++){ + *((uint32_t*)((uint8_t*)screen->pixels + line)) = theme[(*glyph & mask)?fg:bg]; + mask >>= 1; + line += 4; + } + glyph += bytesperline; + offs += screen->pitch; + } + x += font->width + 1; + } +} + +/** + * Redraw the toolbar + */ +void sdltoolbar() +{ + int i, m; + char *s = status, str[5]; + SDL_Rect src, dst; + + dst.x = dst.y = 0; dst.w = 36; dst.h = screen->h; + SDL_FillRect(screen, &dst, theme[THEME_BG]); + dst.x = 2; dst.y = 4 + 5*36; dst.w = 32; dst.h = screen->h - 3*font->height - 2*37 - dst.y; + SDL_FillRect(screen, &dst, theme[THEME_TABBG]); + + src.w = src.h = dst.w = dst.h = 32; src.x = 0; dst.x = 2; + dst.y = screen->h - 3*font->height - 2*37; + for(i = 5; i < 7; i++, dst.y += 36) { + src.y = (i+6) * 32; + dst.x--; dst.y--; dst.w += 2; dst.h += 2; + SDL_FillRect(screen, &dst, theme[i == activetool ? THEME_FG : (i == overtool ? THEME_INACT : THEME_BG)]); + dst.x++; dst.y++; dst.w -= 2; dst.h -= 2; + SDL_BlitSurface(icons, &src, screen, &dst); + } + dst.y = 2; + for(i = 0; i < 5; i++) { + src.y = (i+5) * 32; + dst.y = 4 + (i * 36); + dst.x--; dst.y--; dst.w += 2; dst.h += 2; + SDL_FillRect(screen, &dst, theme[i == activetool ? THEME_FG : (i == overtool ? THEME_INACT : THEME_BG)]); + dst.x++; dst.y++; dst.w -= 2; dst.h -= 2; + SDL_BlitSurface(icons, &src, screen, &dst); + } + src.y = 10 * 32; dst.x = 2; dst.y = 4 + 5 * 36; + m = screen->h - 3*font->height - 2*37; + SDL_FillRect(screen, &dst, theme[!activeblock ? THEME_INACT : (!overblock ? THEME_INPBG : THEME_TABBG)]); + SDL_BlitSurface(icons, &src, screen, &dst); + src.y = 0; dst.y += 32; + for(i = 1; i < 16 && palette[i] && dst.y < m; dst.y += 32, i++) { + if(dst.y + dst.h > m) dst.h = src.h = m - dst.y; + SDL_FillRect(screen, &dst, theme[i == activeblock ? THEME_INACT : (i == overblock ? THEME_INPBG : THEME_TABBG)]); + blk->pixels = blocks[palette[i]].img[0] ? blocks[palette[i]].img[0] : (uint8_t*)icons->pixels + 32 * icons->pitch; + SDL_BlitSurface(blk, &src, screen, &dst); + } + dst.x = 36; dst.y = screen->h - font->height; dst.w = screen->w - 36; dst.h = font->height; + SDL_FillRect(screen, &dst, theme[THEME_BG]); + if(!status) { + if((activetool == -1 && overtool < -1) || overtool >= 0) s = lang[LOAD + overtool]; + if(overblock != -1 && (!overblock || palette[overblock])) s = blocks[palette[overblock]].name; + } + dst.x = 2; dst.y = screen->h - 3*font->height; dst.w = 32; dst.h = font->height; + i = currlayer == gndlayer ? THEME_FG : THEME_TABBG; + SDL_FillRect(screen, &dst, theme[i]); + sdlprint(5, screen->h - 3*font->height, THEME_INACT, i, lang[GND]); + dst.x = 2; dst.y = screen->h - 2*font->height; dst.w = 32; dst.h = font->height; + SDL_FillRect(screen, &dst, theme[THEME_BG]); + sprintf(str, "%4d", currlayer - gndlayer); + sdlprint(0, screen->h - 2*font->height, THEME_INACT, THEME_BG, str); + sprintf(str, "%3d%%", layerprob[currlayer] * 100 / 127); + sdlprint(2, screen->h - font->height, THEME_INACT, THEME_BG, str); + if(s) + sdlprint(42, screen->h - font->height, THEME_FG, THEME_BG, s); +} + +/** + * Redraw the SDL window + */ +void sdlredraw() +{ + SDL_Rect rect; + + /* main window area */ + strmaxw = screen->w; + rect.x = 36; rect.y = 0; rect.w = screen->w - 36; rect.h = screen->h - font->height; + SDL_FillRect(screen, &rect, theme[activetool == 1 || activetool == 4 ? THEME_BG : THEME_TABBG]); + if(activetool == 1) save_redraw(); else + if(activetool == 4) search_redraw(); else + edit_redraw(1); + + /* toolbar and status bar */ + sdltoolbar(); + /* flush the window */ + SDL_UpdateWindowSurface(window); +} + +/** + * UI for saving + */ +void sdldosave() { + SDL_SetCursor(working); + mts_save(); + SDL_SetCursor(pointer); + activetool = -1; activeblock = 0; + sdlredraw(); +} + +/** + * UI for loading + */ +void sdldoload() { + SDL_SetCursor(working); + mts_load(); + SDL_SetCursor(pointer); + activetool = -1; activeblock = 0; + sdlredraw(); +} + +/** + * UI for preview + */ +void sdldopreview() { + SDL_SetCursor(working); + mts_view(shift); + SDL_SetCursor(pointer); + sdlredraw(); +} + +/** + * UI for rotating + */ +void sdldorotate() { + SDL_SetCursor(working); + mts_rotate(); + SDL_SetCursor(pointer); +} + +/** + * The main SDL loop + */ +void sdlmain() +{ + stbi__context s; + stbi__result_info ri; + unsigned char *icondata; + char *fn, *title, str[32]; + int iw = 0, ih = 0, len, i, j, k; + SDL_Event event; + SDL_Rect dst; + + /* get our font */ + font = (psf_t*)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)&font_start, (char*)&font_end - (char*)&font_start, + 32768, &len, 1); + if(!font) error(lang[ERR_MEM]); + + /* initialize SDL */ + SDL_Init(SDL_INIT_VIDEO); + fn = strrchr(mtsfile, DIRSEP); + if(fn) fn++; else fn = mtsfile; + title = (char*)malloc(strlen(fn) + 10); + sprintf(title, "MTSEdit: %s", fn); + window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_RESIZABLE); + free(title); + screen = SDL_GetWindowSurface(window); + + working = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW); + pointer = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + + /* get icons */ + s.read_from_callbacks = 0; + s.img_buffer = s.img_buffer_original = (unsigned char *)&icons_start; + s.img_buffer_end = s.img_buffer_original_end = (unsigned char *)&icons_end; + ri.bits_per_channel = 8; + icondata = (uint8_t*)stbi__png_load(&s, &iw, &ih, &len, 0, &ri); + icons = SDL_CreateRGBSurfaceFrom((uint32_t*)icondata, iw, iw, 32, iw*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + SDL_SetWindowIcon(window, icons); + SDL_FreeSurface(icons); + icons = SDL_CreateRGBSurfaceFrom((uint32_t*)icondata, iw, ih, 32, iw*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + blk = SDL_CreateRGBSurfaceFrom(NULL, 32, 32, 32, 32*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + fg = SDL_CreateRGBSurface(0, screen->w - 36, screen->h - font->height, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + bg = SDL_CreateRGBSurface(0, screen->w - 36, screen->h - font->height, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + + /* draw window and loading message */ + memset(palette, 0, sizeof(palette)); + status = lang[LOADING]; + sdlredraw(); + SDL_SetCursor(working); + + /* parse blocks.csv */ + parseblocks(); + /* load MTS */ + mts_load(mtsfile); + + /* populate search window with default results (may include unknown blocks from the MTS) */ + numresults = numblocks - 1; + results = (int*)malloc(numresults * sizeof(int)); + for(i = 0; (int)i < numresults; i++) results[i] = i + 1; + /* clear block probability for air */ + for(i = 0; i < 256; i++) + for(j = 0; j < 256; j++) + for(k = 0; k < 256; k++) + if(!nodes[i][j][k].param0) nodes[i][j][k].param1 = 0; + + /* repaint window with the loaded MTS */ + SDL_SetCursor(pointer); + sdlredraw(); + SDL_StartTextInput(); + + /* main loop */ + while (!quitting) { + if (SDL_WaitEvent(&event)) { + switch (event.type) { + /* window events */ + case SDL_QUIT: quitting = 1; break; + case SDL_WINDOWEVENT: + switch(event.window.event) { + case SDL_WINDOWEVENT_CLOSE: quitting = 1; break; + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + SDL_FreeSurface(screen); + SDL_FreeSurface(fg); + SDL_FreeSurface(bg); + screen = SDL_GetWindowSurface(window); + fg = SDL_CreateRGBSurface(0, screen->w - 36, screen->h - font->height, 32, + 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + bg = SDL_CreateRGBSurface(0, screen->w - 36, screen->h - font->height, 32, + 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + sdlredraw(); + break; + default: sdlredraw(); break; + } + break; + + /* mouse events */ + case SDL_MOUSEMOTION: + i = j = -1; status = NULL; + if(event.motion.x < 36) { + /* toolbar */ + if(event.motion.y < 4 + 5 * 36) { + i = (event.motion.y > 3 ? event.motion.y - 4 : event.motion.y) / 36; + } else if(event.motion.y >= (int)(screen->h - font->height)) { + i = -3; + } else if(event.motion.y >= (int)(screen->h - 2*font->height)) { + i = -4; + } else if(event.motion.y >= (int)(screen->h - 3*font->height)) { + i = -2; + } else if(event.motion.y >= (int)(screen->h - 3*font->height - 36)) { + i = 6; + } else if(event.motion.y >= (int)(screen->h - 3*font->height - 2*36)) { + i = 5; + } else if(event.motion.y < (int)(screen->h - 3*font->height - 2*36)) { + j = (event.motion.y - (4 + 5 * 36)) / 32; + } + } else + if(activetool == 4) search_mouseover(&event); else + if(activetool == -1) { + if(event.motion.x > screen->w - 32 && event.motion.y >= (int)(screen->h - font->height - 32)) i = -5; + status = NULL; + dst.x = 36; dst.y = screen->h - font->height; dst.w = screen->w - 36; dst.h = font->height; + SDL_FillRect(screen, &dst, theme[THEME_BG]); + edit_hidecursor(); + edit_mouseover(&event); + edit_redraw(event.button.button); + sdltoolbar(); + SDL_UpdateWindowSurface(window); + } + if(i != overtool || j != overblock) { + overtool = i; + overblock = j; + sdltoolbar(); + SDL_UpdateWindowSurface(window); + } + break; + case SDL_MOUSEBUTTONDOWN: + if(activetool == -1 && overtool == -1 && event.motion.x > 36) edit_mousedown(&event); else + if(overtool == -3) mts_layerprob(event.button.button == 1 ? +1 : -1); else + if(overtool == -2) gndlayer = currlayer; + if(overtool >= 0 || overblock != -1) { + if(overtool >= 0 && activetool != overtool) activetool = overtool; + else { activetool = -1; if(overblock != -1) activeblock = overblock; } + sdltoolbar(); + SDL_UpdateWindowSurface(window); + } + if(activetool == 1) save_mousedown(&event); else + if(activetool == 4) search_mousedown(&event); + break; + case SDL_MOUSEBUTTONUP: + if(activetool != -1 && activetool == overtool) { + if(activetool == 0) sdldoload(); + if(activetool == 2) sdldopreview(); + if(activetool == 3) sdldorotate(); + if(activetool == 5 && currlayer < 255) currlayer++; + if(activetool == 6 && currlayer > 0) currlayer--; + if(activetool != 1 && activetool != 4) activetool = -1; + } + sdlredraw(); + break; + case SDL_MOUSEWHEEL: + if(overtool == -3) mts_layerprob(event.wheel.y); else + if(activetool == 1) save_scroll(&event); else + if(activetool == 4) search_scroll(&event); else + if(activetool == -1) edit_scroll(&event); + sdlredraw(); + break; + + /* handle the keyboard */ + case SDL_KEYDOWN: + status = NULL; + if(event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT) shift = 1; + if(event.key.keysym.sym == SDLK_LCTRL || event.key.keysym.sym == SDLK_RCTRL) ctrl = 1; + if(activetool != -1 && event.key.keysym.sym == SDLK_ESCAPE) { + activetool = -1; + } else if(activetool != 1 && activetool != 4) { + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + case SDLK_q: quitting = 1; break; + case SDLK_s: if(!shift) sdldosave(); break; + case SDLK_l: sdldoload(); break; + case SDLK_p: sdldopreview(); break; + case SDLK_r: sdldorotate(); break; + case SDLK_g: gndlayer = currlayer; break; + case SDLK_PLUS: case SDLK_EQUALS: mts_layerprob(+1); break; + case SDLK_MINUS: mts_layerprob(-1); break; + case SDLK_PAGEUP: if(currlayer < 255) currlayer++; break; + case SDLK_PAGEDOWN: if(currlayer > 0) currlayer--; break; + case SDLK_BACKQUOTE: case SDLK_0: activeblock = 0; break; + case SDLK_1: case SDLK_2: case SDLK_3: case SDLK_4: case SDLK_5: + case SDLK_6: case SDLK_7: case SDLK_8: case SDLK_9: + if(palette[event.key.keysym.sym - SDLK_0]) + activeblock = event.key.keysym.sym - SDLK_0; + break; + case SDLK_h: dx--; sprintf(str, "dx %d dz %d", dx, dz); status = str; break; + case SDLK_k: dx++; sprintf(str, "dx %d dz %d", dx, dz); status = str; break; + case SDLK_u: dz--; sprintf(str, "dx %d dz %d", dx, dz); status = str; break; + case SDLK_j: dz++; sprintf(str, "dx %d dz %d", dx, dz); status = str; break; + case SDLK_UP: + if(ctrl) { + if(oy < 127) oy++; + } else if(shift) { + if(up > -128) up--; + } else { + if(cz > 0) cz--; + status = nodes[currlayer][cz][cx].param0 ? + blocks[nodes[currlayer][cz][cx].param0].name : NULL; + } + break; + case SDLK_DOWN: + if(ctrl) { + if(oy > -128) oy--; + } else if(shift) { + if(up < 127) up++; + } else { + if(cz < 255) cz++; + status = nodes[currlayer][cz][cx].param0 ? + blocks[nodes[currlayer][cz][cx].param0].name : NULL; + } + break; + case SDLK_LEFT: + if(ctrl) { + if(ox < 127) ox++; + } else if(shift) { + edit_rotate(currlayer, cz, cx); + } else { + if(cx > 0) cx--; + status = nodes[currlayer][cz][cx].param0 ? + blocks[nodes[currlayer][cz][cx].param0].name : NULL; + } + break; + case SDLK_RIGHT: + if(ctrl) { + if(ox > -128) ox--; + } else if(shift) { + edit_rotate(currlayer, cz, cx); + } else { + if(cx < 255) cx++; + status = nodes[currlayer][cz][cx].param0 ? + blocks[nodes[currlayer][cz][cx].param0].name : NULL; + } + break; + case SDLK_TAB: edit_rotate(currlayer, cz, cx); break; + case SDLK_BACKSPACE: + case SDLK_DELETE: + case SDLK_SPACE: + blocks[nodes[currlayer][cz][cx].param0].numref--; + i = event.key.keysym.sym == SDLK_SPACE && !shift ? palette[activeblock] : 0; + blocks[i].numref++; + nodes[currlayer][cz][cx].param0 = i; + nodes[currlayer][cz][cx].param1 = i ? 127 : 0; + status = i ? blocks[i].name : NULL; + break; + } + } + sdlredraw(); + break; + case SDL_TEXTINPUT: + case SDL_KEYUP: + if(event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT) shift = 0; + if(event.key.keysym.sym == SDLK_LCTRL || event.key.keysym.sym == SDLK_RCTRL) ctrl = 0; + if(activetool == 1) save_key(&event); else + if(activetool == 4) search_key(&event); + if(shift && event.key.keysym.sym == SDLK_s) activetool = 1; + if(event.key.keysym.sym == SDLK_m || event.key.keysym.sym == SDLK_TAB) activetool = 4; + sdlredraw(); + break; + } + } + } + + /* cleanup SDL */ + SDL_StopTextInput(); + SDL_FreeCursor(working); + SDL_FreeCursor(pointer); + SDL_FreeSurface(icons); + SDL_FreeSurface(blk); + SDL_FreeSurface(fg); + SDL_FreeSurface(bg); + SDL_Quit(); + free(icondata); + free(font); + freeblocks(); +} diff --git a/src/search.c b/src/search.c new file mode 100644 index 0000000..e45490e --- /dev/null +++ b/src/search.c @@ -0,0 +1,183 @@ +/* + * mtsedit/search.c + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief Search Block window + * + */ + +#include "main.h" + +int numresults, *results = NULL, line, overblk = -1, searchscr = 0; +int searchlen = 0, searchpos = 0; +char search[256] = { 0 }, cur[2] = {0, 0}; + +/** + * Redraw the Search Block window + */ +void search_redraw() +{ + int i; + SDL_Rect rect; + + strmaxw = screen->w - 5; + sdlprint((screen->w - 47 - strlen(lang[ADDBLOCKS]) * (font->width+1)) / 2 + 47, 4, THEME_INPUT, THEME_BG, lang[ADDBLOCKS]); + rect.y = 12 + font->height; + sdlprint(42, rect.y, THEME_FG, THEME_BG, lang[SEARCH]); + rect.x = 160; rect.w = screen->w - 165; rect.h = font->height + 2; + SDL_FillRect(screen, &rect, theme[THEME_INPBG]); + strmaxw = screen->w - 5; + sdlprint(161,rect.y + 1, THEME_INPUT, THEME_INPBG, search); + cur[0] = search[searchpos] ? search[searchpos] : ' '; + sdlprint(161 + searchpos * (font->width+1), rect.y + 1, THEME_INPBG, THEME_INPUT, cur); + rect.x = 42; rect.y = 20 + 2*font->height; rect.w = rect.h = 32; + if(line != (screen->w - 47) / 32) { + line = (screen->w - 47) / 32; + searchscr = 0; + } + for(i = searchscr * line; i < numresults && rect.y + 32 < (int)(screen->h - font->height); i++, rect.x += 32) { + if(rect.x + 32 > screen->w - 5) { rect.x = 42; rect.y += 32; } + if(rect.y + 32 > (int)(screen->h - font->height)) rect.h = screen->h - font->height - rect.y; + blk->pixels = blocks[results[i]].img[0] ? blocks[results[i]].img[0] : (uint8_t*)icons->pixels + 32 * icons->pitch; + SDL_FillRect(screen, &rect, theme[i == overblk ? THEME_INACT : THEME_BG]); + SDL_BlitSurface(blk, NULL, screen, &rect); + } +} + +/** + * Search Block window scrolling event handler + */ +void search_scroll(SDL_Event *event) +{ + int i = numresults / line; + searchscr -= event->wheel.y; + if(searchscr < 0) searchscr = 0; + if(searchscr + 1 >= i) searchscr = i - 1; +} + +/** + * Add a block to the palette + */ +void search_addblock(int blk) +{ + int i, j, b; + + if(blk != -1 && blk < numresults) { + b = results[blk]; + if(!b) return; + for(j = 1; j < 15 && palette[j] != b; j++); + for(i = j; i > 1; i--) palette[i] = palette[i - 1]; + palette[1] = b; + activeblock = 1; + } +} + +/** + * Search Block window mouse down event handler + */ +void search_mousedown(_unused SDL_Event *event) +{ + if(overblk != -1 && overblk < numresults) { + search_addblock(overblk); + sdlredraw(); + } +} + +/** + * Search Block mouse over event handler + */ +void search_mouseover(SDL_Event *event) +{ + int i, j = -1; + + if(event->motion.x >= 42 && event->motion.x < screen->w - 5) { + if(event->motion.y >= (int)(20 + 2*font->height) && event->motion.y < (int)(screen->h - font->height)) { + i = ((event->motion.x - 42) / 32); + j = (((event->motion.y - (20 + 2*font->height)) / 32 - searchscr) * line) + (i < line ? i : line); + if(j >= numresults || !results[j]) j = -1; + } + } + if(j != overblk) { + overblk = j; + status = j == -1 ? NULL : blocks[results[j]].name; + sdlredraw(); + } +} + +/** + * Search Block window keypress event handler + */ +void search_key(SDL_Event *event) +{ + int i, j, l; + + switch(event->type) { + case SDL_TEXTINPUT: + j = strlen(event->text.text); + if(!ctrl && searchlen + j < (int)(sizeof(search))) { + for(i = searchlen - 1; i >= searchpos; i--) search[i + j] = search[i]; + memcpy(search + searchpos, &event->text.text, j); + searchpos += j; + searchlen += j; + search[searchlen] = 0; + } + break; + case SDL_KEYUP: + switch (event->key.keysym.sym) { + case SDLK_BACKSPACE: + if(searchpos) { + searchpos--; + for(i = searchpos; i < searchlen + 1; i++) search[i] = search[i+1]; + searchlen--; + } + break; + case SDLK_DELETE: + if(searchpos < searchlen) { + for(i = searchpos; i < searchlen + 1; i++) search[i] = search[i+1]; + searchlen--; + } + break; + case SDLK_UP: searchpos = 0; break; + case SDLK_DOWN: searchpos = searchlen; break; + case SDLK_LEFT: if(searchpos) searchpos--; break; + case SDLK_RIGHT: if(searchpos < searchlen) searchpos++; break; + } + if(ctrl && event->key.keysym.sym >= SDLK_1 && event->key.keysym.sym <= SDLK_9) + search_addblock(event->key.keysym.sym - SDLK_1); + break; + } + if(!searchlen) { + numresults = numblocks - 1; + for(i = 0; (int)i < numresults; i++) results[i] = i + 1; + } else { + for(numresults = 0, i = 1; i < numblocks; i++) { + l = strlen(blocks[i].name) - searchlen; + for(j = 0; j < l; j++) + if(!strncasecmp(blocks[i].name + j, search, searchlen)) { + results[numresults++] = i; + break; + } + } + } +} diff --git a/src/stb_image.h b/src/stb_image.h new file mode 100644 index 0000000..5186d4d --- /dev/null +++ b/src/stb_image.h @@ -0,0 +1,1582 @@ +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality); + +/* PNG decompressor from + + stb_image - v2.23 - public domain image loader - http://nothings.org/stb_image.h +*/ +const char *stbi__g_failure_reason; + +enum +{ + STBI_default = 0, + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +typedef unsigned short stbi_us; + +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; + +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + void *io_user_data; + + int read_from_callbacks; + int buflen; + unsigned char buffer_start[128]; + + unsigned char *img_buffer, *img_buffer_end; + unsigned char *img_buffer_original, *img_buffer_original_end; +} stbi__context; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#define STBI_ASSERT(v) +#define STBI_NOTUSED(v) (void)sizeof(v) +#define STBI__BYTECAST(x) ((unsigned char) ((x) & 255)) +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) + +void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len); + +#ifdef STB_IMAGE_IMPLEMENTATION + +_inline static unsigned char stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + return 0; +} + +_inline static int stbi__at_eof(stbi__context *s) +{ + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, unsigned char *buffer, int n) +{ + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#define stbi__err(x,y) stbi__errstr(y) +static int stbi__errstr(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +_inline static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + return a <= 2147483647 - b; +} + +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; + return a <= 2147483647/b; +} + +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +static unsigned char stbi__compute_y(int r, int g, int b) +{ + return (unsigned char) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + stbi__err("outofmem", "Out of memory"); + return NULL; + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + stbi__err("outofmem", "Out of memory"); + return NULL; + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#define STBI__ZFAST_BITS 9 +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + unsigned char size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, unsigned char *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (unsigned char ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +typedef struct +{ + unsigned char *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +_inline static unsigned char stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + unsigned char *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (unsigned char *) (zout - dist); + if (dist == 1) { + unsigned char v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static unsigned char length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + unsigned char lencodes[286+32+137]; + unsigned char codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (unsigned char) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (unsigned char) c; + else { + unsigned char fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +_inline static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + unsigned char header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); + k = 0; + while (a->num_bits > 0) { + header[k++] = (unsigned char) (a->code_buffer & 255); + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); + return 1; +} + +static unsigned char stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + stbi__init_zdefaults(); + return stbi__parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (unsigned char *) buffer; + a.zbuffer_end = (unsigned char *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +_inline static int stbi__check_png_header(stbi__context *s) +{ + static unsigned char png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + unsigned char *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static unsigned char first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static unsigned char stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +static int stbi__create_png_image_raw(stbi__png *a, unsigned char *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (unsigned char *) stbi__malloc_mad3(x, y, output_bytes, 0); + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + unsigned char *cur = a->out + stride*j; + unsigned char *prior = cur - stride; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; + + if (j == 0) filter = first_row_filter[filter]; + + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; + cur[filter_bytes+1] = 255; + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + if (depth == 16) { + cur = a->out + stride*j; + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + if (depth < 8) { + for (j=0; j < y; ++j) { + unsigned char *cur = a->out + stride*j; + unsigned char *in = a->out + stride*j + x*out_n - img_width_bytes; + unsigned char scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + unsigned char *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, unsigned char *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + unsigned char *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + final = (unsigned char *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, unsigned char tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + unsigned char *p = z->out; + + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, unsigned char *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + unsigned char *p, *temp_out, *orig = a->out; + + p = (unsigned char *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + unsigned char palette[1024], pal_img_n=0; + unsigned char has_trans=0, tc[3]; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (unsigned char)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + unsigned char *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (unsigned char *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + bpl = (s->img_x * z->depth + 7) / 8; + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (unsigned char *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, 1); + if (z->expanded == NULL) return 0; + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (pal_img_n) { + s->img_n = pal_img_n; + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + return stbi__err("invalid_chunk", "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) { stbi__err("bad req_comp", "Internal error"); return NULL; } + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +/* zlib_compressor from + + stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h +*/ +typedef unsigned char stbiw__uc; +typedef unsigned short stbiw__us; + +typedef uint16_t stbiw__uint16; +typedef int16_t stbiw__int16; +typedef uint32_t stbiw__uint32; +typedef int32_t stbiw__int32; + +#define STBIW_MALLOC(s) STBI_MALLOC(s) +#define STBIW_REALLOC(p,ns) STBI_REALLOC(p,ns) +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#define STBIW_FREE STBI_FREE +#define STBIW_MEMMOVE memmove +#define STBIW_UCHAR (uint8_t) +#define STBIW_ASSERT(x) +#define stbiw___sbraw(a) ((int *) (a) - 2) +#define stbiw___sbm(a) stbiw___sbraw(a)[0] +#define stbiw___sbn(a) stbiw___sbraw(a)[1] + +#define stbiw___sbneedgrow(a,n) ((a)==0 || stbiw___sbn(a)+n >= stbiw___sbm(a)) +#define stbiw___sbmaybegrow(a,n) (stbiw___sbneedgrow(a,(n)) ? stbiw___sbgrow(a,n) : 0) +#define stbiw___sbgrow(a,n) stbiw___sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw___sbpush(a, v) (stbiw___sbmaybegrow(a,1), (a)[stbiw___sbn(a)++] = (v)) +#define stbiw___sbcount(a) ((a) ? stbiw___sbn(a) : 0) +#define stbiw___sbfree(a) ((a) ? STBIW_FREE(stbiw___sbraw(a)),0 : 0) + +static void *stbiw___sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw___sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw___sbraw(*arr) : 0, *arr ? (stbiw___sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw___sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw___zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw___sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw___zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw___zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw___zhash(unsigned char *data) +{ + stbiw__uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw___zlib_flush() (out = stbiw___zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw___zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw___zlib_flush()) +#define stbiw___zlib_huffa(b,c) stbiw___zlib_add(stbiw___zlib_bitrev(b,c),c) +#define stbiw___zlib_huff1(n) stbiw___zlib_huffa(0x30 + (n), 8) +#define stbiw___zlib_huff2(n) stbiw___zlib_huffa(0x190 + (n)-144, 9) +#define stbiw___zlib_huff3(n) stbiw___zlib_huffa(0 + (n)-256,7) +#define stbiw___zlib_huff4(n) stbiw___zlib_huffa(0xc0 + (n)-280,8) +#define stbiw___zlib_huff(n) ((n) <= 143 ? stbiw___zlib_huff1(n) : (n) <= 255 ? stbiw___zlib_huff2(n) : (n) <= 279 ? stbiw___zlib_huff3(n) : stbiw___zlib_huff4(n)) +#define stbiw___zlib_huffb(n) ((n) <= 143 ? stbiw___zlib_huff1(n) : stbiw___zlib_huff2(n)) + +#define stbiw___ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw___ZHASH * sizeof(char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw___sbpush(out, 0x78); + stbiw___sbpush(out, 0x5e); + stbiw___zlib_add(1,1); + stbiw___zlib_add(1,2); + + for (i=0; i < stbiw___ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + int h = stbiw___zhash(data+i)&(stbiw___ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw___sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { + int d = stbiw___zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + if (hash_table[h] && stbiw___sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw___sbn(hash_table[h]) = quality; + } + stbiw___sbpush(hash_table[h],data+i); + + if (bestloc) { + h = stbiw___zhash(data+i+1)&(stbiw___ZHASH-1); + hlist = hash_table[h]; + n = stbiw___sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw___zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw___zlib_huff(j+257); + if (lengtheb[j]) stbiw___zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw___zlib_add(stbiw___zlib_bitrev(j,5),5); + if (disteb[j]) stbiw___zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw___zlib_huffb(data[i]); + ++i; + } + } + for (;i < data_len; ++i) + stbiw___zlib_huffb(data[i]); + stbiw___zlib_huff(256); + while (bitcount) + stbiw___zlib_add(0,1); + + for (i=0; i < stbiw___ZHASH; ++i) + (void) stbiw___sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw___sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw___sbpush(out, STBIW_UCHAR(s2)); + stbiw___sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw___sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw___sbn(out); + STBIW_MEMMOVE(stbiw___sbraw(out), out, *out_len); + return (unsigned char *) stbiw___sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * y; + int signed_stride = stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = -1; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, j, n, force_filter, line_buffer); + } else { + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, j, n, filter_type, line_buffer); + + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); + STBIW_FREE(filt); + if (!zlib) return 0; + + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#endif /* STB_IMAGE_IMPLEMENTATION */ + +#endif diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..6f36177 --- /dev/null +++ b/src/util.c @@ -0,0 +1,280 @@ +/* + * mtsedit/util.c + * + * Copyright (C) 2019 bzt (bztsrc@gitlab) + * + * 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. + * + * @brief Utilities + * + */ + +#include "main.h" +#include + +char *path = NULL, *fn = NULL; + +int fncmp(const void *a, const void *b) { return strcmp(*((const char**)a), *((const char**)b)); } + +/** + * Read a file into memory from the data directory + */ +unsigned char *readfile(char *file, unsigned int *size) +{ + FILE *f; + unsigned char *ret = NULL; + + *size = 0; + if(!path || !fn) return NULL; + strcpy(fn, file); + f = fopen(path, "rb"); + if(f) { + fseek(f, 0L, SEEK_END); + *size = (unsigned int)ftell(f); + fseek(f, 0L, SEEK_SET); + ret = (unsigned char*)malloc(*size + 1); + if(ret) { + fread(ret, *size, 1, f); + ret[*size] = 0; + } else + *size = 0; + fclose(f); + } + return ret; +} + +/** + * CSV error + */ +void errorcsv(int line) +{ + char msg[1024]; + sprintf(msg, "blocks.csv(%d): %s", line, lang[ERR_CSV]); +} + +/** + * Convert an ascii hex value int integer + */ +unsigned char ahtoi(char *s) +{ + int r = 0; + for(; *s; s++) { + if(*s >= '0' && *s <= '9') { r <<= 4; r += (uint32_t)(*s-'0'); } + else if(*s >= 'a' && *s <= 'f') { r <<= 4; r += (uint32_t)(*s-'a'+10); } + else if(*s >= 'A' && *s <= 'F') { r <<= 4; r += (uint32_t)(*s-'A'+10); } + else break; + } + return (unsigned char)r; +} + +/** + * Parse the blocks.csv + */ +void parseblocks() +{ + stbi__context sc; + stbi__result_info ri; + int w, h, l; + unsigned int numfiles = 0, i, j, k; + char **files = NULL, c, *s, *e; + DIR *dir; + struct dirent *de; + unsigned int size; + unsigned char *img; + char *data = (char*)readfile("blocks.csv", &size); + + if(!data || !size) error(lang[ERR_CSV]); + + /* get block images */ + *fn = 0; + dir = opendir(path); + if(dir) { + while((de = readdir(dir))) { + i = strlen(de->d_name); + if(i > 8 && !strcmp(de->d_name + i - 4, ".png") && de->d_name[i-7] == '_') { + j = numfiles++; + files = (char**)realloc(files, numfiles * sizeof(char*)); + if(!files) error(lang[ERR_MEM]); + files[j] = (char*)malloc(i + 1); + if(!files[j]) error(lang[ERR_MEM]); + memcpy(files[j], de->d_name, i + 1); + } + } + closedir(dir); + } + qsort(files, numfiles, sizeof(char*), fncmp); + + /* parse header */ + for(e = data, i = j = 0; *e && *e != '\r' && *e != '\n' && i < 3; e++) { + if(*e == '\"') j ^= 1; + if(!j && (*e == ',' || *e == ';' || *e == '\t')) i++; + } + if(i != 3 || !*e || *e == '\r' || *e == '\n') errorcsv(1); + while(*e && *e != '\r' && *e != '\n') { + while(*e == ',' || *e == ';' || *e == '\t' || *e == ' ') e++; + if(*e == '\"') { e++; c = '\"'; } else c = ','; + for(s = e; *e && *e != '\r' && *e != '\n' && *e != c; e++) + if(*e == '\\' || (*e == '\"' && e[1] == '\"')) { e++; continue; } + while(*s <= ' ') s++; + while(*(e-1) <= ' ') e--; + j = numpalettes++; + palettes = (char**)realloc(palettes, numpalettes * sizeof(char*)); + if(!palettes) error(lang[ERR_MEM]); + palettes[j] = (char*)malloc(e - s + 1); + if(!palettes[j]) error(lang[ERR_MEM]); + memcpy(palettes[j], s, e - s + 1); + palettes[j][e - s] = 0; + if((int)(e - s) > lenpalettes) lenpalettes = e - s; + while(*e && *e != ',' && *e != ';' && *e != '\t' && *e != '\r' && *e != '\n') e++; + } + /* add Air as first block */ + numblocks++; + blocks = (mtsblock_t*)realloc(blocks, numblocks * sizeof(mtsblock_t)); + if(!blocks) error(lang[ERR_MEM]); + memset(&blocks[0], 0, sizeof(mtsblock_t)); + blocks[0].name = (char*)malloc(4); + if(!blocks[0].name) error(lang[ERR_MEM]); + memcpy(blocks[0].name, "Air", 4); + blocks[0].blocknames = (char**)malloc((numpalettes + 1) * sizeof(char*)); + if(!blocks[0].blocknames) error(lang[ERR_MEM]); + blocks[0].blocknames[0] = NULL; + for(i = 1; i < (unsigned int)numpalettes + 1; i++) { + blocks[0].blocknames[i] = (char*)malloc(4); + if(!blocks[0].blocknames[i]) error(lang[ERR_MEM]); + memcpy(blocks[0].blocknames[i], "air", 4); + } + + /* parse rows */ + while(*e) { + if(*e == '\r' || *e == '\n') { + while(*e == '\r' || *e == '\n') e++; + if(!*e) break; + /* get canonical name */ + if(*e == '\"') { e++; c = '\"'; } else c = ','; + for(s = e; *e && *e != '\r' && *e != '\n' && *e != c; e++) + if(*e == '\\' || (*e == '\"' && e[1] == '\"')) { e++; continue; } + while(*s <= ' ') s++; + while(*(e-1) <= ' ') e--; + j = numblocks++; + blocks = (mtsblock_t*)realloc(blocks, numblocks * sizeof(mtsblock_t)); + if(!blocks) error(lang[ERR_MEM]); + memset(&blocks[j], 0, sizeof(mtsblock_t)); + blocks[j].name = (char*)malloc(e - s + 1); + if(!blocks[j].name) error(lang[ERR_MEM]); + blocks[j].blocknames = (char**)malloc((numpalettes + 1) * sizeof(char*)); + if(!blocks[j].blocknames) error(lang[ERR_MEM]); + memset(blocks[j].blocknames, 0, (numpalettes + 1) * sizeof(char*)); + for(i = 0; s + i < e; i++) { + blocks[j].name[i] = s[i] == '_' ? ' ' : s[i]; + if(s[i] == ' ') s[i] = '_'; + } + blocks[j].name[i] = 0; + /* get block images and possible param2 values */ + for(i = 0, l = 0; i < numfiles; i++) { + if((int)strlen(files[i]) == (int)(e - s + 7) && !memcmp(files[i], s, e - s)) { + img = readfile(files[i], &size); + if(img && size) { + k = ahtoi(strrchr(files[i], '_') + 1) & 0x1F; + sc.read_from_callbacks = 0; + sc.img_buffer = sc.img_buffer_original = img; + sc.img_buffer_end = sc.img_buffer_original_end = img + size; + ri.bits_per_channel = 8; + blocks[j].img[k] = (unsigned char*)stbi__png_load(&sc, &w, &h, &l, 0, &ri); + if(blocks[j].img[k]) l++; + free(img); + } + } + } + if(!l) + fprintf(stderr, "mtsedit: blocks.csv(%d): %s '%s'\r\n", j+1, lang[ERR_IMG], blocks[j].name); + while(*e && *e != ',' && *e != ';' && *e != '\t' && *e != '\r' && *e != '\n') e++; + while(*e == ' ') e++; + if(*e != ',' && *e != ';' && *e != '\t') errorcsv(j+1); + e++; + while(*e == ' ') e++; + /* get Block IDs */ + if(*e == '\"') { e++; c = '\"'; } else c = ','; + for(s = e; *e && *e != '\r' && *e != '\n' && *e != c; e++) + if(*e == '\\' || (*e == '\"' && e[1] == '\"')) { e++; continue; } + while(*s <= ' ') s++; + for(i = 0; s < e && i < 8;) { + blocks[j].blockids[i] = atoi(s); + if(blocks[j].blockids[i]) i++; + while(*s >= '0' && *s <= '9') s++; + if(*s == '/') s++; + } + while(*e && *e != ',' && *e != ';' && *e != '\t' && *e != '\r' && *e != '\n') e++; + k = 0; + } + /* get palette specific names */ + while(*e == ' ') e++; + if(*e != ',' && *e != ';' && *e != '\t') errorcsv(j+1); + e++; + while(*e == ' ') e++; + if(!*e) errorcsv(j+1); + if(*e == '\"') { e++; c = '\"'; } else c = ','; + for(s = e; *e && *e != '\r' && *e != '\n' && *e != c; e++) + if(*e == '\\' || (*e == '\"' && e[1] == '\"')) { e++; continue; } + while(*s <= ' ') s++; + while(*(e-1) <= ' ') e--; + if(e != s) { + blocks[j].blocknames[k] = (char*)malloc(e - s + 1); + if(!blocks[j].blocknames[k]) error(lang[ERR_MEM]); + memcpy(blocks[j].blocknames[k], s, e - s + 1); + blocks[j].blocknames[k][e - s] = 0; + } + k++; if(k > (unsigned int)numpalettes + 1) errorcsv(j+2); + while(*e && *e != ',' && *e != ';' && *e != '\t' && *e != '\r' && *e != '\n') e++; + } + if(!numblocks || numblocks > 65535) error(lang[ERR_CSV]); + + /* free temp resources */ + for(i = 0; i < numfiles; i++) free(files[i]); + free(files); + free(data); +} + +/** + * Free resources + */ +void freeblocks() +{ + int i, j; + + for(i = 0; i < numpalettes; i++) free(palettes[i]); + free(palettes); + + for(i = 0; i < numblocks; i++) { + free(blocks[i].name); + if(blocks[i].blocknames) { + for(j = 0; j < numpalettes; j++) + if(blocks[i].blocknames[j]) free(blocks[i].blocknames[j]); + free(blocks[i].blocknames); + } + for(j = 0; j < 32; j++) { + if(blocks[i].img[j]) free(blocks[i].img[j]); + if(blocks[i].dr[j]) free(blocks[i].dr[j]); + if(blocks[i].tr[j]) free(blocks[i].tr[j]); + } + } + free(results); + free(blocks); +}